Skip to content

工具调用与Function Calling

工具调用是Agent从"能说"到"能做"的关键桥梁

什么是Function Calling?

Function Calling(函数调用)是大模型的一项核心能力,允许模型在生成文本的同时,请求执行外部函数或工具。这使得AI能够获取实时信息、执行实际操作、与外部系统交互。

没有Function Calling:
────────────────────────────
用户:北京今天天气怎么样?
AI:根据我的训练数据,北京通常...(可能过时/不准确)

有Function Calling:
────────────────────────────
用户:北京今天天气怎么样?
AI:[调用get_weather("北京")]
    → 获取实时天气数据
    → 北京今天晴天,气温20-28℃,适合户外活动

核心价值

价值说明示例
实时数据获取最新信息天气、股价、新闻
外部操作执行实际动作发送邮件、创建订单
专业计算处理复杂计算数学运算、数据分析
系统集成连接外部服务数据库查询、API调用

Function Calling工作原理

工作流程

Function Calling完整流程:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  1. 用户请求                                                 │
│     "帮我查一下北京明天的天气"                                 │
│                    ↓                                        │
│  2. 模型分析                                                 │
│     LLM分析用户意图,判断需要调用工具                          │
│                    ↓                                        │
│  3. 生成函数调用                                             │
│     {                                                       │
│       "name": "get_weather",                                │
│       "arguments": {"city": "北京", "date": "明天"}          │
│     }                                                       │
│                    ↓                                        │
│  4. 执行函数                                                 │
│     客户端执行函数,获取结果                                   │
│                    ↓                                        │
│  5. 返回结果                                                 │
│     {"temperature": "15-25℃", "weather": "晴天"}            │
│                    ↓                                        │
│  6. 生成最终回复                                             │
│     "北京明天天气晴朗,气温15-25℃..."                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

关键概念

Tool Definition(工具定义)
────────────────────────────
告诉模型有哪些工具可用,以及如何使用

Tool Call(工具调用)
────────────────────────────
模型决定调用哪个工具,以及传递什么参数

Tool Result(工具结果)
────────────────────────────
执行工具后返回的结果

Tool Response(工具响应)
────────────────────────────
将结果返回给模型,模型生成最终回复

工具定义规范

OpenAI格式

OpenAI定义了业界最广泛使用的Function Calling格式:

php
<?php

// 定义工具列表
$tools = [
    [
        // 工具类型为函数
        'type' => 'function',
        'function' => [
            // 工具名称
            'name' => 'get_weather',
            // 工具描述
            'description' => '获取指定城市的天气信息',
            // 参数定义
            'parameters' => [
                'type' => 'object',
                'properties' => [
                    // 城市参数
                    'city' => [
                        'type' => 'string',
                        'description' => '城市名称,如:北京、上海'
                    ],
                    // 温度单位参数
                    'unit' => [
                        'type' => 'string',
                        'enum' => ['celsius', 'fahrenheit'],
                        'description' => '温度单位,默认摄氏度'
                    ]
                ],
                // 必填参数列表
                'required' => ['city']
            ]
        ]
    ]
];

参数类型详解

JSON Schema参数类型:
┌─────────────────────────────────────────┐
│ 类型        │ 说明          │ 示例       │
├─────────────────────────────────────────┤
│ string     │ 字符串        │ "北京"     │
│ number     │ 数字          │ 25         │
│ integer    │ 整数          │ 100        │
│ boolean    │ 布尔值        │ true       │
│ array      │ 数组          │ [1, 2, 3]  │
│ object     │ 对象          │ {"a": 1}   │
│ null       │ 空值          │ null       │
└─────────────────────────────────────────┘

特殊约束:
────────────────────────────
• enum:限定可选值
• minimum/maximum:数值范围
• minLength/maxLength:字符串长度
• pattern:正则表达式匹配
• default:默认值

复杂参数定义

php
<?php

