Skip to content

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"}

应用仍然需要:

  1. 验证工具是否在白名单中;
  2. 验证参数类型和业务权限;
  3. 真正执行函数;
  4. 把结果返回模型或工作流;
  5. 记录调用和处理异常。

所以“模型生成了工具调用”不等于“模型可以直接操作系统”。执行权仍在应用代码手里。

模型选工具适合意图空间较开放、工具副作用较低的场景。例如内部知识助手可以在多个只读搜索工具之间选择。

自主 Agent 又多了什么

自主 Agent 在模型选工具的基础上增加循环:

text
观察当前状态

模型决定动作

执行工具

把结果加入状态

继续观察,直到完成或触发停止条件

它不仅选择一次工具,还会根据工具结果决定下一步。自主性主要来自这个反馈循环,而不是类名里有没有 Agent

适合 Agent 的任务通常具有这些特征:

  • 无法提前写死完整步骤;
  • 需要根据中间发现调整方向;
  • 工具大多只读或副作用可撤销;
  • 结果可以被程序或人工验证;
  • 失败成本可控。

例如调查一组日志、搜索多份资料并形成带证据的初稿,比自动退款或修改订单更适合 Agent。

为什么客服场景不应默认开放自主循环

客服场景涉及订单、隐私、投诉和赔偿。这里最重要的不是“尽可能自主”,而是行为可预测。

假设系统有三个业务工具:

lookup_order

只读查询订单状态。缺少合法订单号时不调用,而是要求用户补充信息。

search_policy

只读查询退款、退货和配送政策。运行轨迹中的原始客户消息会被脱敏,避免敏感文本进入教学面板。

create_ticket

为破损、赔偿或隐私问题创建人工工单。系统只承诺“已经转交”,不会自动决定赔偿金额。

这个设计刻意使用确定性路由:分类结果可以来自模型,但权限边界不能依赖模型自觉遵守 Prompt。

安全与停止条件

如果确实需要自主 Agent,至少回答下面这些问题。

权限

  • Agent 能看到哪些数据?
  • 哪些工具只读,哪些会产生副作用?
  • 是否按用户身份再次鉴权?

参数

  • Tool 参数是否经过 Schema 校验?
  • 订单号、SQL、文件路径是否有白名单?
  • 模型生成的文本是否被当成不可信输入?

停止条件

  • 最多允许多少轮?
  • 最大 token、时间和费用是多少?
  • 连续重复同一工具时是否停止?
  • 哪些动作必须等待人工批准?

可观察性

  • 是否记录每次模型决策和工具调用?
  • 失败能否定位到具体节点?
  • 日志是否脱敏?

没有停止条件的循环,不是自主,而是失控风险。

动手练习

  1. 为政策查询、缺订单号查询和破损投诉列出各自允许调用的工具。
  2. 请求“帮我查订单”但不提供订单号,确认系统不会猜测参数。
  3. 请求“订单 HB20260722003 到货破损,要求赔偿”,观察 lookup_ordercreate_ticket 的调用顺序。
  4. 为一个低风险只读场景设计三项 Tool,并写出 Agent 的最大轮数、超时和人工审批条件。

小结

Tool 是带契约的函数;工作流由程序决定路径;模型选工具把一次选择交给模型;自主 Agent 再增加反馈循环。判断系统风险时,应关注决策权、循环和副作用,而不是它用了哪个类名。

最后一篇,我们会把视角从“能运行”提升到“能上线”,看看 SQL 护栏、轨迹、隐私和评测如何共同构成生产边界。

上一篇:完整 RAG · 返回目录 · 下一篇:生产化实践

别急,先让缓存热一下。