第 14 章 工具增强 Tool Augmentation

第 14 章 工具增强 Tool Augmentation

本章要解决的问题

Agent 的能力边界取决于工具库------工具该设计哪些、怎么让模型正确选择和调用?

章节大纲

  • 14.1 工具库设计原则
  • 14.2 搜索、计算、代码执行等工具实战
  • 14.3 工具选择策略
  • 14.4 主流框架对照与变体
  • 🛠 解决方案:工具幻觉参数识别与校验

14.1 模式原理:工具是 Agent 的手脚

14.1.1 一句话定义

工具增强(Tool Augmentation)是通过让模型调用外部函数/API,把它的能力从"纯文本推理"扩展到"可计算、可检索、可执行、可写系统"。模型负责"想",工具负责"做"。

这是全书第 5 章(工具调用 Tool Calling)的"模式化延伸":第 5 章讲的是单次调用的机制与排错,本章讲的是工具库的整体设计哲学------工具库怎么组织、暴露多少、怎么让模型选对。

14.1.2 为什么工具是关键分水岭

一个残酷的事实:没有工具的模型是"嘴上巨人"。让它做数学题,它可能一本正经算错;让它查最新股价,它只能胡编。工具的引入让模型发生了质变:

图 1:无工具 vs 有工具

能力 无工具 有工具
数学计算 易算错、不可复现 调计算器,精确
实时信息 知识截止日前的旧数据 调搜索/API,最新
代码执行 只能"读"代码 能跑、能验证
数据库操作 只能描述 能查询真实数据
系统动作 只能建议 能下单/发邮件/写文件

工具是第 13 章结论的"最优解":知识型错误反思修不了,工具能------模型不知道就调用工具去查,这是"能力外挂"。

14.1.3 工具库设计的黄金比例

工具库不是越多越好。每多一个工具定义,就多一份上下文占用,也增加模型选错的概率。设计原则:

markdown 复制代码
工具数量与质量:
  - 3~5 个核心工具:覆盖 80% 高频需求(最佳状态)
  - 6~10 个:中大型系统,按域分组
  - 10+ 个:必须分组 + 显式路由(见 14.3),否则工具选择错误率可能显著上升

宁可少而精,不可多而滥。 每加一个工具前问自己:这个工具是覆盖一个真实的高频需求,还是"可能有用"?后者不加。

14.2 工具库设计原则

14.2.1 工具定义五要素

一个高质量的工具定义(Function Schema)必须包含:

图 2:工具定义五要素

要素 说明 例子
名称 动词开头、语义清晰 query_order_status 而非 get
描述 说明用途 + 何时用 + 何时不用 "查询订单状态,当用户询问物流/发货进度时使用"
参数 Schema 类型、必填、枚举、默认值 order_id: string, required
边界 声明不能做的事 "仅支持近 90 天订单"
返回说明 返回结构说明(帮助模型理解结果) "返回 JSON:{status, eta}"

描述是工具的灵魂。模型"选工具"主要靠读描述,描述写得好,选择准确率直接提升。一个反例 vs 正例:

text 复制代码
❌ 描述:"处理订单相关请求"
✅ 描述:"查询订单物流状态。当用户询问'货到哪了/什么时候发货/快递进度'时使用;
         不支持退款申请(用 apply_refund)。"

14.2.2 工具名称与描述的对齐测试

用 20~30 条真实用户问题测"模型能否为每个问题选对工具"。如果某工具的选中率低于 60%,不是模型笨,是描述不清楚------回去改描述。工具的选型准确率是可以用评测集量化的(呼应第 20 章)。

14.2.3 工具封装层:别让模型直接碰原始系统

生产环境的铁律:给模型暴露"安全封装"而非"原始系统"

图 3:安全封装层

原始系统 模型不应直接访问 应暴露的封装
生产数据库 直接 SQL query_reports(start, end) 只读 + 限字段
订单系统 直接改库 create_order(items) 带权限校验
文件系统 任意路径读写 read_workspace(path) 白名单路径
邮件 直接发送 send_email(to, subject, body) 审核后发

封装层的三个作用:安全(权限/白名单)、稳定性(异常兜底)、可观测(统一日志)(呼应第 22 章安全、第 21 章可观测)。

14.3 完整示例:搜索、计算、代码执行三件套

实现一个"分析师助手"的工具库------三个经典工具,覆盖"查资料、算数字、验证代码"三大高频需求:

图 4:工具增强闭环

python 复制代码
import json, subprocess, re
from openai import OpenAI

client = OpenAI(base_url="https://api.deepseek.com", api_key="<你的Key>")

# ── 工具 1:计算器(防模型算错数学)──
def calculator(expr: str) -> str:
    """安全表达式求值:仅允许数字/运算符/括号"""
    if not re.fullmatch(r"[0-9+\-*/().\s]+", expr):
        return json.dumps({"error": "非法表达式"})
    try:
        result = eval(expr, {"__builtins__": {}}, {})
        return json.dumps({"result": result})
    except Exception as e:
        return json.dumps({"error": str(e)})

