框架与编排 / LangChain 生态 / Agent 构建 / 构建生产级 Agent
本文转载自 Awesome AI Roadmap,原作者 Polo Li,依 CC BY 4.0 协议共享。

第四章:用 LangChain 构建生产级 Agent 的七步

4.1 什么才算完整 Agent

模型成功调用一次天气工具,只能说明 Demo 跑通了。

进入业务后,问题会一层层出现:

  • 它能做什么、不能做什么
  • 模型选中工具后,参数是否正确?失败或重复调用会不会带来副作用
  • 流程跑得更久时:会话中断后能否恢复?最终结果能否稳定进入业务系统?线上出错后能不能复现

完整 Agent 关注的不只是一次模型调用,而是一条从任务设计、能力接入、运行控制走到测试监控的工程链路。

flowchart LR
    S1["① 明确任务边界"] --> S2["② 选模型与 Tools"] --> S3["③ 约束行为与输出"] --> S4["④ 组装 Agent"] --> S5["⑤ 补齐状态与安全"] --> S6["⑥ 选择调用方式"] --> S7["⑦ 测试与监控"]

    style S1 fill:#e8f0fe
    style S7 fill:#e6f4ea

4.2 第一步:明确任务边界

构建 Agent 的第一步不是选择模型,而是定义任务。

以订单客服 Agent 为例:

项目 内容
允许做 查询订单、解释物流状态
禁止做 自行退款
必须转人工 订单不存在、身份验证失败、用户要求高风险操作
输出要求 答复 + 订单状态 + 是否转人工

把边界说清楚的顺序

  1. 定义目标和允许执行的动作
  2. 划出禁止动作与权限边界
  3. 约定什么算成功、什么算失败、什么时候停止
  4. 哪些情况必须转人工

这些答案会继续决定后面的工具、Prompt 和测试用例。边界一旦模糊,模型就只能猜测什么行为算正确,后面的工程配置也很难补回来。

4.3 第二步:选择模型与 Tools

模型需要支持项目所需的工具调用、结构化输出和上下文长度

模型负责判断和规划;访问数据库、搜索资料、发送消息这类动作应由 Tool 执行。

4.3.1 Tool 为什么要尽量小而清楚

因为模型主要依靠名称、描述和参数 Schema 来判断能不能调用。

  • 一个工具同时负责查询、退款和通知,模型就更容易选错动作;
  • 参数没有类型与范围约束,运行时也很难拦住错误输入。
from langchain.tools import tool

# 装饰器会把函数名、docstring 和类型注解转换成工具说明
@tool
def lookup_order(order_id: str) -> dict[str, str]:
    """根据订单号查询订单状态,只读,不修改订单。"""
    # 真实项目应在这里调用经过身份校验的订单服务
    return {"order_id": order_id, "status": "已发货"}

4.3.2 两层职责必须分开

目标 做什么
给模型看的一层 帮模型选对 职责单一、名称清楚、输入输出容易理解
服务端执行的一层 保证系统做得安全 重新检查身份与权限;有副作用就补幂等和审计

工具描述里的「只读」能帮助模型少走错路,但 Prompt 中写「禁止退款」不能替代退款接口本身的身份校验。

4.4 第三步:约束行为与输出

system_prompt 应说明角色、目标、信息边界、工具规则和失败策略。例如:回答订单状态前必须查询工具,不能猜测数据库中不存在的信息,高风险请求必须转人工。

4.4.1 什么时候需要结构化输出

输出去向 形式
只供人阅读 自然语言即可
进入前端、工单或后续工作流 定义结构化输出
from pydantic import BaseModel, Field

class SupportReply(BaseModel):
    # response_format 会按这三个字段校验 Agent 的最终结果
    answer: str = Field(description="给用户的简洁答复")
    order_status: str | None = Field(default=None, description="订单状态")
    needs_human: bool = Field(description="是否需要转人工")