// 定义复杂工具配置
$complexTool = [
    // 工具类型
    'type' => 'function',
    'function' => [
        // 工具名称
        'name' => 'search_products',
        // 工具描述
        'description' => '搜索商品信息',
        // 参数定义
        'parameters' => [
            'type' => 'object',
            'properties' => [
                // 搜索关键词参数
                'query' => [
                    'type' => 'string',
                    'description' => '搜索关键词'
                ],
                // 过滤器参数
                'filters' => [
                    'type' => 'object',
                    'properties' => [
                        // 商品分类
                        'category' => [
                            'type' => 'string',
                            'enum' => ['electronics', 'clothing', 'food']
                        ],
                        // 价格范围
                        'price_range' => [
                            'type' => 'object',
                            'properties' => [
                                // 最低价格
                                'min' => ['type' => 'number'],
                                // 最高价格
                                'max' => ['type' => 'number']
                            ]
                        ],
                        // 品牌列表
                        'brands' => [
                            'type' => 'array',
                            'items' => ['type' => 'string']
                        ]
                    ]
                ],
                // 排序方式
                'sort' => [
                    'type' => 'string',
                    'enum' => ['price_asc', 'price_desc', 'relevance'],
                    'default' => 'relevance'
                ],
                // 返回数量限制
                'limit' => [
                    'type' => 'integer',
                    'minimum' => 1,
                    'maximum' => 100,
                    'default' => 10
                ]
            ],
            // 必填参数
            'required' => ['query']
        ]
    ]
];

实现Function Calling

基础实现

php
<?php

// 引入自动加载文件
require_once __DIR__ . '/vendor/autoload.php';

use OpenAI\Client;

/**
 * 天气Agent类
 * 实现天气查询功能,支持Function Calling
 */
class WeatherAgent
{
    // OpenAI客户端实例
    private Client $client;

    /**
     * 构造函数
     * @param Client $client OpenAI客户端实例
     */
    public function __construct(Client $client)
    {
        // 初始化客户端
        $this->client = $client;
    }

    /**
     * 获取天气信息
     * @param string $city 城市名称
     * @param string $unit 温度单位
     * @return string 天气信息JSON
     */
    private function getWeather(string $city, string $unit = 'celsius'): string
    {
        // 模拟天气数据
        $weatherData = [
            '北京' => ['temp' => '20-28', 'condition' => '晴天'],
            '上海' => ['temp' => '22-30', 'condition' => '多云'],
        ];

        // 获取城市天气,不存在则返回未知
        $data = $weatherData[$city] ?? ['temp' => '未知', 'condition' => '未知'];

        // 返回JSON格式天气信息
        return json_encode([
            'city' => $city,
            'temperature' => $data['temp'],
            'condition' => $data['condition'],
            'unit' => $unit
        ], JSON_UNESCAPED_UNICODE);
    }

    /**
     * 获取工具定义列表
     * @return array 工具定义数组
     */
    private function getTools(): array
    {
        return [
            [
                // 工具类型
                'type' => 'function',
                'function' => [
                    // 工具名称
                    'name' => 'get_weather',
                    // 工具描述
                    'description' => '获取指定城市的天气信息',
                    // 参数定义
                    'parameters' => [
                        'type' => 'object',
                        'properties' => [
                            // 城市参数
                            'city' => [
                                'type' => 'string',
                                'description' => '城市名称'
                            ],
                            // 温度单位参数
                            'unit' => [
                                'type' => 'string',
                                'enum' => ['celsius', 'fahrenheit'],
                                'description' => '温度单位'
                            ]
                        ],
                        // 必填参数
                        'required' => ['city']
                    ]
                ]
            ]
        ];
    }

