moe-mizrak / laravel-prompt-alchemist
Laravel 通用 LLM 工具使用(函数调用)包,兼容所有 LLM,使 LLM 能够执行实际代码函数(与 LLM 的内置功能不同)。
Requires
- php: ^8.1
- ext-json: *
- caseyamcl/guzzle_retry_middleware: ^2.9
- guzzlehttp/guzzle: ^7.8
- moe-mizrak/laravel-openrouter: ^1.0
- phpdocumentor/reflection-docblock: ^5.4
- spatie/data-transfer-object: ^3.9.1
Requires (Dev)
- fakerphp/faker: ^1.12
- mockery/mockery: ^1.0
- orchestra/testbench: ^6.0
- phpunit/phpunit: ^9.0
This package is not auto-updated.
Last update: 2024-09-24 09:28:58 UTC
README
通用 LLM 工具使用(函数调用) 包用于 Laravel,兼容 所有 LLM,使 LLM 能够执行 实际代码函数(与 LLM 的内置功能不同)。
在 Laravel 应用程序中解锁强大的大型语言模型(LLM)交互。
此 Laravel 包实现了通用的 LLM 工具使用(函数调用),允许 LLM 根据提示 决定 和 执行 函数调用。与可能仅列出函数的内置功能不同,此包进行 实际调用,确保 动态执行。与 所有 LLM 兼容,它增强了应用程序的自动化和交互性。
目录
🤖 要求
- PHP 8.1 或 更高版本
- Laravel Openrouter
ℹ️ OpenRouter 作为包中默认的 LLM 提供者。包结构 灵活,允许您使用自己的选择 LLM 提供者,如 OpenAI、Claude、Gemini 等。(OpenRouter 是 LLM 的统一接口。)
🏁 开始使用
您可以通过 composer 安装 此包
composer require moe-mizrak/laravel-prompt-alchemist
您可以通过以下方式 发布 配置文件
php artisan vendor:publish --tag=laravel-prompt-alchemist
这是已发布的配置文件的内容
return [ 'env_variables' => [ 'api_key' => env('OPENROUTER_API_KEY'), 'api_endpoint' => env('OPENROUTER_API_ENDPOINT', 'https://openrouter.ai/api/v1/'), 'default_model' => 'Your selection of the default model', ], 'functions_yml_path' => 'Path to the functions yml file', 'schemas' => [ 'function_payload_schema_path' => 'Path to the function payload schema yml file', 'function_results_schema_path' => 'Path to the function results schema yml file', ], 'instructions' => [ 'prompt_function_instructions' => 'Instructions for the LLM about how to make use of provided functions, prompt and function_payload_schema in order to get the desired response', 'function_results_instructions' => 'Instructions for the LLM about how to make use of provided function_results, prompt and function_results_schema in order to get the desired response', 'generate_prompt_function_instructions' => 'Instructions for generating specific/customised prompt_function_instructions', ], 'function_signature_mapping' => 'Function signature mapping data (FunctionSignatureMappingData)', ];
⚙️ 配置
发布包配置文件后,您应遵循的步骤
➡️ env_variables
如果您将使用 OpenRouter 作为 LLM 提供者,请将以下环境变量添加到您的 .env 文件中(如果您的 LLM 提供者选择不同,则此操作 不必要)
OPENROUTER_API_ENDPOINT=https://openrouter.ai/api/v1/ OPENROUTER_API_KEY=your_api_key OPENROUTER_DEFAULT_MODEL=default_model
- OPENROUTER_API_ENDPOINT:OpenRouter API 的端点 URL(默认:https://openrouter.ai/api/v1/)。
- OPENROUTER_API_KEY:访问 OpenRouter API 的 API 密钥。您可以从 OpenRouter 仪表板 获取此密钥。(例如:sk-or-v1... )
- OPENROUTER_DEFAULT_MODEL:包中将使用的默认模型 - 对于 generateInstructions 功能是必要的(在代码库中,您仍然可以为 OpenRouter 请求指定任何您想要的模型)。您可以从 OpenRouter 模型 检查模型列表。(例如:'mistralai/mistral-7b-instruct:free')
➡️ functions_yml_path
添加函数 yml 文件的路径,该文件是您的项目可调用的函数列表。在 生成函数列表 部分提供了对函数 yml 文件的深入探讨;如何定义函数、需要使用的格式等。(例如:__DIR__ . '/../resources/functions.yml')。
➡️ function_signature_mapping
:添加函数签名命名的映射。在定义函数签名映射部分,可以深入了解函数签名映射。例如:new FunctionSignatureMappingData(['parameters' => new MappingData(['path' => 'input_schema.properties', 'type' => 'array']), ...])
。
➡️ schemas
:添加函数负载和函数结果负载所需的路径。
function_payload_schema_path
:函数负载模式路径。在函数负载模式部分,可以深入了解函数负载模式。例如:__DIR__ . '/../resources/schemas/function_payload_schema.yml'
。function_results_schema_path
:函数结果模式路径。在函数结果模式部分,可以深入了解函数结果模式。例如:__DIR__ . '/../resources/schemas/function_results_schema.yml'
。
➡️ instructions
:添加prompt_function_instructions、function_results_instructions和generate_prompt_function_instructions的说明。
prompt_function_instructions
:用于提示函数负载的LLM的说明。在生成提示函数说明部分,可以深入了解提示函数说明。例如:你是一个严格遵守指示并提供响应的AI助手...function_results_instructions
:用于函数结果负载的LLM的说明。在准备函数结果负载部分,可以深入了解函数结果说明。例如:你将严格遵守以下指示...generate_prompt_function_instructions
:使用generateInstructions
函数生成prompt_function_instructions的说明。在生成提示函数说明部分,可以深入了解生成提示函数说明。例如:你的角色是分析提供的"函数"并...
⚡ 快速使用指南
此软件包旨在具有灵活性
,但为了快速入门
,请按照以下步骤操作。
- 在您选择的目录下创建一个名为
functions.yml
的文件。根据需要修改配置文件中的functions_yml_path
。有关详细信息,请参阅生成函数列表。
'functions_yml_path' => __DIR__ . '/../resources/functions.yml', // functions.yml is located under resources folder in this example.
- 使用使用generateFunctionList方法生成
函数列表
。它将自动将所有可能的函数签名详细信息/描述
添加到functions.yml
文件中,同时考虑函数的docblock。
(添加LLM应考虑的所有类
和函数
,尽可能在FunctionData
中添加更多信息,以获得更好的性能)
$class = Example::class; $functionDataA = new FunctionData([ 'function_name' => 'getFinancialData', 'parameters' => [ new ParameterData([ 'name' => 'userId', 'type' => 'int', 'required' => true, 'description' => 'The unique identifier for the user.', 'example' => 12345 ]), new ParameterData([ 'name' => 'startDate', 'type' => 'string', 'required' => true, 'description' => 'The starting date for the timeframe (inclusive).', 'example' => '2023-01-01' ]), new ParameterData([ 'name' => 'endDate', 'type' => 'string', 'required' => true, 'description' => 'The ending date for the timeframe (inclusive).', 'example' => '2023-01-31' ]), ], 'visibility' => VisibilityType::PUBLIC, 'description' => 'Retrieves financial data for a specific user and timeframe.', 'return' => new ReturnData([ 'type' => 'object', 'description' => 'An object containing details like totalAmount, transactions (array), and other relevant financial data.' ]), ]); $functionDataB = new FunctionData([ 'function_name' => 'categorizeTransactions', 'parameters' => [ new ParameterData([ 'name' => 'transactions', 'type' => 'array', 'required' => true, 'description' => 'An array of transactions with details like amount, date, and description.', 'example' => [ ['amount' => 100, 'date' => '2023-01-01', 'description' => 'Groceries'], ['amount' => 50, 'date' => '2023-01-02', 'description' => 'Entertainment'] ] ]), ], 'visibility' => VisibilityType::PUBLIC, 'description' => 'Categorizes a list of transactions based on predefined rules or machine learning models.', 'return' => new ReturnData([ 'type' => 'array', 'description' => 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.', 'example' => [ ['amount' => 100, 'date' => '2023-01-01', 'description' => 'Groceries', 'category' => 'Food', 'confidenceScore' => 0.95], ['amount' => 50, 'date' => '2023-01-02', 'description' => 'Entertainment', 'category' => 'Leisure', 'confidenceScore' => 0.8] ] ]), ]); $functions = [$functionDataA, $functionDataB]; $fileName = __DIR__ . '/../resources/functions.yml'; // Path and the name of the file that function list will be generated // Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed). LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
- 创建用于
函数负载模式
的yml文件,并相应地修改配置中的function_payload_schema_path
。有关详细信息,请参阅函数负载模式。
示例function_payload_schema.yml
如下:
-
function_name: getFinancialData
parameters: [{ name: userId, type: int }, { name: startDate, type: string }, { name: endDate, type: string }]
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\ExampleA
-
function_name: getCreditScore
parameters: [{ name: userId, type: int }]
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\ExampleD
function_payload_schema_path
的配置文件路径
'function_payload_schema_path' => __DIR__ . '/../resources/schemas/function_payload_schema.yml', // function_payload_schema.yml is located under resources/schemas folder in this example.
- 在您的环境文件中添加
OPENROUTER_API_KEY
、OPENROUTER_DEFAULT_MODEL
和OPENROUTER_API_ENDPOINT
- 如配置中的env_variables
中定义(OPENROUTER_API_ENDPOINT
已具有默认值,可以跳过;OPENROUTER_API_KEY
和OPENROUTER_DEFAULT_MODEL
应按配置部分所述设置)。 - 保留
prompt_function_instructions
在 config 中不变,或者查看 生成提示函数说明 以生成 自定义 的新说明。 - 为工具使用(函数调用)请求 Laravel OpenRouter,以便 LLM 决定对于 给定的提示 调用哪些函数。(注意: Laravel OpenRouter 已经包含了这个包 - 作为默认的 LLM 提供者 - 因此不需要单独安装)
$prompt = 'Can tell me Mr. Boolean Bob credit score?'; $model = config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models $content = LaravelPromptAlchemist::preparePromptFunctionPayload($prompt); // Prepare payload for the request. $messageData = new MessageData([ 'content' => json_encode($content), 'role' => RoleType::USER, ]); $chatData = new ChatData([ 'messages' => [ $messageData, ], 'model' => $model, 'temperature' => 0.1, // Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. ]); // Send OpenRouter request $response = LaravelOpenRouter::chatRequest($chatData);
- 验证 从 OpenRouter 获取的响应。(查看 验证函数签名 以获取更多详细信息)。
LLM 返回的函数示例
// $response = LaravelOpenRouter::chatRequest($chatData); $responseContentData = str_replace("\n", "", (Arr::get($response->choices[0], 'message.content'))); // Get content from the response. $llmReturnedFunctions = json_decode($responseContentData, true); // Functions returned from LLM. // Foreach $llmReturnedFunctions and get each function to validate: $llmReturnedFunction = [ // Sample LLM returned function "function_name" => "getFinancialData", "parameters" => [ [ "name" => "userId", "type" => "int"], [ "name" => "startDate", "type" => "string"], [ "name" => "endDate", "type" => "string"], ], 'class_name' => 'MoeMizrak\LaravelPromptAlchemist\Tests\Example' ]; // Formed LLM returned function data (FunctionData). $llmReturnedFunctionData = LaravelPromptAlchemist::formLlmReturnedFunctionData($llmReturnedFunction);
调用 validateFunctionSignature
对 $llmReturnedFunctionData
的函数签名进行 验证。
$isValid = LaravelOpenRouter::validateFunctionSignature($llmReturnedFunctionData);
- 最后,调用 LLM 返回的 函数,这些函数对于回答 提示 是必要的(由于 函数签名 已 验证,现在调用 LLM 返回的函数 是安全的)。(查看 调用函数(实际函数调用) 以获取更多详细信息)。
(注意: 您需要设置 参数值 才能调用函数。必须设置 必需的参数,否则将返回 ErrorData。最好在调用 validateFunctionSignature
时设置参数值,因为如果设置了,也会验证 提供的值 的类型,但这也可以在这一步完成。)
在调用函数之前,如下设置 参数值。
// Create parameter values, you just need parameter name and its value in correct type (int, string, array, object ...) $parameters = [ new ParameterData([ 'name' => 'userId', 'value' => 99, // int userId ]), new ParameterData([ 'name' => 'startDate', 'value' => '2023-06-01', // string startDate ]), new ParameterData([ 'name' => 'endDate', 'value' => '2023-07-01', // string endDate ]), ]; if (true === $isValid) { // Set parameter values. $llmReturnedFunctionData->setParameterValues($parameters); }
如下 调用函数。
$functionResultData = LaravelPromptAlchemist::callFunction($llmReturnedFunctionData);
示例函数结果($functionResultData)DTO 对象
output: FunctionResultData([ 'function_name' => 'getFinancialData', 'result' => (object) [ 'totalAmount' => 1000.0, 'transactions' => [ ['amount' => 100, 'date' => '2023-01-01', 'description' => 'Groceries'], ['amount' => 200, 'date' => '2023-01-02', 'description' => 'Utilities'], ], 'message' => 'Retrieved financial data for user 99 from 2023-06-01 to 2023-07-01' ], ])
其中 function_name
是根据名称建议的 调用函数的名称,而 result
是 函数调用结果 可以是任何东西(void、数组、对象、bool 等。你的函数返回什么)。
- 可选地,您还可以将 函数结果 发送到 LLM,以便根据
function_results_schema
返回答案。(查看 准备函数结果有效载荷 以获取更多详细信息)
$prompt = 'Can tell me Mr. Boolean Bob credit score?'; $model = config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models $functionResults = [ new FunctionResultData([ 'function_name' => 'getFinancialData', 'result' => [ 'totalAmount' => 122, 'transactions' => [ [ 'amount' => 12, 'date' => '2023-02-02', 'description' => 'food', ], ] ] ]), new FunctionResultData([ 'function_name' => 'getCreditScore', 'result' => [ 'creditScore' => 0.8, 'summary' => 'reliable', ] ]), ... ]; // Prepare function results payload for the request. $content = LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults); $messageData = new MessageData([ 'content' => json_encode($content), 'role' => RoleType::USER, ]); $chatData = new ChatData([ 'messages' => [ $messageData, ], 'model' => $model, 'temperature' => 0.1, // Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. ]); // Send OpenRouter request for function results $response = LaravelOpenRouter::chatRequest($chatData);
其中 $response
是 LLM 根据函数 function_results_instructions
形成的答案所形成的 function_results_schema
。
🎨 使用
此包提供了两种与 Laravel Prompt Alchemist 包交互的方式
- 使用
LaravelPromptAlchemist
门面(即 使用门面)。 - 直接实例化
PromptAlchemistRequest
类(即 使用 PromptAlchemistRequest 类)。
使用 Facade
LaravelPromptAlchemist
门面提供了一种方便的方式来发起 Laravel Prompt Alchemist 请求。以下部分将引导您进一步配置和使用 LaravelPromptAlchemist
门面。
生成函数列表
为了生成函数列表(functions.yml),您可以选择以下方法
手动创建函数列表
使用您选择的命名约定在 扁平关联数组结构 中 手动 创建函数签名。
确保与 function_signature_mapping 一致,它使您的命名实践与包格式一致以进行集成。(注意: 请参阅 定义函数签名映射 部分,以了解哪些函数签名应在函数列表 yml 文件中定义,以获得更好的性能)
- 创建任何首选命名和目录的函数列表 yml 文件。
例如:您可以在项目资源文件夹下创建一个名为 functions.yml
的文件。
__DIR__ . '/../resources/functions.yml'
- 在 yml 文件中定义您将用于包中的函数(用于工具使用 - 函数调用)。
例如:您可以在 扁平关联数组结构 或您选择的函数命名约定中创建以下示例中的函数。
推荐的命名约定(用于生成函数列表的 generateFunctionList 方法,也以这种格式输出函数列表):
-
function_name: getFinancialData
parameters: [{ name: userId, type: int, required: true, description: 'The unique identifier for the user.', example: 12345 }, { name: startDate, type: string, required: true, description: 'The starting date for the timeframe (inclusive).', example: '2023-01-01' }, { name: endDate, type: string, required: true, description: 'The ending date for the timeframe (inclusive).', example: '2023-01-31' }]
visibility: public
description: 'Retrieves financial data for a specific user and timeframe. '
return: { type: object, description: 'An object containing details like totalAmount, transactions (array), and other relevant financial data.' }
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\Example
-
function_name: categorizeTransactions
parameters: [{ name: transactions, type: array, required: true, description: 'An array of transactions with details like amount, date, and description.', example: [{ amount: 100, date: '2023-01-01', description: 'Groceries' }, { amount: 50, date: '2023-01-02', description: 'Entertainment' }] }]
visibility: public
description: 'Categorizes a list of transactions based on predefined rules or machine learning models. '
return: { type: array, description: 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.', example: [{ amount: 100, date: '2023-01-01', description: 'Groceries', category: 'Food', confidenceScore: 0.95 }, { amount: 50, date: '2023-01-02', description: 'Entertainment', category: 'Leisure', confidenceScore: 0.8 }] }
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\Example
- function: getFinancialData
input_schema:
type: object
properties:
- name: userId
type: int
required: true
description: 'The unique identifier for the user.'
- name: startDate
type: string
required: true
description: 'The starting date for the timeframe (inclusive).'
- name: endDate
type: string
required: true
description: 'The ending date for the timeframe (inclusive).'
description: 'Retrieves financial data for a specific user and timeframe.'
return:
type: object
description: 'An object containing details like totalAmount, transactions (array), and other relevant financial data.'
class: MoeMizrak\LaravelPromptAlchemist\Tests\Example
- function: categorizeTransactions
input_schema:
properties:
- name: transactions
type: array
required: true
description: 'An array of transactions with details like amount, date, and description.'
description: 'Categorizes a list of transactions based on predefined rules or machine learning models.'
return:
type: array
description: 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.'
class: MoeMizrak\LaravelPromptAlchemist\Tests\Example
使用 generateFunctionList 方法
使用 generateFunctionList
函数从一个给定的类生成详细的函数列表,并将其写入 yml 格式的文件。
(对于每个不同的类名,您需要运行 generateFunctionList
生成函数列表 - functions.yml,每次调用 generateFunctionList
时列表都会附加)
ℹ️ 自动生成函数列表是一种良好的实践。在函数描述(functionData)中定义的字段会 覆盖 如果存在的话函数预定义的字段。如果函数签名字段缺失,则将其添加到函数列表定义中。
$class = Example::class; // Or you can give fully qualified class name string as $class = 'MoeMizrak\LaravelPromptAlchemist\Tests\Example' // Function descriptions that will be used to generate function list (functions.yml) $functionDataA = new FunctionData([ 'function_name' => 'getFinancialData', 'parameters' => [ new ParameterData([ 'name' => 'userId', 'type' => 'int', 'required' => true, 'description' => 'The unique identifier for the user.', 'example' => 12345 ]), new ParameterData([ 'name' => 'startDate', 'type' => 'string', 'required' => true, 'description' => 'The starting date for the timeframe (inclusive).', 'example' => '2023-01-01' ]), new ParameterData([ 'name' => 'endDate', 'type' => 'string', 'required' => true, 'description' => 'The ending date for the timeframe (inclusive).', 'example' => '2023-01-31' ]), ], 'visibility' => VisibilityType::PUBLIC, 'description' => 'Retrieves financial data for a specific user and timeframe.', 'return' => new ReturnData([ 'type' => 'object', 'description' => 'An object containing details like totalAmount, transactions (array), and other relevant financial data.' ]), ]); $functionDataB = new FunctionData([ 'function_name' => 'categorizeTransactions', 'parameters' => [ new ParameterData([ 'name' => 'transactions', 'type' => 'array', 'required' => true, 'description' => 'An array of transactions with details like amount, date, and description.', 'example' => [ ['amount' => 100, 'date' => '2023-01-01', 'description' => 'Groceries'], ['amount' => 50, 'date' => '2023-01-02', 'description' => 'Entertainment'] ] ]), ], 'visibility' => VisibilityType::PUBLIC, 'description' => 'Categorizes a list of transactions based on predefined rules or machine learning models.', 'return' => new ReturnData([ 'type' => 'array', 'description' => 'An array of transactions with an added "category" field if successfully categorized. Each transaction may also include a "confidenceScore" field.', 'example' => [ ['amount' => 100, 'date' => '2023-01-01', 'description' => 'Groceries', 'category' => 'Food', 'confidenceScore' => 0.95], ['amount' => 50, 'date' => '2023-01-02', 'description' => 'Entertainment', 'category' => 'Leisure', 'confidenceScore' => 0.8] ] ]), ]); $functions = [$functionDataA, $functionDataB]; $fileName = __DIR__ . '/../resources/functions.yml'; // Path and the name of the file that function list will be generated // Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed). LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
另一种 创建 functions
数组的方法;当函数签名和 docblock 定义良好 时,只需添加函数名称即可创建一个全面的函数列表
$class = Example::class; // Or you can give fully qualified class name string as $class = 'MoeMizrak\LaravelPromptAlchemist\Tests\Example' // Name of the functions that will be added to the list - functions that will be used for Tool Use (Function Calling) $functions = [ 'getFinancialData', 'categorizeTransactions', ]; $fileName = __DIR__ . '/../resources/functions.yml'; // Path and the name of the file that function list will be generated // Call generateFunctionList for automated function list generation in given $fileName (Creates file in this path if not existed). LaravelPromptAlchemist::generateFunctionList($class, $functions, $fileName);
良好定义的函数 签名和 docblock 的示例。 (带有足够信息的 docblock 描述、类型提示参数、返回类型声明和返回标签描述在 docblock 中、参数描述在 docblock 中)
/** * This public function is intended for testing purposes. It accepts a string and int parameters and returns a string. * It has additional parameter descriptions and detailed docblock. * * @param string $stringParam This is the string param description * @param int $intParam This is the int param description * * @return string This is the return value description */ public function detailedDocBlockFunction(string $stringParam, int $intParam = 2): string { return 'detailed docblock function return value ' . $stringParam . ' ' . $intParam; }
定义不良的函数 签名和 docblock 的示例。 (没有 docblock、没有参数的类型提示、没有返回类型声明)
function noExtraInfoProvidedFunction($stringParam, $intParam) { return 'missing parameter and docblock function return value ' . $stringParam . ' ' . $intParam; }
部分定义的函数 签名和 docblock 的示例
/** * This public function is intended for testing purposes. * It has missing parameter descriptions and missing type-hint. * * @return string This is the return value description */ public function functionWithSomeMissingDocBlockAndMissingTypeHint($stringParam, $intParam): string { return 'missing parameter docblock function return value ' . $stringParam . ' ' . $intParam; }
根据您的代码库中的最佳实践,您可以选择如何生成函数列表(functions.yml)。
- 如果您的代码库中的函数 定义良好,则可以使用 替代方法 向函数数组中发送函数名称,无需提供其他信息(
generateFunctionList
会为您完成所有工作)。 - 如果您的代码库中的函数 定义不良,那么最好使用 主要方法,为每个函数创建 FunctionData DTO 并设置与函数相关的信息/描述以创建函数数组。
- 如果您的代码库中的函数 部分定义,那么同样使用 主要方法 是最佳选择,因为更多信息可以帮助 LLM 更好地了解您的函数以做出最佳决策。
注意:您只需在 FunctionData DTO 中添加 缺失/模糊/定义不良 的字段,并跳过已 定义良好 的描述/字段即可。请注意,添加到 FunctionData DTO 的字段 覆盖 函数声明中现有的预定义描述/字段。 (基本上,如果字段添加到 FunctionData DTO,则将其考虑在内;如果字段没有添加到 FunctionData 并且在函数声明中存在,则此预定义描述/字段将添加到函数列表中)。
定义函数签名映射
根据 函数列表(functions.yml),调整您的函数命名约定以与 集成包格式 保持一致。更好的做法是为函数签名(FunctionSignatureMappingData
)设置 所有可能的字段 以获得更好的性能,因为提供给 LLM 的信息越多,结果越好。
需要在 配置文件 中定义 function_signature_mapping
,以下是一些示例:
(如下示例所示,使用 '[]'
表示数组。在包中,它被替换为索引键,例如 parameters[].name
变为 parameters.{key}.name
,对于第一个数组索引是 parameters.0.name
,对于第二个是 parameters.1.name
等。)
- 如果您的函数列表(functions.yml)是按照 推荐命名规范 创建的
'function_signature_mapping' => new FunctionSignatureMappingData([ 'function_name' => new MappingData([ 'path' => 'function_name', 'type' => 'string', ]), 'function_description' => new MappingData([ 'path' => 'description', 'type' => 'string', ]), 'function_visibility' => new MappingData([ 'path' => 'visibility', 'type' => 'string', ]), 'function_return_type' => new MappingData([ 'path' => 'return.type', 'type' => 'string', ]), 'function_return_description' => new MappingData([ 'path' => 'return.description', 'type' => 'string', ]), 'function_return_example' => new MappingData([ 'path' => 'return.example', 'type' => 'mixed', ]), 'parameters' => new MappingData([ 'path' => 'parameters', // could be arguments, parameter_definitions, input_schema.properties, parameters.properties 'type' => 'array', ]), 'parameter_name' => new MappingData([ 'path' => 'parameters[].name', // since parameters field is array, '[]' states the index key which will be resolved in the package as 'parameters.0.name' for the first array and so on. 'type' => 'string', ]), 'parameter_type' => new MappingData([ 'path' => 'parameters[].type', 'type' => 'string', ]), 'parameter_required_info' => new MappingData([ 'path' => 'parameters[].required', 'type' => 'boolean', ]), 'parameter_description' => new MappingData([ 'path' => 'parameters[].description', 'type' => 'string', ]), 'parameter_example' => new MappingData([ 'path' => 'parameters[].example', 'type' => 'mixed', ]), 'parameter_default' => new MappingData([ 'path' => 'parameters[].default', 'type' => 'mixed', ]), 'class_name' => new MappingData([ 'path' => 'class_name', 'type' => 'string', ]) ]),
- 如果您的函数列表(functions.yml)是按照 另一个命名规范 创建的
'function_signature_mapping' => new FunctionSignatureMappingData([ 'function_name' => new MappingData([ 'path' => 'function', 'type' => 'string', ]), 'function_description' => new MappingData([ 'path' => 'description', 'type' => 'string', ]), 'function_visibility' => new MappingData([ 'path' => 'visibility', 'type' => 'string', ]), 'function_return_type' => new MappingData([ 'path' => 'return.type', 'type' => 'string', ]), 'function_return_description' => new MappingData([ 'path' => 'return.description', 'type' => 'string', ]), 'function_return_example' => new MappingData([ 'path' => 'return.example', 'type' => 'mixed', ]), 'input_schema_type' => new MappingData([ 'path' => 'input_schema.type', 'type' => 'string', ]), 'parameters' => new MappingData([ 'path' => 'input_schema.properties', 'type' => 'array', ]), 'parameter_name' => new MappingData([ 'path' => 'input_schema.properties[].name', // since properties field is array, '[]' states the index key which will be resolved in the package as 'properties.0.name' for the first array and so on. 'type' => 'string', ]), 'parameter_type' => new MappingData([ 'path' => 'input_schema.properties[].type', 'type' => 'string', ]), 'parameter_required_info' => new MappingData([ 'path' => 'input_schema.properties[].required', 'type' => 'boolean', ]), 'parameter_description' => new MappingData([ 'path' => 'input_schema.properties[].description', 'type' => 'string', ]), 'parameter_example' => new MappingData([ 'path' => 'input_schema.properties[].example', 'type' => 'mixed', ]), 'parameter_default' => new MappingData([ 'path' => 'input_schema.properties[].default', 'type' => 'mixed', ]), 'class_name' => new MappingData([ 'path' => 'class', 'type' => 'string', ]) ]),
关于这两个示例,您可以根据选择的命名规范在配置文件中定义您的 function_signature_mapping
。
生成提示函数指令
本节定义了指令,这些指令给出了对所需格式答案的 严格描述 以及 LLM 提供商将如何处理提供的提示、模式等。
您可以使用您自己的创建的/生成的指令为 prompt_function_instructions
,或者使用 generateInstructions
方法。
generateInstructions
方法简单地生成关于您的 函数列表(functions.yml
)、函数有效载荷模式(function_payload_schema.yml
)和 生成指令的提示(config
中的 generate_prompt_function_instructions
)的 定制指令。
(注意: generate_prompt_function_instructions
是 config 中的提示/指令,描述如何使用 函数 和 函数有效载荷模式 生成特定/定制的指令。)
为 generateInstructions
调用的示例 generate_prompt_function_instructions
You are an AI assistant tasked with providing instructions to another AI system on how to respond to a given prompt with a specific JSON format. Your role is to analyze the provided "functions" and "function_payload_schema" and create a set of instructions that will ensure the other AI system generates a response following the specified format.
The "functions" field contains a list of available functions, their parameters, descriptions, and return types. The "function_payload_schema" field specifies the expected format for the response, which should be a JSON array listing the required fields for each function.
Your instructions should cover the following points:
1. Read the provided "prompt" and the list of available "functions".
2. Identify which function(s) from the "functions" list are needed to answer the prompt.
3. Analyze the "function_payload_schema" to determine the required fields for each function in the response JSON array. Do not add or omit any fields from the schema.
4. Explain that the response should ONLY contain a JSON array following the exact format specified in the "function_payload_schema". No additional fields, values, text, or explanations should be included.
5. Specify that the JSON array should list the required fields for each function, as specified in the "function_payload_schema".
6. Emphasize that no other information beyond the JSON array matching the "function_payload_schema" format should be added.
7. Ensure that the response can be directly used in PHP code without any modifications.
8. For the parameter fields of each function, analyze the "function_payload_schema" and include the fields exactly as specified in the schema, without making any assumptions about the field names or structure.
9. Clarify that no actual values for the parameters should be provided. Only the parameter fields as specified in the "function_payload_schema" should be included.
10. If no relevant function is found in the "functions" list to answer the prompt, the response should be the string "NULL" without any additional description or text.
11. If the other AI system cannot understand or follow the instructions, it should return the string "NULL" without any additional description or text.
Your response should be a clear and concise set of instructions that the other AI system can follow to generate the desired JSON response format based on the provided "functions" and "function_payload_schema".
OK, now provide the instructions as described above for the other AI system to generate the desired JSON response format based on the provided "functions" and "function_payload_schema"
为了生成 prompt_function_instructions
,您可以调用 generateInstructions
方法
LaravelPromptAlchemist::generateInstructions();
generateInstructions
的示例响应将看起来像
You are an AI assistant that strictly follows instructions and provides responses in a specific format.
Your task is to analyze a given prompt and identify the required functions from a provided list to answer the prompt.
Your response should be a JSON array that lists the required function names, their parameters (name and type only), and the class_name, following the exact format specified in the "function_payload_schema".
Do not include any additional information, explanations, or values beyond what is specified in the schema. Adhere to the following instructions:
1. Read the provided "prompt" and the list of available "functions".
2. Identify which function(s) from the "functions" list are needed to answer the prompt.
3. Your response should ONLY contain a JSON array following the exact format specified in the "function_payload_schema". Do not include any additional fields, values, text, or explanations.
4. The JSON array should list the required function names, their parameters (name and type only), and the class_name.
5. Do not add any other information beyond the JSON array matching the "function_payload_schema" format.
6. Ensure that the response can be directly used in PHP code without any modifications.
7. Do not provide any actual values for the parameters. Only include the parameter names and types as specified in the "function_payload_schema".
8. If you do not understand the instructions or cannot provide a response following the specified format, respond with "NULL".
9. If no relevant function is found in the "functions" list to answer the prompt, the response should be the string "NULL" without any additional description or text
定义模式
本节定义了用于格式化 函数有效载荷 和 函数结果 的所需模式样本。基本上,模式是用于决定 LLM 的 响应格式。它们与其他信息(提示、函数 - 函数结果、指令等)一起发送给 LLM,并在指令中要求 LLM 按照提供的模式提供结果。这样,LLM 响应就可以在包中解析,以便用于 工具使用(函数调用)。
函数有效载荷模式
定义 函数有效载荷结构 的模式。
- 为函数有效载荷模式创建 yml 文件,使用任何首选的命名和目录,将其添加到配置文件中的
schemas
。
'schemas' => [ 'function_payload_schema_path' => __DIR__ . '/../resources/schemas/function_payload_schema.yml', ]
- 如下示例定义模式。命名规范应与函数列表(functions.yml)一致,因为 LLM 响应将取决于此模式,响应将在包中通过比较函数列表(functions.yml)文件进行 验证。
-
function_name: getFinancialData
parameters: [{ name: userId, type: int }, { name: startDate, type: string }, { name: endDate, type: string }]
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\Example
-
function_name: getCreditScore
parameters: [{ name: userId, type: int }]
class_name: MoeMizrak\LaravelPromptAlchemist\Tests\ExampleD
函数结果模式
定义 函数结果结构 的模式。此模式用于决定 最终响应,其中 函数结果 发送到 LLM 以形成响应,在 工具使用(函数调用) 之后。
如果您不会将 函数调用 结果发送到 LLM,则不需要此模式。您可能更愿意在 您的代码库 中使用 函数结果 直接生成响应。
- 为函数结果模式创建 yml 文件,使用任何首选的命名和目录,将其添加到配置文件中的
schemas
。
'schemas' => [ 'function_results_schema_path' => __DIR__ . '/../resources/schemas/function_results_schema.yml', ]
- 如下示例定义模式。
-
function_name: getFinancialData
result: [{ name: transactions, type: array, value: [{ amount: , date: '2023-02-02', description: shoes }] }, { name: totalAmount, type: int, value: 1234 }]
-
function_name: getCreditScore
result: [{ name: creditScore, type: float, value: 0.5 }, { name: summary, type: string, value: reliable }]
准备提示函数有效载荷
此方法负责根据给定的提示和函数列表(functions.yml)创建一个结构化有效载荷模板。该方法构建一个包含 指令、提示、函数列表 和 函数有效载荷模式 的数组。指令详细说明了如何处理提示,确保响应严格遵循提供的格式。
用于工具使用(功能调用)的提示
$prompt = 'Can tell me Mr. Boolean Bob credit score?';
这是如何通过外观调用 preparePromptFunctionPayload 方法的示例
LaravelPromptAlchemist::preparePromptFunctionPayload($prompt);
以下是预期的准备好的有效载荷响应样本
[ "prompt" => "Can tell me Mr. Boolean Bob credit score?", "instructions" => "You are an AI assistant that strictly follows instructions and provides responses in a specific format.\n Your task is to analyze a given prompt and identify the required functions from a provided list to answer the prompt.\n Your response should be a JSON array that lists the required function names, their parameters (name and type only), and the class_name, following the exact format specified in the \"function_payload_schema\".\n Do not include any additional information, explanations, or values beyond what is specified in the schema. Adhere to the following instructions:\n 1. Read the provided \"prompt\" and the list of available \"functions\".\n 2. Identify which function(s) from the \"functions\" list ...", "functions" => [ [ "function_name" => "getFinancialData", "parameters" => [ ["name" => "userId", "type" => "int"], ["name" => "startDate", "type" => "string"], ["name" => "endDate", "type" => "string"] ], "visibility" => "public", "description" => "Retrieves financial data for a specific user and timeframe.", "return" => [ "type" => "object", "description" => "An object containing details like totalAmount, transactions (array), and other relevant financial data." ], "class_name" => "MoeMizrak\LaravelPromptAlchemist\Tests\Example" ], [ "function_name" => "getCreditScore", "parameters" => [ ["name" => "userId", "type" => "int"] ], "visibility" => "public", "description" => "Retrieves the current credit score for a specific user.", "return" => [ "type" => "object", "description" => "An object containing the credit score, credit report summary, and any relevant notes." ], "class_name" => "MoeMizrak\LaravelPromptAlchemist\Tests\Example" ], ... ], "function_payload_schema" => [ [ "function_name" => "getFinancialData", "parameters" => [ ["name" => "userId", "type" => "int"], ["name" => "startDate", "type" => "string"], ["name" => "endDate", "type" => "string"] ], "class_name" => "MoeMizrak\LaravelPromptAlchemist\Tests\ExampleA" ], [ "function_name" => "getCreditScore", "parameters" => [ ["name" => "userId", "type" => "int"] ], "class_name" => "MoeMizrak\LaravelPromptAlchemist\Tests\ExampleD" ], ... ] ]
向 OpenRouter 发送工具使用(函数调用)请求
由于此包以灵活的方式设计,您可以使用 Laravel OpenRouter(请查看 OpenRouter 的 GitHub 仓库以获取更多信息),它是此包的 默认 LLM 提供商,或者您可以使用此包与任何其他 LLM 提供商一起发送工具使用(功能调用)请求。
这是 OpenRouter 请求的示例
$prompt = 'Can tell me Mr. Boolean Bob credit score?'; $model = config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models $content = LaravelPromptAlchemist::preparePromptFunctionPayload($prompt); $messageData = new MessageData([ 'content' => json_encode($content), 'role' => RoleType::USER, ]); $chatData = new ChatData([ 'messages' => [ $messageData, ], 'model' => $model, 'temperature' => 0.1, // Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. ]); // Send OpenRouter request $response = LaravelOpenRouter::chatRequest($chatData);
示例 Laravel OpenRouter 响应(返回 ResponseData)
output: ResponseData([ 'id' => 'gen-YFd68mMgTkrfHVvkdemwYxdGSfZA', 'model' => 'mistralai/mistral-7b-instruct:free', 'object' => 'chat.completion', 'created' => 1719251736, 'choices' => [ 0 => [ 'index' => 0, 'message' => [ 'role' => 'assistant', 'content' => '["function_name":"getFinancialData", "parameters":[{"name":"userId","type":"int"},{"name":"startDate","type":"string"},{"name":"endDate","type":"string"}],"function_name":"categorizeTransactions", "parameters":[{"name":"transactions","type":"array"}],"function_name":"getTopCategories", "parameters":[{"name":"transactions","type":"array"}]]', ], 'finish_reason' => 'stop', ] ], 'usage' => UsageData([ 'prompt_tokens' => 1657, 'completion_tokens' => 97, 'total_tokens' => 1754, ]) ]);
验证函数签名
验证 LLM 返回的函数签名。对于缺少/错误的字段的 错误 函数签名,它返回 布尔值 或 ErrorData。您可以从 发送工具使用(功能调用)请求到 OpenRouter 返回的 ResponseData 中检索 LLM 返回的函数。
示例 LLM 返回的函数
// $response = LaravelOpenRouter::chatRequest($chatData); as shown in [Send Tool Use (Function Calling) Request to OpenRouter] section $responseContentData = str_replace("\n", "", (Arr::get($response->choices[0], 'message.content'))); // Get content from the response. $llmReturnedFunctions = json_decode($responseContentData, true); // Functions returned from LLM. // Foreach $llmReturnedFunctions and get each function to validate: $llmReturnedFunction = [ // Sample LLM returned function "function_name" => "getFinancialData", "parameters" => [ [ "name" => "userId", "type" => "int"], [ "name" => "startDate", "type" => "string"], [ "name" => "endDate", "type" => "string"], ], 'class_name' => 'MoeMizrak\LaravelPromptAlchemist\Tests\Example' ]; // Form LLM returned function data (FunctionData). $llmReturnedFunctionData = $this->request->formLlmReturnedFunctionData($llmReturnedFunction); // Add parameter values if exists, only name and value of the parameter are set. $parameters = [ new ParameterData([ 'name' => 'userId', 'value' => 99, ]), new ParameterData([ 'name' => 'startDate', 'value' => '2023-06-01', ]), new ParameterData([ 'name' => 'endDate', 'value' => '2023-07-01', ]), ]; // Set parameter values in $llmReturnedFunctionData DTO $llmReturnedFunctionData->setParameterValues($parameters);
以下是验证从 LLM 返回的函数签名的示例(如上所示的 已形成 llm 返回的函数,参数值也 已设置)
$isValid = LaravelOpenRouter::validateFunctionSignature($llmReturnedFunctionData); // $isValid is bool or ErrorData
如果 LLM 返回的函数签名是 无效的,则这是一个返回的 ErrorData 示例
output: ErrorData([ 'code' => 400, 'message' => 'Function invalidFunctionName does not exist in class MoeMizrak\LaravelPromptAlchemist\Tests\Example' ]);
调用函数(实际函数调用)
此方法使用提供的函数签名以及参数值进行 实际函数调用。由于在 验证函数签名 步骤之前执行了 验证函数签名,因此函数签名是 安全的,可以进行此函数调用(在 验证函数签名 步骤中 设置 参数值)。无论函数可见性(私有、受保护、公共 等)如何,都可以直接调用函数。
与 LLM 的内置功能不同,LLM 的内置功能可能只能列出函数。基本上,使用此包,您可以从 LLM 获取有关提示的 函数列表,然后进行实际的函数调用以检索 函数结果。
这是如何 调用函数
$functionResult = LaravelPromptAlchemist::callFunction($llmReturnedFunctionData);
结果作为 FunctionResultData
DTO 对象返回,如下所示为示例结果
output: FunctionResultData([ 'function_name' => 'getFinancialData', 'result' => [ 'totalAmount' => 122, 'transactions' => [ [ 'amount' => 12, 'date' => '2023-02-02', 'description' => 'food', ], ] ] ]),
其中 function_name
是被调用的 函数名称,result
是此 函数返回的任何内容(数组、布尔值、整数、混合、对象、void 等)
准备函数结果有效载荷
此方法负责根据给定的提示和 函数结果 创建一个结构化有效载荷模板,稍后将发送到 LLM 提供商。此方法构建一个包含 指令、提示、函数结果 和 函数结果模式 的数组。指令详细说明了如何处理提示,确保响应严格遵循提供的格式作为 函数结果模式。
用于准备 函数结果有效载荷 的提示
$prompt = 'Can tell me Mr. Boolean Bob credit score?'; $functionResults = [ new FunctionResultData([ 'function_name' => 'getFinancialData', 'result' => [ 'totalAmount' => 122, 'transactions' => [ [ 'amount' => 12, 'date' => '2023-02-02', 'description' => 'food', ], ] ] ]), new FunctionResultData([ 'function_name' => 'getCreditScore', 'result' => [ 'creditScore' => 0.8, 'summary' => 'reliable', ] ]), ... ];
这是如何通过外观调用 prepareFunctionResultsPayload 方法的示例
LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults);
以下是预期的准备好的函数结果有效载荷样本
[ "prompt" => "Can tell me Mr. Boolean Bob credit score?", "instructions" => "You will strictly follow the instructions:\n - Understand the provided prompt and answer the prompt using the function_results (needed info is provided in function_results). If function_results are not sufficient enough, then your answer will be \"Please provide more information about [missing information]\"\n - Respond based on the function_results_schema sample provided (Do not add any extra info, exactly the same format provided in function_results_schema).\n - Format the response as an array following ...", "function_results" => [ [ "function_name" => "getFinancialData", "result" => [ "totalAmount" => 122, "transactions" => [ [ "amount" => 12, "date" => "2023-02-02", "description" => "food" ] ] ] ], [ "function_name" => "getCreditScore", "result" => [ "creditScore" => 0.8, "summary" => "reliable" ] ] ... ], "function_results_schema" => [ [ "function_name" => "getFinancialData", "result" => [ [ "name" => "transactions", "type" => "array", "value" => [ [ "amount" => null, "date" => "2023-02-02", "description" => "shoes" ] ] ], [ "name" => "totalAmount", "type" => "int", "value" => 1234 ] ] ], [ "function_name" => "getCreditScore", "result" => [ [ "name" => "creditScore", "type" => "float", "value" => 0.5 ], [ "name" => "summary", "type" => "string", "value" => "reliable" ] ] ] ... ] ]
将函数结果发送到 OpenRouter
由于此包以灵活的方式设计,您可以使用 Laravel OpenRouter(请查看 OpenRouter 的 GitHub 仓库以获取更多信息),它是此包的默认 LLM 提供商,或者您可以使用此包与任何其他 LLM 提供商一起发送工具使用(功能调用)请求。
这是针对 函数结果 的 OpenRouter 请求的示例
$prompt = 'Can tell me Mr. Boolean Bob credit score?'; $functionResults = [ new FunctionResultData([ 'function_name' => 'getFinancialData', 'result' => [ 'totalAmount' => 122, 'transactions' => [ [ 'amount' => 12, 'date' => '2023-02-02', 'description' => 'food', ], ] ] ]), new FunctionResultData([ 'function_name' => 'getCreditScore', 'result' => [ 'creditScore' => 0.8, 'summary' => 'reliable', ] ]), ... ]; $content = LaravelPromptAlchemist::prepareFunctionResultsPayload($prompt, $functionResults); $model = config('laravel-prompt-alchemist.env_variables.default_model'); // Check https://openrouter.ai/docs/models for supported models $messageData = new MessageData([ 'content' => json_encode($content), 'role' => RoleType::USER, ]); $chatData = new ChatData([ 'messages' => [ $messageData, ], 'model' => $model, 'temperature' => 0.1, // Set temperature low to get better result. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. ]); // Send OpenRouter request $response = LaravelOpenRouter::chatRequest($chatData);
示例 Laravel OpenRouter 响应(返回 ResponseData)
output: ResponseData([ 'id' => 'gen-YFd68mMgTkrfHVvkdemwYxdGSfZA', 'model' => 'mistralai/mistral-7b-instruct:free', 'object' => 'chat.completion', 'created' => 1719440793, 'choices' => [ 0 => [ 'index' => 0, 'message' => [ 'role' => 'assistant', 'content' => '[{"name": "creditScore", "type": "float", "value": 0.8}, {"name": "summary", "type": "string", "value": "reliable"}]', ], 'finish_reason' => 'stop', ] ], 'usage' => UsageData([ 'prompt_tokens' => 657, 'completion_tokens' => 69, 'total_tokens' => 726, ]) ]);
使用 PromptAlchemistRequest 类
您还可以在类的构造函数中注入 PromptAlchemistRequest
类并直接使用其方法。
public function __construct(protected PromptAlchemistRequest $promptAlchemistRequest) {}
在其他一切保持不变的情况下,使用 使用外观模式,您可以使用 PromptAlchemistRequest
调用以下方法
// generateFunctionList request. $this->promptAlchemistRequest->generateFunctionList($class, $functions, $fileName); // Generate instructions request. $this->promptAlchemistRequest->generateInstructions(); // Prepare prompt function payload request. $this->promptAlchemistRequest->preparePromptFunctionPayload($prompt); // Forms LLM returned function into the FunctionData. $this->promptAlchemistRequest->formLlmReturnedFunctionData($llmReturnedFunction); // Validate function signature returned by the LLM request. $this->promptAlchemistRequest->validateFunctionSignature($llmReturnedFunctionData); // $isValid is bool or ErrorData // Make an actual function call. $this->promptAlchemistRequest->callFunction($llmReturnedFunctionData); // Prepare function results payload request. $this->promptAlchemistRequest->prepareFunctionResultsPayload($prompt, $functionResults);
💫 贡献
我们欢迎贡献! 如果您想改进这个包,只需创建一个带有您更改的拉取请求。您的努力有助于提高其功能和文档。
📜 许可证
Laravel Prompt Alchemist 是一个开源软件,许可协议为 MIT 许可证。