graph LR
A["接收调用请求"] --> B["验证参数"]
B --> C["执行工具"]
C --> D["处理结果"]
D --> E["返回给模型"]
图 8-3:工具执行流程
在执行前验证参数的有效性:
def validate_params(tool_name, params, schema):
# 检查必需参数
for required in schema.get("required", []):
if required not in params:
raise ValueError(f"缺少必需参数: {required}")
# 检查参数类型
for key, value in params.items():
expected_type = schema["properties"][key]["type"]
if not isinstance(value, TYPE_MAP[expected_type]):
raise TypeError(f"参数 {key} 类型错误")
# 检查枚举值
for key, value in params.items():
if "enum" in schema["properties"][key]:
if value not in schema["properties"][key]["enum"]:
raise ValueError(f"参数 {key} 值无效")简单场景,等待执行完成:
result = execute_tool(tool_name, params)IO 密集型或耗时操作:
async def execute_tools(tool_calls):
tasks = [execute_tool_async(tc) for tc in tool_calls]
results = await asyncio.gather(*tasks)
return results防止工具执行无限等待:
try:
result = await asyncio.wait_for(
execute_tool(tool_name, params),
timeout=30.0
)
except asyncio.TimeoutError:
result = {"error": "执行超时"}将工具结果格式化为模型易理解的形式:
简洁输出
包含上下文
{
"query": "北京天气",
"result": {
"city": "北京",
"temperature": 25,
"condition": "晴",
"humidity": 45
},
"data_time": "2026-03-01 10:00"
}工具执行可能失败,需要优雅处理:
| 错误类型 | 处理方式 | 反馈给模型 |
|---|---|---|
| 参数错误 | 不执行 | 说明具体问题 |
| 执行失败 | 记录日志 | 返回错误信息 |
| 超时 | 终止执行 | 建议稍后重试 |
| 权限不足 | 拒绝执行 | 说明权限要求 |
{
"status": "error",
"error_type": "execution_failed",
"message": "无法连接到天气服务",
"suggestion": "请稍后再试或换一个城市查询"
}工具结果需要正确注入对话上下文:
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": 25, \"condition\": \"晴\"}"
}工具返回大量数据时可能需要压缩:
def compress_result(result, max_tokens=500):
if count_tokens(result) > max_tokens:
# 提取关键信息
return summarize(result)
return result复杂任务可能需要多轮工具调用:
sequenceDiagram
participant M as 模型
participant A as 应用
M->>A: 调用工具 A
A->>M: 工具 A 结果
Note over M: 分析结果,决定下一步
M->>A: 调用工具 B
A->>M: 工具 B 结果
Note over M: 综合结果,生成响应
M->>A: 最终响应
图 8-4:多轮工具调用流程
每轮结果都成为上下文的一部分,影响后续决策。
工具执行涉及安全风险:
- 输入验证:防止注入攻击
- 权限控制:验证操作权限
- 操作审计:记录敏感操作
- 确认机制:危险操作需人工确认
随着智能体需要使用的工具数量增长,传统的工具调用方式面临挑战。以下介绍两项高级技术:
问题:当连接多个 MCP 服务器时,工具定义可能消耗大量 Token(规模取决于工具数量、Schema 复杂度与描述长度):
解决方案:不预加载所有工具,而是按需发现。
{
"tools": [
{"type": "tool_search_tool", "name": "tool_search"},
{
"name": "github.createPullRequest",
"defer_loading": true
}
]
}效果(常见观察):
- 只暴露必要工具可以显著降低上下文开销
- 工具选择更聚焦,误调用与歧义通常会减少
问题:传统工具调用有两个瓶颈:
- 中间结果污染上下文(分析 10MB 日志文件,全部进入上下文)
- 每次调用需要一次推理,开销大
解决方案:让模型生成受限的工作流计划,由服务端在沙箱和权限边界内执行;不要让模型生成的任意代码直接持有团队、费用、客户或生产系统凭据。
# 模型只生成声明式计划;服务端负责鉴权、执行、脱敏和审计
workflow = {
"operation": "expense_budget_check",
"team": "engineering",
"period": "Q3",
"output": "over_budget_summary",
}
result = await workflow_runner.run(
workflow,
caller=user_identity,
policy_checks=["team_scope", "read_only", "pii_redaction"],
network_scope="internal_tools_only",
)
return result.redacted_summary效果(常见观察):
- 上下文只接收最终结果而非原始数据,通常能显著降低 Token 消耗
- 降低中间结果“污染上下文”的概率,并减少不必要的多轮推理
使用指南:
| 技术 | 适用场景 |
|---|---|
| Tool Search Tool | 工具定义 >10K Token、10+ 工具 |
| Programmatic Tool Calling | 处理大数据集只需摘要、3+ 步依赖工具调用 |
以下展示一个完整的数据分析助手工作流,包含规划、执行、结果处理和上下文更新。
场景:用户上传 CSV 文件并要求:“分析上季度的销售趋势,并画图展示。”
流程图:
graph TB
User["用户输入"] --> Plan["规划器(Planner)"]
Plan --> Step1["步骤1: 读取CSV"]
Step1 --> Tool1["调用 read_csv"]
Tool1 --> Res1["返回 DataFrame 摘要"]
Res1 --> Step2["步骤2: 分析数据"]
Step2 --> Tool2["调用 python_repl"]
Tool2 --> Res2["生成图表(Base64)"]
Res2 --> Final["生成最终回复"]
图 8-5:端到端数据分析工作流
关键代码实现逻辑:
async def run_analysis_agent(user_query, context):
messages = context + [{"role": "user", "content": user_query}]
# 1. 规划阶段
plan = await llm.plan(messages)
# output: 1. read_csv, 2. analyze_trend, 3. plot_chart
results = {}
for step in plan.steps:
# 2. 执行阶段
tool_name = step.tool
tool_args = step.args
try:
# 3. 结果处理
# 仅截取前5行数据作为上下文,避免 Token 爆炸
raw_result = await tools.execute(tool_name, tool_args)
processed_result = truncate_dataframe(raw_result)
# 注入当前步骤结果到上下文
messages.append({
"role": "tool",
"tool_call_id": step.tool_call_id,
"content": processed_result
})
except Exception as e:
# 错误恢复机制
messages.append({"role": "system", "content": f"工具执行失败: {str(e)},请尝试其他方法"})
continue
# 4. 生成最终回复
final_response = await llm.chat(messages)
return final_response在生产环境中,需要关注以下指标来衡量工具执行系统的健康度:
| 指标 | 目标值(示意) | 说明 |
|---|---|---|
| 执行成功率 | 以基线提升为目标 | 工具调用无异常(Crash/Timeout)的比例 |
| 参数准确率 | 以基线提升为目标 | 模型生成的参数符合 Schema 定义且逻辑正确 |
| 平均延迟 | 取决于工具与 SLO | 工具本身的执行耗时(不含 LLM 推理) |
| 幻觉率 | 越低越好 | 调用了不存在的工具或虚构了参数 |
| Token 效率 | 越高越好 | 原始工具结果 vs 注入上下文的结果大小 |
优化建议:
- 对于高频且低延迟要求的工具(如查天气),使用本地逻辑而非 LLM 规划。
- 对于易错的复杂参数(如 SQL),在 System Prompt 中提供 Few-Shot 示例。
{ "status": "success", "data": { "temperature": 25, "condition": "晴" } }