    /**
     * 运行Agent
     * @param string $userInput 用户输入
     * @return string Agent响应
     */
    public function run(string $userInput): string
    {
        // 初始化消息列表
        $messages = [
            ['role' => 'user', 'content' => $userInput]
        ];

        // 调用OpenAI API
        $response = $this->client->chat()->create([
            'model' => 'gpt-4',
            'messages' => $messages,
            'tools' => $this->getTools(),
            'tool_choice' => 'auto'
        ]);

        // 获取响应消息
        $message = $response->choices[0]->message;

        // 检查是否有工具调用
        if (isset($message->toolCalls) && count($message->toolCalls) > 0) {
            // 获取第一个工具调用
            $toolCall = $message->toolCalls[0];
            // 获取函数名称
            $functionName = $toolCall->function->name;
            // 解析函数参数
            $functionArgs = json_decode($toolCall->function->arguments, true);

            // 判断是否为天气查询函数
            if ($functionName === 'get_weather') {
                // 执行天气查询
                $functionResponse = $this->getWeather(
                    $functionArgs['city'],
                    $functionArgs['unit'] ?? 'celsius'
                );
            }

            $messages[] = $message->toArray();
            $messages[] = [
                'tool_call_id' => $toolCall->id,
                'role' => 'tool',
                'name' => $functionName,
                'content' => $functionResponse
            ];

            $secondResponse = $this->client->chat()->create([
                'model' => 'gpt-4',
                'messages' => $messages
            ]);

            return $secondResponse->choices[0]->message->content;
        }

        return $message->content;
    }
}

$client = OpenAI::factory()
    ->withApiKey($_ENV['OPENAI_API_KEY'])
    ->make();

$agent = new WeatherAgent($client);
echo $agent->run("北京今天天气怎么样?");

多工具调用处理

php
<?php

class MultiToolAgent
{
    private Client $client;
    private array $tools;

    public function __construct(Client $client, array $tools)
    {
        $this->client = $client;
        $this->tools = $tools;
    }

    public function run(string $userInput): string
    {
        $messages = [
            ['role' => 'user', 'content' => $userInput]
        ];

        while (true) {
            $response = $this->client->chat()->create([
                'model' => 'gpt-4',
                'messages' => $messages,
                'tools' => $this->tools,
                'tool_choice' => 'auto'
            ]);

            $message = $response->choices[0]->message;

            if (!isset($message->toolCalls) || count($message->toolCalls) === 0) {
                return $message->content;
            }

            $messages[] = $message->toArray();

            foreach ($message->toolCalls as $toolCall) {
                $functionName = $toolCall->function->name;
                $functionArgs = json_decode($toolCall->function->arguments, true);

                $result = $this->executeTool($functionName, $functionArgs);

                $messages[] = [
                    'tool_call_id' => $toolCall->id,
                    'role' => 'tool',
                    'name' => $functionName,
                    'content' => $result
                ];
            }
        }
    }

    private function executeTool(string $name, array $args): string
    {
        $toolFunctions = [
            'get_weather' => [$this, 'getWeather'],
            'search_web' => [$this, 'searchWeb'],
            'calculate' => [$this, 'calculate'],
        ];

        if (isset($toolFunctions[$name])) {
            return json_encode(call_user_func($toolFunctions[$name], $args), JSON_UNESCAPED_UNICODE);
        }

        return json_encode(['error' => "Unknown tool: {$name}"]);
    }

    private function getWeather(array $args): array
    {
        return ['city' => $args['city'], 'temp' => '20-28'];
    }

    private function searchWeb(array $args): array
    {
        return ['query' => $args['query'], 'results' => []];
    }

    private function calculate(array $args): array
    {
        return ['expression' => $args['expression'], 'result' => 0];
    }
}

并行工具调用

php
<?php

use React\Promise\Promise;

class ParallelToolExecutor
{
    public function executeToolsParallel(array $toolCalls): array
    {
        $promises = [];
        
        foreach ($toolCalls as $toolCall) {
            $promises[] = $this->executeSingleAsync($toolCall);
        }
        
        $results = [];
        foreach ($promises as $promise) {
            $results[] = $promise->wait();
        }
        
        return $results;
    }