# ── 工具 2:代码执行沙箱(验证代码正确性)──
def run_python(code: str) -> str:
    """在隔离沙箱执行 Python 代码,返回 stdout/stderr。
    注意:本示例仅做超时和输出截断,不具备真正的沙箱隔离。
    生产环境应使用容器(Docker)、Firejail、gVisor 或云端沙箱服务隔离执行。"""
    try:
        proc = subprocess.run(
            ["python3", "-c", code],
            capture_output=True, text=True, timeout=10,
        )
        return json.dumps({
            "stdout": proc.stdout[:2000],
            "stderr": proc.stderr[:2000],
            "exit_code": proc.returncode,
        })
    except subprocess.TimeoutExpired:
        return json.dumps({"error": "执行超时"})

# ── 工具 3:知识检索(查最新/外部信息)──
def search(query: str) -> str:
    """调用搜索 API 返回前 3 条结果摘要(真实实现接 SerpAPI/必应等)"""
    # 伪实现:真实场景接入搜索服务
    return json.dumps({"results": [f"关于{query}的结果1", "结果2", "结果3"]})

# ── 工具注册表 + 让模型选择并调用 ──
TOOLS = [
    {"type": "function", "function": {
        "name": "calculator",
        "description": "精确数学计算,当问题涉及加减乘除/百分比/利息等计算时使用",
        "parameters": {"type": "object", "properties": {
            "expr": {"type": "string", "description": "数学表达式,如 (1200*0.08)+35"}},
            "required": ["expr"]}}},
    {"type": "function", "function": {
        "name": "run_python",
        "description": "在沙箱执行 Python 代码验证算法/逻辑,当需要验证代码正确性时使用",
        "parameters": {"type": "object", "properties": {
            "code": {"type": "string", "description": "完整的 Python 代码"}},
            "required": ["code"]}}},
    {"type": "function", "function": {
        "name": "search",
        "description": "检索最新外部信息,当需要实时数据/新闻/事实核查时使用",
        "parameters": {"type": "object", "properties": {
            "query": {"type": "string", "description": "搜索关键词"}},
            "required": ["query"]}}},
]

HANDLERS = {"calculator": calculator, "run_python": run_python, "search": search}

def tool_augmented_chat(user_msg: str, max_tool_rounds: int = 3):
    messages = [{"role": "user", "content": user_msg}]
    for _ in range(max_tool_rounds):
        resp = client.chat.completions.create(
            model="deepseek-chat",
            messages=messages,
            tools=TOOLS,
            tool_choice="auto",
            temperature=0.1,       # 工具选择/参数生成要低温度(第24章表二)
        )
        msg = resp.choices[0].message
        if not msg.tool_calls:
            return msg.content      # 模型认为无需工具,直接回答
        messages.append(msg)
        for call in msg.tool_calls:
            # 执行工具 → 把结果塞回上下文 → 继续循环
            try:
                args = json.loads(call.function.arguments)
            except json.JSONDecodeError:
                messages.append({
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": json.dumps({"error": "参数 JSON 非法,请重新生成参数"}),
                })
                continue
            result = HANDLERS[call.function.name](**args)
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": result,
            })
    return "已达最大工具轮数,返回当前结果"

# 使用:模型会自动决定调哪个工具
print(tool_augmented_chat("帮我算一下 1200 元打 8 折再减 35 是多少?"))
print(tool_augmented_chat("写段代码验证冒泡排序是否正确?"))

这个示例展示工具增强的完整闭环:工具定义(Schema)→ 模型选择(tool_choice=auto)→ 执行 → 结果回填 → 循环 → 无工具调用即结束

14.3.2 工具结果的"回填艺术"

工具执行完,结果怎么喂回模型是有讲究的:

  • 结果要简洁:只回填模型需要的字段,别把 2000 行 API 响应全塞回去(上下文污染)。
  • 结果要标注role: "tool" + tool_call_id,模型能对应到是哪次调用。
  • 失败要说明:工具报错时,回填"错误信息 + 可能原因",让模型有机会换参数重试。

14.4 主流框架对照与变体

14.4.1 框架对照

实现方式 特点 适用
OpenAI 兼容 tools 参数(本章主线) 标准函数调用协议,各家模型兼容 通用
MCP(Model Context Protocol) 标准化工具接入,即插即用(第 7 章) 跨系统工具集成
LangChain @tool 装饰器 装饰器定义工具,框架自动生成 Schema 已用 LangChain
ReAct 提示式工具调用 不用原生 tools 协议,靠提示词引导"工具:xxx"格式 老模型/特殊场景

重要提醒:如果工具数量多或跨系统,优先 MCP(第 7 章)------它是工具生态的"USB 接口",避免每个工具写一套适配代码。