结构化输出可以约束字段和类型,但不能保证业务事实正确,也不会执行授权。事实仍必须来自可信工具,权限仍必须由业务服务控制。

4.4.2 模型字段不是授权开关

needs_human: bool 只是模型产出的一个字段:模型可以漏填、误填,甚至被提示注入诱导填成 false。因此,不能用它决定敏感操作是否真的执行;同样,不能把「模型没有调用敏感工具」当作授权结论。

把不可协商的策略放在确定性执行路径上。对标准工具调用 Agent,优先用 middleware 在工具执行前中断;工具服务仍要按可信身份再次授权:

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver

@tool
def request_refund(order_id: str) -> str:
    """提交退款申请;实际退款服务必须基于可信身份重新授权并使用幂等键。"""
    return f"退款申请已提交:{order_id}"

agent = create_agent(
    model="<provider>:<your-model-id>",
    tools=[lookup_order, request_refund],
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={
                "lookup_order": False,
                "request_refund": {"allowed_decisions": ["approve", "reject"]},
            }
        )
    ],
    # 示例使用内存;生产环境必须使用持久化 checkpointer。
    checkpointer=InMemorySaver(),
)

金额阈值、租户隔离、职责分离等业务规则若不属于一次 Tool Call,则应在调用 Agent 前的确定性路由节点,或在 LangGraph 节点与边中执行。中断和服务端授权才是控制点;模型字段只是供界面和后续流程参考的数据。

4.5 第四步:组装 Agent

from langchain.agents import create_agent

# 将模型、工具、行为约束和输出 Schema 组装成 Agent
agent = create_agent(
    model="<provider>:<your-model-id>",
    tools=[lookup_order],
    system_prompt=(
        "你是订单客服。回答订单状态前必须调用查询工具;"
        "不得猜测,无法处理时设置转人工。"
    ),
    response_format=SupportReply,
)

# messages 是 Agent State 的默认输入字段
result = agent.invoke({
    "messages": [{"role": "user", "content": "订单 A100 到哪了?"}]
})

# 结构化结果已经通过 SupportReply 的字段校验
reply: SupportReply = result["structured_response"]

底层执行流程(详见 第三章):

flowchart TB
    U["用户消息"] --> M["模型判断"]
    M --> D{"有工具调用?"}
    D -->|否| F["最终结果"]
    D -->|是| T["LangGraph 运行时执行工具"]
    T --> TM["ToolMessage 写回消息状态"]
    TM --> M

    style F fill:#e6f4ea

旧资料中的 create_tool_calling_agentAgentExecutor 仍可能出现在存量项目中,但新项目应优先使用 create_agent

4.6 第五步:补齐状态与安全

4.6.1 两种持久化不能混为一谈

机制 作用 关键配置
Checkpointer 保存同一线程的消息和执行状态,中断后可恢复 稳定传入 thread_id
Store 保存跨线程的用户偏好或长期事实 用 namespace + key 隔离租户与用户

二者都能落盘,但职责不同,不能混用。

4.6.2 横切逻辑写在 Middleware

重试、摘要、权限和审批往往会同时影响多个模型或工具调用。散落在每个节点里,规则很快就会重复。

场景 Middleware 位置
模型或只读工具临时失败 调用周围做有上限的重试
上下文过长 模型调用前压缩历史
用户权限变化 动态隐藏工具
敏感动作 工具执行前暂停等待审批
模型输出后 补充格式或安全检查

付款、发邮件、删除数据等工具必须具备幂等、最小权限和审计能力——自动重试不能导致重复扣款或重复发信。

4.7 第六步:选择调用方式

调用方式 适用场景
invoke 短任务、后台任务、等待最终结果
异步调用 并发 I/O、异步 Web 服务
stream 长任务,需要展示 Token、步骤或工具进度

流式输出改善的是等待体验,不会自动缩短工具执行时间。超时、取消、并发限制和缓存仍要单独设计。

4.8 第七步:测试与监控

