Appearance
04. Tools、工作流与 Agent 不要混为一谈
只要代码里出现 StructuredTool,很多项目就会把自己称为 Agent。这个说法过于宽泛,会让人忽略最重要的问题:到底是谁在决定下一步动作?
Tool、工作流和 Agent 是三个不同层次。
三个概念,一张边界表
| 形态 | 谁决定下一步 | 是否循环 | 可预测性 | 适用场景 | 主要风险 |
|---|---|---|---|---|---|
| Tool | 调用它的代码或模型 | Tool 自身不循环 | 取决于调用方 | 封装业务能力 | 参数越权、副作用 |
| 确定性工作流 | 程序的边和条件 | 按代码定义 | 高 | 客服、审批、交易 | 规则遗漏 |
| 模型选工具 | 模型从候选工具中选择 | 可以不循环 | 中 | 意图较开放的助手 | 选错工具或参数 |
| 自主 Agent | 模型反复观察并决定动作 | 通常循环 | 较低 | 研究、排障、探索 | 失控循环、成本和副作用 |
这四种形态没有高低之分。越靠后,自由度越高,同时越需要权限、预算、停止条件和评测。
Tool 是有契约的函数
普通 Python 函数只告诉程序员怎样调用。LangChain Tool 还向模型或编排器提供名称、描述和参数结构。
订单查询工具可以这样注册:
python
from langchain_core.tools import StructuredTool
lookup_order = StructuredTool.from_function(
func=service._lookup_order_value,
name="lookup_order",
description="根据订单号查询订单状态、商品、金额和异常信息",
)StructuredTool 的价值在于统一契约:
name是稳定标识;description告诉调用方什么时候使用;- 函数签名形成参数 Schema;
invoke()提供统一执行入口。
Tool 本身不会决定何时执行。把锤子放进工具箱,不代表工具箱会自己开始修东西。
确定性工作流如何调用工具
客服工作流可以使用确定性分派:
python
calls = []
if state["intent"] in {"order_status", "escalation"} and state.get("order_no"):
calls.append(service.lookup_order(state["order_no"]))
if state["intent"] == "policy":
calls.append(service.search_policy(state["message"]))
if state["intent"] == "escalation":
calls.append(service.create_ticket(state.get("order_no"), state["message"]))这段代码调用了与 Tool 对应的业务函数,但下一步是 if 分支决定的。它的优点是:
- 哪种意图能调用哪个函数一目了然;
- 不会因为模型一句话同时执行多个高风险动作;
- 分支容易写固定测试;
- 业务人员可以审查规则。
模型选工具是什么
模型选工具时,程序把一组 Tool 的描述交给支持工具调用的模型。模型响应不一定是最终文本,也可能是一项结构化调用请求:
text
tool: lookup_order
arguments: {"order_no": "HB20260722001"}应用仍然需要:
- 验证工具是否在白名单中;
- 验证参数类型和业务权限;
- 真正执行函数;
- 把结果返回模型或工作流;
- 记录调用和处理异常。
所以“模型生成了工具调用”不等于“模型可以直接操作系统”。执行权仍在应用代码手里。
模型选工具适合意图空间较开放、工具副作用较低的场景。例如内部知识助手可以在多个只读搜索工具之间选择。
自主 Agent 又多了什么
自主 Agent 在模型选工具的基础上增加循环:
text
观察当前状态
↓
模型决定动作
↓
执行工具
↓
把结果加入状态
↓
继续观察,直到完成或触发停止条件它不仅选择一次工具,还会根据工具结果决定下一步。自主性主要来自这个反馈循环,而不是类名里有没有 Agent。
适合 Agent 的任务通常具有这些特征:
- 无法提前写死完整步骤;
- 需要根据中间发现调整方向;
- 工具大多只读或副作用可撤销;
- 结果可以被程序或人工验证;
- 失败成本可控。
例如调查一组日志、搜索多份资料并形成带证据的初稿,比自动退款或修改订单更适合 Agent。
为什么客服场景不应默认开放自主循环
客服场景涉及订单、隐私、投诉和赔偿。这里最重要的不是“尽可能自主”,而是行为可预测。
假设系统有三个业务工具:
lookup_order
只读查询订单状态。缺少合法订单号时不调用,而是要求用户补充信息。
search_policy
只读查询退款、退货和配送政策。运行轨迹中的原始客户消息会被脱敏,避免敏感文本进入教学面板。
create_ticket
为破损、赔偿或隐私问题创建人工工单。系统只承诺“已经转交”,不会自动决定赔偿金额。
这个设计刻意使用确定性路由:分类结果可以来自模型,但权限边界不能依赖模型自觉遵守 Prompt。
安全与停止条件
如果确实需要自主 Agent,至少回答下面这些问题。
权限
- Agent 能看到哪些数据?
- 哪些工具只读,哪些会产生副作用?
- 是否按用户身份再次鉴权?
参数
- Tool 参数是否经过 Schema 校验?
- 订单号、SQL、文件路径是否有白名单?
- 模型生成的文本是否被当成不可信输入?
停止条件
- 最多允许多少轮?
- 最大 token、时间和费用是多少?
- 连续重复同一工具时是否停止?
- 哪些动作必须等待人工批准?
可观察性
- 是否记录每次模型决策和工具调用?
- 失败能否定位到具体节点?
- 日志是否脱敏?
没有停止条件的循环,不是自主,而是失控风险。
动手练习
- 为政策查询、缺订单号查询和破损投诉列出各自允许调用的工具。
- 请求“帮我查订单”但不提供订单号,确认系统不会猜测参数。
- 请求“订单 HB20260722003 到货破损,要求赔偿”,观察
lookup_order和create_ticket的调用顺序。 - 为一个低风险只读场景设计三项 Tool,并写出 Agent 的最大轮数、超时和人工审批条件。
小结
Tool 是带契约的函数;工作流由程序决定路径;模型选工具把一次选择交给模型;自主 Agent 再增加反馈循环。判断系统风险时,应关注决策权、循环和副作用,而不是它用了哪个类名。
最后一篇,我们会把视角从“能运行”提升到“能上线”,看看 SQL 护栏、轨迹、隐私和评测如何共同构成生产边界。
上一篇:完整 RAG · 返回目录 · 下一篇:生产化实践