14.4.2 变体一:工具选择策略(auto vs 显式)

策略 行为 适用
tool_choice="auto" 模型自己判断是否需要工具 通用默认
tool_choice="required" 强制必须调工具 流水线场景(每步都该调)
tool_choice="none" 禁用工具 纯问答降本
显式指定某工具 固定用某一个 明确场景,防选错

策略建议:入口用 auto,但配合 14.1.3 的"少而精"工具库;工具多的系统,用第 11 章路由先分域,再让每个域内 auto 选择("先路由、后选择"降低选择空间)。

14.4.3 变体二:多工具并行调用

parallel_tool_calls=true 时,模型可以在一次响应里同时发起多个工具调用(比如同时查订单 + 查物流)。收益是省一轮往返,代价是模型可能并发调用有依赖的工具------工具间有依赖时必须关掉并行 (呼应第 12 章并行化)。注意:并非所有 OpenAI 兼容模型/服务端都支持此参数,使用前需确认目标模型的兼容性。

14.4.4 变体三:工具就是"可验证的反思"(Tool-based Verification)

接第 13 章 13.4.4:生成代码后用 run_python 验证,生成 SQL 后真跑一次,生成数据后对账。把"让模型反思"升级为"让工具验证",是最可靠的质量保障(呼应第 13、17 章)。

🛠 解决方案:工具幻觉参数识别与校验

常见问题

  1. "模型生成了不存在的工具名":罕见但会发生。对策:调用前用注册表校验工具名,非法直接返回"工具不存在,请重新选择"。
  2. "工具参数瞎编"(幻觉参数) :比如订单号编造 "order_id": "ABC123"。对策:① temperature 降到 0~0.1;② Schema 写严格 description + 枚举约束;③ 工具内部做参数真实性校验(查无此单 → 返回明确错误);④ 对关键参数让模型先"从原文引用"再填。
  3. "工具一直失败重试,烧钱":无限重试是成本黑洞。对策:单工具重试 2~3 次 + 熔断(连续失败 5 次停用该工具并转人工/换路径,呼应第 24 章 B1)。
  4. "模型不用工具,全靠编" :描述写得不够清楚,模型不知道何时该调。对策:重写描述(14.2.1 正例格式)+ 给 few-shot 示例 + 必要时 tool_choice="required"
  5. "工具被恶意调用":用户提示注入诱导模型调用危险工具。对策:工具封装层白名单 + 敏感操作二次确认 + 注入检测(呼应第 22 章 E2)。

解决方案速查表

现象 根因 解决方案
工具名幻觉 罕见错误 注册表校验 + 非法返回
参数幻觉 温度高/描述弱 低温度 + 严格 Schema + 引用原文
无限重试 无熔断 重试上限 + 连续失败熔断
不用工具 描述不清 重写描述 + few-shot
恶意调用 注入攻击 白名单 + 二次确认

实战提示

  1. 工具 Schema 当"合同"写:description 写清"何时用、何时不用、边界、返回结构",这是投入产出比最高的优化。
  2. 先测选型准确率:用 20~30 条真实问题验证"模型选对工具的比例",<60% 就改描述(14.2.2)。
  3. 工具执行结果必须回填干净:简洁 + 标注 + 失败说明,别污染上下文(14.3.2)。
  4. 封装层不可省略:模型不应直接访问生产系统,中间必须有安全封装层(14.2.3)------除非有明确的安全评估和授权豁免。
相关推荐
东方-教育技术博主21 分钟前
数字人对话接口方案ASR(语音识别)+ LLM(大脑)+ TTS(语音合成)
人工智能·语音识别
超级赛博搬砖工23 分钟前
Reddit养号教程:2026Reddit环境搭建、养号流程与Karma提升完整攻略
人工智能
小鹿的周先生24 分钟前
第 3 篇:Spring AI System Prompt 实战——为 AI 助手设置角色和行为规则
人工智能·spring·prompt
流光D26 分钟前
AI时代,搭建 web 站点并配置 nginx 反向代理流程
运维·服务器·前端·人工智能·nginx·ai·ai编程
hh95028 分钟前
Agent Plan × DeepSeek Harness:基于 DeepSeek 的物理系统数字孪生建模与实时同步:架构设计与实现深度解析
大数据·人工智能·adg·agent plan·adg成都社区
GreatVicent29 分钟前
AI Agent互操作标准化加速:A2A协议正式加入Linux基金会
linux·运维·人工智能·agent·aws·a2a
寻道码路32 分钟前
大模型工程化实战(七):JSON Schema 强约束——模型吐的 json 不合规?schema 校验不通过,重试、兜底、降级一条龙
大模型·agent·rag·json schema·ai工程化·llmops`
Ado柳贯一34 分钟前
Transformer模型详解-CSDN发布版
人工智能·深度学习·transformer
玹外之音35 分钟前
Codex CLI 沙箱实战:安全地让 AI 执行 Shell 命令
人工智能·安全