    private function executeSingleAsync(object $toolCall): Promise
    {
        return new Promise(function ($resolve) use ($toolCall) {
            $functionName = $toolCall->function->name;
            $functionArgs = json_decode($toolCall->function->arguments, true);
            $result = $this->asyncExecuteTool($functionName, $functionArgs);
            
            $resolve([
                'tool_call_id' => $toolCall->id,
                'role' => 'tool',
                'name' => $functionName,
                'content' => $result
            ]);
        });
    }

    private function asyncExecuteTool(string $name, array $args): string
    {
        return match ($name) {
            'get_weather' => $this->asyncGetWeather($args),
            'search_web' => $this->asyncSearchWeb($args),
            default => json_encode(['error' => 'Unknown tool'])
        };
    }
}

工具选择策略

tool_choice参数

php
<?php

$toolChoiceOptions = [
    'auto' => '模型自动决定是否调用工具',
    'none' => '模型不调用任何工具',
    'required' => '模型必须调用至少一个工具',
];

$response = $client->chat()->create([
    'model' => 'gpt-4',
    'messages' => $messages,
    'tools' => $tools,
    'tool_choice' => 'auto'
]);

$response = $client->chat()->create([
    'model' => 'gpt-4',
    'messages' => $messages,
    'tools' => $tools,
    'tool_choice' => [
        'type' => 'function',
        'function' => ['name' => 'get_weather']
    ]
]);

动态工具选择

php
<?php

class DynamicToolSelector
{
    private array $allTools;
    private array $toolEmbeddings;

    public function __construct(array $allTools)
    {
        $this->allTools = $allTools;
        $this->toolEmbeddings = $this->computeEmbeddings();
    }

    public function selectRelevantTools(string $query, int $topK = 5): array
    {
        $queryEmbedding = $this->embed($query);
        $similarities = $this->computeSimilarities($queryEmbedding);
        
        arsort($similarities);
        $topIndices = array_slice(array_keys($similarities), 0, $topK, true);
        
        return array_map(fn($i) => $this->allTools[$i], $topIndices);
    }

    private function computeEmbeddings(): array
    {
        $embeddings = [];
        foreach ($this->allTools as $tool) {
            $text = "{$tool['function']['name']}: {$tool['function']['description']}";
            $embeddings[] = $this->embed($text);
        }
        return $embeddings;
    }
}

不同平台的Function Calling

OpenAI

php
<?php

$response = $client->chat()->create([
    'model' => 'gpt-4',
    'messages' => $messages,
    'tools' => $tools,
    'tool_choice' => 'auto',
    'parallel_tool_calls' => true
]);

Anthropic Claude

php
<?php

use Anthropic\Client;

$client = new Client();

$response = $client->messages()->create([
    'model' => 'claude-3-opus-20240229',
    'max_tokens' => 1024,
    'tools' => [
        [
            'name' => 'get_weather',
            'description' => '获取天气信息',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'city' => ['type' => 'string']
                ],
                'required' => ['city']
            ]
        ]
    ],
    'messages' => [
        ['role' => 'user', 'content' => '北京天气怎么样?']
    ]
]);

foreach ($response->content as $block) {
    if ($block['type'] === 'tool_use') {
        $toolName = $block['name'];
        $toolInput = $block['input'];
    }
}

国内大模型

php
<?php

use ZhipuAI\Client;

$client = new Client();

$response = $client->chat()->create([
    'model' => 'glm-4',
    'messages' => $messages,
    'tools' => $tools,
    'tool_choice' => 'auto'
]);

use Dashscope\Client;

$client = new Client();

$response = $client->generation()->call([
    'model' => 'qwen-max',
    'messages' => $messages,
    'tools' => $tools,
    'result_format' => 'message'
]);

高级技巧

工具结果验证

php
<?php

class ToolResultValidator
{
    private array $validators = [];

    public function __construct()
    {
        $this->validators = [
            'get_weather' => [$this, 'validateWeatherResult'],
            'search_web' => [$this, 'validateSearchResult'],
        ];
    }

