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"]
}
}
}
定义的三要素:
- 名称(name):工具的唯一标识
- 描述(description):告诉模型这个工具是做什么的
- 参数(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 的"双手"。没有它,大模型只是一个知识渊博的书呆子;有了它,模型才能真正动手做事。