Agent 输出具有概率性,所以测试不能只比较最终文本。

flowchart TB
    L1["第一层:测 Tool
正常输入、非法参数、权限错误、超时、幂等性
Tool 是相对确定的业务代码,应优先做到稳定"] L2["第二层:测 Agent 轨迹
是否选对工具、参数是否正确
是否越权调用、结构化输出是否符合 Schema"] L3["第三层:端到端评测 + 线上监控
典型问题/边界案例/历史故障整理成数据集
对比模型、Prompt、Tool 版本
Trace 观察延迟、Token、失败率、人工转接率"] L1 --> L2 --> L3 style L1 fill:#e6f4ea style L3 fill:#fff3cd

如何把 Trace、生产反馈、Dataset、离线实验和发布门禁连成闭环,见 LangSmith 生产质量闭环

4.8.1 上线前检查清单

  • [ ] 任务和停止条件是否明确
  • [ ] Tool 是否职责单一,并在服务端校验权限
  • [ ] 有副作用的操作是否具备幂等和审批
  • [ ] Checkpointer 与 Store 是否使用持久化实现并做好用户隔离
  • [ ] 是否设置超时、重试上限、并发和成本预算
  • [ ] 是否覆盖工具、轨迹和端到端评测
  • [ ] 是否能够追踪一次失败运行的完整调用链

4.9 常见错误

4.9.1 先选模型再想任务

边界模糊,后面所有工程配置都补不回来。

4.9.2 把一堆能力塞进一个大工具

模型靠名称、描述、Schema 选工具,职责越杂越容易选错

4.9.3 用 Prompt 代替权限校验

Prompt 里写「禁止退款」拦不住任何东西,权限必须在服务端。

4.9.4 以为结构化输出保证事实正确

它只约束字段和类型,事实要来自可信工具。

4.9.5 把模型字段当授权结论

needs_human=false、风险分低或未选中敏感工具,都不是执行权限。敏感 Tool Call 要由确定性 middleware/interrupt() 暂停,业务服务还要基于可信身份授权。

4.9.6 把 Checkpointer 和 Store 混用

一个是线程内状态恢复,一个是跨线程长期数据,用途完全不同

4.9.7 给有副作用的工具配自动重试

没有幂等就会重复扣款、重复发信。

4.9.8 以为流式能加快任务

它只改善等待体验,超时、取消、并发限制仍要单独设计。

4.9.9 只对比最终文本做测试

必须测工具轨迹——选对工具、参数正确、无越权,这些用最终文本看不出来。

4.9.10 用内存版 Checkpointer 上生产

进程重启状态全丢,必须换持久化实现

4.10 本章总结

  1. 完整 Agent 是一条工程链路,不是一次成功的工具调用;
  2. 第一步定边界:允许动作、禁止动作、成功/失败/停止条件、转人工条件;
  3. 模型负责判断,Tool 负责动作;模型需支持工具调用、结构化输出和足够上下文;
  4. Tool 分两层:给模型看的一层帮它选对,服务端一层保证做得安全;
  5. system_prompt 约束行为,response_format 固定业务输出,但都不保证事实正确或授权;敏感动作由确定性 middleware/interrupt() 与服务端授权控制;
  6. create_agent 是 v1 的标准入口,底层由 LangGraph 管理模型与工具的循环;
  7. Checkpointer 管线程内恢复,Store 管跨线程长期数据,二者不可混淆;
  8. Middleware 承接横切逻辑:重试、摘要、权限、审批、安全校验;
  9. 有副作用的工具必须幂等、最小权限、可审计
  10. 调用方式按产品形态选:invoke / 异步 / stream,流式不缩短执行时间;
  11. 三层测试:Tool → 轨迹 → 端到端与线上 Trace。

create_agent 跑通只是起点;要让 Agent 落到业务里,还得沿着边界、能力、约束、组装、状态、交互和验证七步,把概率性的模型循环收束成可测试、可观测的系统。

参考资料