Skip to main content

2.1 Function Calling:从说话到行动

Function Calling 是让大模型按约定格式输出调用指令,从而由外部系统真正去执行具体操作的一种机制。它让模型从"只会说话"变为"会调用工具"。

Function Calling 解决的核心问题

大模型的"能力边界"

大模型虽然知识丰富,但有些事情它"做不到":

❌ 获取实时信息(天气、股价、新闻)
❌ 执行具体操作(发邮件、查数据库、调用 API)
❌ 精确计算(复杂数学、代码执行)
❌ 访问私有数据(内部文档、用户数据)

Function Calling 就是突破这些边界的桥梁。

能力的范式跃迁

传统 LLM:输入 → 思考 → 输出文字
↓ 只能"说说"而已

带 Function Calling 的 LLM:输入 → 思考 → 决定调用工具 → 执行工具
→ 获取结果 → 继续思考 → 最终输出
↓ 真正"做到"事情

Function Calling 的核心机制

完整的调用流程

用户提问

LLM 分析:这个问题需要调用工具吗?
├─ 不需要 → 直接回答
└─ 需要 → 输出工具调用指令

外部系统解析指令,执行工具调用

获取工具执行结果

LLM 结合结果,生成最终回答

工具定义格式

以 OpenAI 格式为例:

{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
}

定义的三要素

  1. 名称(name):工具的唯一标识
  2. 描述(description):告诉模型这个工具是做什么的
  3. 参数(parameters):定义入参的类型、格式、约束

模型调用输出

{
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\",\"unit\":\"celsius\"}"
}
}]
}

工具结果回传

{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\":25,\"condition\":\"晴\",\"humidity\":60}"
}

工具设计的核心原则

1. 单一职责原则

❌ 不好的设计

def handle_user_request(request):
"""处理用户的任何请求"""
# 可能查天气、可能查股票、可能发邮件...

✅ 好的设计

def get_weather(city: str) -> dict:
"""获取指定城市的天气信息"""

def get_stock_price(symbol: str) -> float:
"""获取股票当前价格"""

def send_email(to: str, subject: str, body: str) -> bool:
"""发送邮件"""

2. 清晰描述原则

描述要写得像给人看的一样清楚,模型"读"得懂才会用。

❌ 描述太模糊

"description": "处理数据"

✅ 描述清晰

"description": "执行 SQL 查询并返回结果。注意:1. 只允许 SELECT 查询;
2. 查询必须包含 LIMIT 子句;3. 不允许查询 user_password 等敏感字段"

3. 强类型约束

尽可能使用枚举和具体类型约束,减少模型生成错误参数的概率。

❌ 太宽松

"format": {"type": "string"}

✅ 强约束

"format": {
"type": "string",
"enum": ["json", "csv", "markdown"],
"description": "输出格式"
}

4. 幂等性设计

工具调用可能重复,设计时要考虑安全。

✅ 推荐:get_weather(idempotent)
❌ 谨慎:delete_user(side effects)

常见工具类型与设计模式

1. 信息获取类

# 搜索引擎
def search_web(query: str, num_results: int = 5) -> list:
"""搜索网络获取信息"""

# 数据库查询
def execute_sql(query: str) -> list:
"""执行只读 SQL 查询"""

# 文件读取
def read_file(path: str) -> str:
"""读取文件内容"""

设计要点:限制返回大小,避免信息过载。

2. 操作执行类

# API 调用
def create_github_issue(repo: str, title: str, body: str) -> str:
"""在指定仓库创建 Issue"""

# 系统操作
def run_command(command: str, timeout: int = 30) -> dict:
"""执行系统命令(需白名单验证)"""

# 消息发送
def send_slack_message(channel: str, message: str) -> bool:
"""发送 Slack 消息"""

设计要点:权限验证、操作审计、风险控制。

3. 计算处理类

# 代码执行
def run_python_code(code: str) -> str:
"""在沙箱环境中执行 Python 代码"""

# 数据分析
def calculate_statistics(data: list) -> dict:
"""计算统计指标"""

# 格式转换
def convert_format(input: str, from_format: str, to_format: str) -> str:
"""格式转换"""

设计要点:沙箱隔离、资源限制、超时保护。

多工具协作模式

1. 链式调用

用户:帮我分析一下 AAPL 今天的走势并总结要点

1. get_stock_price("AAPL") → 获取价格
2. get_news("AAPL") → 获取相关新闻
3. analyze_report(price, news) → 生成分析报告

2. 分支选择

用户查询

分析意图
├─ 天气相关 → 调用 get_weather
├─ 股票相关 → 调用 get_stock_price
└─ 其他 → 直接回答

3. 循环调用直到完成

目标:写一份市场分析报告

循环:
1. 分析还缺什么信息
2. 调用工具获取
3. 整合到报告中
直到:报告完整或达到最大次数

错误处理与可靠性

常见错误场景

错误类型发生原因应对策略
参数错误模型生成的参数格式不对自动重试 + 友好提示修正
工具执行失败API 超时、网络错误重试机制、降级方案
误用工具模型用错了工具工具描述优化 + 结果校验
无限循环反复调用同一个工具最大调用次数限制

防御性设计

def safe_tool_call(tool_name, params):
# 1. 验证工具是否在白名单
if tool_name not in ALLOWED_TOOLS:
return "Error: Tool not allowed"

# 2. 参数校验
if not validate_params(tool_name, params):
return "Error: Invalid parameters"

# 3. 超时保护
try:
with timeout(30):
return execute_tool(tool_name, params)
except TimeoutError:
return "Error: Tool execution timed out"

# 4. 异常捕获
except Exception as e:
return f"Error: {str(e)}"

Function Calling 的进阶技巧

1. 思维链 + 工具调用

让模型先思考再决定调用什么工具:

先分析用户的问题,然后:
1. 说明你需要调用什么工具
2. 说明为什么需要这个工具
3. 输出工具调用

2. 并行工具调用

支持一次调用多个工具,提升效率:

用户:帮我查一下北京和上海今天的天气

→ 同时调用:
get_weather("北京")
get_weather("上海")

3. 工具动态选择

不是每次都把所有工具给模型,而是根据问题先筛选相关工具:

问题涉及代码 → 给出代码执行、文件操作工具
问题涉及搜索 → 给出搜索引擎工具
简单问题 → 不给出任何工具,直接回答

4. 工具调用的成本意识

每个工具都有成本:
- 时间成本(网络请求、计算耗时)
- 金钱成本(API 调用费用)

→ 让模型学会权衡:是否真的需要调用?有没有更便宜的方式?

Function Calling 是 Agent 的"双手"。没有它,大模型只是一个知识渊博的书呆子;有了它,模型才能真正动手做事。