    public function validate(string $toolName, string $result): array
    {
        try {
            $data = json_decode($result, true, 512, JSON_THROW_ON_ERROR);
        } catch (\JsonException $e) {
            return ['valid' => false, 'error' => 'Invalid JSON: ' . $e->getMessage()];
        }

        if (isset($this->validators[$toolName])) {
            return call_user_func($this->validators[$toolName], $data);
        }

        return ['valid' => true, 'data' => $data];
    }

    private function validateWeatherResult(array $data): array
    {
        $requiredFields = ['city', 'temperature', 'condition'];
        $missing = array_diff($requiredFields, array_keys($data));

        if (!empty($missing)) {
            return [
                'valid' => false,
                'error' => 'Missing fields: ' . implode(', ', $missing)
            ];
        }

        return ['valid' => true, 'data' => $data];
    }

    private function validateSearchResult(array $data): array
    {
        if (!isset($data['results']) || !is_array($data['results'])) {
            return ['valid' => false, 'error' => 'Missing or invalid results array'];
        }

        return ['valid' => true, 'data' => $data];
    }
}

错误处理与重试

php
<?php

class RobustToolExecutor
{
    private int $maxRetries = 3;

    public function executeWithRetry(object $toolCall): string
    {
        for ($attempt = 0; $attempt < $this->maxRetries; $attempt++) {
            try {
                $result = $this->executeTool($toolCall);
                $validation = (new ToolResultValidator())->validate(
                    $toolCall->function->name,
                    $result
                );

                if ($validation['valid']) {
                    return $result;
                }

                if ($attempt < $this->maxRetries - 1) {
                    $correctedArgs = $this->correctArgs(
                        $toolCall->function->arguments,
                        $validation['error']
                    );
                    $toolCall->function->arguments = $correctedArgs;
                }

            } catch (\Exception $e) {
                if ($attempt === $this->maxRetries - 1) {
                    return json_encode([
                        'error' => $e->getMessage(),
                        'retries' => $attempt + 1
                    ], JSON_UNESCAPED_UNICODE);
                }
                sleep(2 ** $attempt);
            }
        }

        return json_encode(['error' => 'Max retries exceeded']);
    }
}

工具链式调用

php
<?php

class ToolChainExecutor
{
    public function executeToolChain(array $toolCalls): string
    {
        $results = [];
        $context = [];

        foreach ($toolCalls as $toolCall) {
            $args = json_decode($toolCall->function->arguments, true);

            foreach ($context as $key => $value) {
                if (isset($args[$key]) && $args[$key] === '$previous_result') {
                    $args[$key] = $value;
                }
            }

            $result = $this->executeTool($toolCall->function->name, $args);
            $results[] = $result;

            $context['previous_result'] = $result;
            $context["{$toolCall->function->name}_result"] = $result;
        }

        return end($results);
    }
}

最佳实践

工具定义原则

1. 描述清晰
────────────────────────────
❌ 差的描述:
"description": "获取信息"

✅ 好的描述:
"description": "获取指定城市的实时天气信息,包括温度、湿度、风速等"

2. 参数完整
────────────────────────────
• 必填参数标记为required
• 可选参数提供default值
• 使用enum限定可选值
• 提供参数示例

3. 单一职责
────────────────────────────
每个工具只做一件事:
• get_weather - 获取天气
• search_web - 搜索网页
• 不要设计get_weather_and_news这样的工具

4. 合理粒度
────────────────────────────
• 太粗:execute_task - 太泛化
• 太细:add_number - 太细碎
• 适中:calculate_expression - 合适

性能优化

1. 减少工具数量
────────────────────────────
• 只提供相关工具
• 动态加载工具
• 避免工具过多导致选择困难

2. 并行调用
────────────────────────────
• 独立的工具调用并行执行
• 减少总等待时间

3. 结果缓存
────────────────────────────
• 缓存频繁调用的结果
• 设置合理的TTL

