{T}

Function Calling 与工具调用

Function Calling 是大模型从"文本生成器"进化为"任务执行者"的关键机制。通过在请求中声明可用工具,模型能够自主决定何时调用哪个工具、传递什么参数,从而实现搜索、计算、文件操作等超越纯文本生成的能力。

核心概念

什么是 Function Calling

Function Calling(也称 Tool Use)允许开发者在 API 请求中向模型声明一组可用工具。模型在推理过程中,如果判断需要外部信息或操作来完成用户请求,会返回工具调用指令而非直接文本回复。

图表渲染中…

调用流程

步骤说明关键点
1. 声明工具在请求 tools 参数中定义可用函数JSON Schema 描述参数
2. 模型决策模型判断是否需要调用工具finish_reason: "tool_calls"
3. 执行工具应用层解析并执行工具调用开发者负责实际执行
4. 回传结果将工具输出以 tool 角色消息回传必须包含 tool_call_id
5. 生成回复模型基于工具结果生成最终回答可能触发多轮调用

工具定义规范

JSON Schema 格式

typescript
const tools = [
  {
    type: 'function',
    function: {
      name: 'get_current_weather',
      description: '获取指定城市的当前天气信息',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: '城市名称,如 "北京" 或 "上海"',
          },
          unit: {
            type: 'string',
            enum: ['celsius', 'fahrenheit'],
            description: '温度单位',
          },
        },
        required: ['location'],
      },
    },
  },
  {
    type: 'function',
    function: {
      name: 'search_web',
      description: '在互联网上搜索信息',
      parameters: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: '搜索关键词',
          },
          max_results: {
            type: 'number',
            description: '最大返回结果数',
          },
        },
        required: ['query'],
      },
    },
  },
];

工具定义最佳实践

原则说明
名称语义化使用动词+名词格式:get_weathercreate_file
描述精确明确说明工具用途、适用场景、限制条件
参数约束enumpatternminimum 等约束参数范围
必填标注required 数组标明必需参数
单一职责每个工具只做一件事,避免"万能工具"

完整实现

服务端工具调用循环

typescript
// server.ts
import express from 'express';
import axios from 'axios';
import { exec } from 'child_process';

const app = express();
app.use(express.json());

// 工具注册表
const toolHandlers: Record<string, (args: any) => Promise<string>> = {
  get_current_weather: async ({ location }) => {
    // 实际调用天气 API
    const res = await axios.get(`https://api.weather.com/v1/${location}`);
    return JSON.stringify(res.data);
  },
  run_shell_command: async ({ command }) => {
    return new Promise((resolve, reject) => {
      exec(command, (error, stdout) => {
        if (error) reject(error);
        else resolve(stdout);
      });
    });
  },
  search_web: async ({ query, max_results = 5 }) => {
    const res = await axios.get('https://api.search.com/search', {
      params: { q: query, limit: max_results },
    });
    return JSON.stringify(res.data.results);
  },
};

app.post('/chat', async (req, res) => {
  const { message } = req.body;
  const messages = [{ role: 'user', content: message }];

  // 工具调用循环(最多 5 轮)
  for (let i = 0; i < 5; i++) {
    const response = await axios.post(
      `${process.env.LLM_BASE_URL}/chat/completions`,
      {
        model: process.env.LLM_MODEL,
        messages,
        tools,
        tool_choice: 'auto',
      },
      {
        headers: { Authorization: `Bearer ${process.env.LLM_API_KEY}` },
      }
    );

    const choice = response.data.choices[0];

    // 如果模型直接回复文本,结束循环
    if (choice.finish_reason === 'stop') {
      return res.json({ reply: choice.message.content });
    }

    // 处理工具调用
    if (choice.finish_reason === 'tool_calls') {
      messages.push(choice.message); // 记录 assistant 的工具调用消息

      for (const toolCall of choice.message.tool_calls) {
        const { name, arguments: args } = toolCall.function;
        const handler = toolHandlers[name];

        let result: string;
        try {
          result = handler
            ? await handler(JSON.parse(args))
            : `Error: Unknown tool "${name}"`;
        } catch (e) {
          result = `Error: ${e.message}`;
        }

        // 回传工具结果
        messages.push({
          role: 'tool',
          tool_call_id: toolCall.id,
          content: result,
        });
      }
    }
  }

  res.json({ reply: '达到最大工具调用轮次限制' });
});

app.listen(3300);

tool_choice 参数

行为适用场景
"auto"模型自主决定是否调用工具通用对话(默认)
"none"禁止调用工具纯文本生成
"required"必须调用至少一个工具强制工具执行
{"type":"function","function":{"name":"xxx"}}强制调用指定工具确定性流程

本地模型工具调用(Ollama)

通过 Ollama 部署的本地模型同样支持 Function Calling:

typescript
// 使用 Ollama 的 OpenAI 兼容接口
const response = await axios.post(
  'http://localhost:11434/v1/chat/completions',
  {
    model: 'qwen3:1.7b',
    messages,
    tools,
    tool_choice: 'auto',
  }
);

本地 vs 云端对比

维度本地模型(Ollama)云端 API
隐私性数据不出本机数据传输至云端
延迟取决于硬件网络延迟 + 推理延迟
工具调用能力小模型可能不稳定大模型更可靠
成本硬件投入按 Token 计费
适用场景敏感数据、离线环境生产环境、复杂推理

MCP 协议(Model Context Protocol)

MCP 是 Anthropic 提出的开放协议,标准化了 AI 应用与外部工具/数据源的连接方式:

图表渲染中…

MCP vs 传统 Function Calling

维度Function CallingMCP
定义位置每次 API 请求中独立 Server 进程
复用性应用内复用跨应用共享
发现机制手动配置自动发现可用工具
生态各平台独立开放标准协议
适用场景应用内工具IDE、Agent 框架

安全注意事项

风险防护措施
命令注入工具参数严格校验,禁止直接拼接 shell 命令
权限越界工具白名单 + 最小权限原则
无限循环设置最大调用轮次(如 5 轮)
数据泄露工具结果脱敏后再回传模型
成本控制监控 Token 消耗,设置单次会话上限

常见问题与陷阱

问题原因解决方案
模型不调用工具工具描述不够清晰优化 description,使用 tool_choice: "required"
参数格式错误小模型 JSON 生成能力弱使用更大模型或增加参数校验 + 重试
工具调用死循环工具结果不满足模型预期设置最大轮次 + 循环检测
tool_call_id 缺失未正确回传必须原样返回模型给出的 id
并行工具调用模型一次返回多个 tool_calls遍历处理所有调用,全部回传后再生成

参考资源