4. 流式输出
────────────────────────────
• 使用streaming模式
• 提前展示中间结果

安全考虑

安全注意事项

工具调用涉及外部系统,需要特别注意安全:

python
class SecureToolExecutor:
    ALLOWED_DOMAINS = ["api.example.com", "api.weather.com"]
    MAX_RESULT_SIZE = 1024 * 1024
    
    def execute_tool(self, name: str, args: dict) -> str:
        if not self.is_tool_allowed(name):
            raise PermissionError(f"Tool {name} is not allowed")
        
        if not self.validate_args(name, args):
            raise ValueError("Invalid arguments")
        
        result = self.do_execute(name, args)
        
        if len(result) > self.MAX_RESULT_SIZE:
            result = result[:self.MAX_RESULT_SIZE]
        
        self.log_tool_call(name, args, result)
        
        return result
    
    def is_tool_allowed(self, name: str) -> bool:
        return name in self.allowed_tools
    
    def validate_args(self, name: str, args: dict) -> bool:
        if "url" in args:
            return self.is_url_allowed(args["url"])
        return True
    
    def is_url_allowed(self, url: str) -> bool:
        from urllib.parse import urlparse
        domain = urlparse(url).netloc
        return domain in self.ALLOWED_DOMAINS

实战案例

智能客服工具集

python
customer_service_tools = [
    {
        "type": "function",
        "function": {
            "name": "query_order",
            "description": "查询订单状态",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "订单号"
                    }
                },
                "required": ["order_id"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "create_ticket",
            "description": "创建工单",
            "parameters": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "description": {"type": "string"},
                    "priority": {
                        "type": "string",
                        "enum": ["low", "medium", "high"]
                    }
                },
                "required": ["title", "description"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_knowledge_base",
            "description": "搜索知识库",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "category": {
                        "type": "string",
                        "enum": ["product", "shipping", "returns", "payment"]
                    }
                },
                "required": ["query"]
            }
        }
    }
]

数据分析工具集

python
data_analysis_tools = [
    {
        "type": "function",
        "function": {
            "name": "query_database",
            "description": "执行SQL查询",
            "parameters": {
                "type": "object",
                "properties": {
                    "sql": {
                        "type": "string",
                        "description": "SQL查询语句(只允许SELECT)"
                    },
                    "database": {
                        "type": "string",
                        "description": "数据库名称"
                    }
                },
                "required": ["sql"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "generate_chart",
            "description": "生成数据图表",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {"type": "array"},
                    "chart_type": {
                        "type": "string",
                        "enum": ["bar", "line", "pie", "scatter"]
                    },
                    "title": {"type": "string"},
                    "x_axis": {"type": "string"},
                    "y_axis": {"type": "string"}
                },
                "required": ["data", "chart_type"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate_statistics",
            "description": "计算统计指标",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {"type": "array"},
                    "metrics": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "enum": ["mean", "median", "std", "min", "max"]
                        }
                    }
                },
                "required": ["data", "metrics"]
            }
        }
    }
]

关键术语速查

术语英文解释
Function CallingFunction Calling大模型调用外部函数的能力
Tool DefinitionTool Definition工具的定义,包括名称、描述、参数
Tool CallTool Call模型发起的工具调用请求
Tool ResultTool Result工具执行后返回的结果
JSON SchemaJSON Schema定义JSON数据结构的规范
Parallel Tool CallsParallel Tool Calls并行调用多个工具

学习检验

概念理解

  1. Function Calling的完整工作流程是什么?
  2. 如何定义一个复杂的工具参数?
  3. tool_choice参数有哪些选项?各有什么作用?

实践任务

  1. 定义一个发送邮件的工具
  2. 实现一个支持多工具调用的对话系统
  3. 添加工具结果验证和错误处理

下一步学习


💡 记住:工具调用是Agent能力的核心,好的工具设计让Agent更强大、更可靠。