芥末
发布于 2026-01-06 / 0 阅读
0
0

生产级 Agent 开发:核心组件、ReAct 循环与稳定性设计

Agent(智能体)不是“给大模型接几个工具”这么简单。

普通 Chatbot 更像一个问答系统,用户输入问题,模型直接生成回答;Agent 则要完成一个任务,它需要理解目标、拆解步骤、选择工具、观察结果,并根据中间状态不断调整执行路径。

两者的差别可以简单理解为:

类型核心能力典型行为
Chatbot生成回答解释概念、回答问题、总结文本
Agent执行任务查数据、调用 API、处理文件、分步骤完成目标

真正能落地的 Agent,至少要把四件事设计清楚:LLM、规划、记忆和工具。

flowchart TB
    User[用户请求] --> Agent[Agent 编排逻辑]

    Agent --> LLM[LLM 大语言模型]
    Agent --> Planning[规划模块]
    Agent --> Memory[记忆系统]
    Agent --> Tools[工具系统]

    Planning --> LLM
    Memory --> LLM
    LLM --> Tools
    Tools --> Observation[观察结果]
    Observation --> Agent
    Agent --> User

Agent 的四个核心组件

LLM(Large Language Model,大语言模型)负责理解和推理,规划模块负责拆任务,记忆系统负责保存上下文和长期信息,工具系统负责让 Agent 真正操作外部世界。

缺少其中任何一块,Agent 都会退化:

  • 没有规划,只能做单轮问答或简单工具调用;
  • 没有记忆,多轮任务容易丢状态;
  • 没有工具,只能“说”,不能“做”;
  • 没有稳定的 LLM,规划和工具调用都会变得不可控。
组件作用工程关注点
LLM理解任务、推理决策、生成输出模型能力、工具调用稳定性、温度参数
规划把复杂目标拆成可执行步骤静态规划、动态规划、最大迭代次数
记忆保存历史对话、任务状态、用户偏好短期记忆、长期记忆、上下文压缩
工具执行搜索、计算、API 调用、文件操作工具描述、参数校验、错误处理

1. LLM:Agent 的决策核心

LLM 决定 Agent 能不能理解任务、能不能选对工具、能不能根据工具返回结果继续推理。

在工程实践里,模型选择会直接影响稳定性。支持稳定 Tool Calling 的模型更适合做 Agent,例如 GPT-4o、Claude 3.5 Sonnet 这类模型在工具调用格式遵循方面表现更稳。

Temperature 参数也要控制。Agent 不是写诗,执行任务时更需要确定性。一般可以把 Temperature 设置在 0 ~ 0.3,降低模型随机发挥导致的工具选择漂移。

agent_llm_config = {
    "model": "gpt-4o",
    "temperature": 0.1,
    "max_tokens": 4096,
}

2. 规划模块:Agent 和 Chatbot 的分界线

规划模块负责把“帮我完成某件事”拆成一组可以执行的动作。

常见规划方式有两类。

规划方式代表方法特点适合场景
静态规划CoT(Chain-of-Thought,思维链)、ToT(Tree-of-Thought,思维树)执行前生成完整计划步骤明确、变化少的任务
动态规划ReAct、Reflexion边执行边观察,边调整计划外部环境不确定、工具结果会影响后续步骤的任务

这里的“动态规划”不是算法课里的 Dynamic Programming,而是指 Agent 在执行过程中动态修正计划。

例如用户要求“查一下北京今天的天气,并判断是否适合跑步”,Agent 不应该凭常识回答,而应该先调用天气工具,再根据温度、空气质量、降水情况判断。

3. 记忆系统:让 Agent 不丢上下文

记忆分为短期记忆和长期记忆。

短期记忆保存当前会话里的上下文,例如用户刚刚说过的约束、工具调用结果、当前任务进度。它通常依赖模型的 Context Window,也可以把状态存在 Redis 里。

长期记忆保存跨会话的信息,例如用户偏好、历史任务结果、企业知识库内容。长期记忆常用向量数据库、MongoDB 向量索引、Redis 向量检索或知识图谱实现。

flowchart LR
    User[用户输入] --> Short[短期记忆<br/>当前会话状态]
    Short --> LLM[LLM 推理]
    Long[长期记忆<br/>用户偏好/知识库] --> Retriever[检索模块]
    Retriever --> LLM
    LLM --> Response[回答或行动]
    Response --> Short
    Response --> Long

短期记忆不能无限增长。多轮对话后,如果把全部历史都塞进上下文,成本会上升,关键信息也会被无关内容稀释。更稳的做法是:

  • 保留最近几轮对话;
  • 对较早历史做摘要;
  • 把长期价值的信息写入向量库;
  • 每次任务只检索相关片段。

4. 工具系统:让 Agent 能执行真实操作

工具是 Agent 和外部世界交互的接口。搜索、查天气、调用企业系统、执行代码、读写文件,本质上都可以封装成工具。

工具定义要清楚,尤其是名称、描述和参数结构。描述模糊会导致模型选错工具,参数约束不清会导致调用失败。

{
  "name": "weather_api",
  "description": "查询指定城市的实时天气,包括温度、天气状况、空气质量和降水概率。只用于天气查询,不用于查询城市介绍。",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,例如北京、上海、深圳"
      }
    },
    "required": ["city"]
  }
}

JSON(JavaScript Object Notation,一种结构化数据格式)Schema(用于描述 JSON 数据结构的规范)可以限制工具入参,减少模型生成无效参数的概率。

工具数量也要控制。工具总池可以很大,但单次任务暴露给 Agent 的工具最好控制在 8 ~ 10 个以内。工具太多时,模型容易在语义相近的工具之间摇摆。

更好的结构是加一层 Router:

flowchart LR
    Request[用户请求] --> Router[Router 意图路由]
    Router --> SearchTools[搜索类工具集]
    Router --> DataTools[数据查询工具集]
    Router --> FileTools[文件处理工具集]
    Router --> BizTools[业务系统工具集]

Router 先判断任务类型,再把对应工具集交给 Agent。这样既能保留完整工具能力,又不会让模型一次面对过多选择。

ReAct:最常见的 Agent 执行循环

ReAct 来自 Reason + Act,核心思想是让模型在“推理”和“行动”之间循环。

一个标准 ReAct 循环包含四个部分:

Thought: 当前要解决什么?已经知道什么?还缺什么?
Action: 要调用哪个工具?
Action Input: 工具参数是什么?
Observation: 工具返回了什么?

Agent 会不断重复这个过程,直到模型判断任务已经完成,然后输出最终答案。

flowchart TD
    Start[用户任务] --> Thought[Thought<br/>分析当前状态]
    Thought --> Action[Action<br/>选择工具]
    Action --> Input[Action Input<br/>生成参数]
    Input --> Tool[调用工具]
    Tool --> Observation[Observation<br/>读取工具结果]
    Observation --> Done{任务完成?}
    Done -- 否 --> Thought
    Done -- 是 --> Final[Final Answer]

用天气查询举例:

用户:北京今天天气怎么样?适合户外运动吗?

Thought: 用户需要北京天气,并希望判断是否适合户外运动。我需要先查询实时天气。
Action: weather_api
Action Input: {"city": "北京"}
Observation: {"temperature": 25, "weather": "晴", "aqi": 45, "rain_probability": 0.05}

Thought: 温度 25°C,晴天,AQI 45,降水概率很低,空气质量较好,适合户外运动。
Final Answer: 北京今天 25°C,晴天,空气质量较好,降水概率低,适合户外运动。

ReAct 的价值在于,它不是让模型一次性猜完整答案,而是把外部信息纳入推理过程。

方法行为缺点
纯 CoT只推理,不调用工具无法获取实时信息
纯工具调用直接调用工具,缺少推理容易选错工具或误用结果
ReAct推理、行动、观察交替进行需要控制循环次数和异常处理

一个简化版 ReAct 执行器可以这样写:

MAX_ITERATIONS = 10

def run_agent(user_task, llm, tool_registry):
    scratchpad = []

    for step in range(MAX_ITERATIONS):
        decision = llm.plan(
            task=user_task,
            history=scratchpad,
            available_tools=tool_registry.schemas()
        )

        if decision["type"] == "final":
            return decision["answer"]

        tool_name = decision["action"]
        tool_input = decision["action_input"]

        tool = tool_registry.get(tool_name)
        if tool is None:
            observation = {
                "success": False,
                "error": f"unknown tool: {tool_name}"
            }
        else:
            try:
                result = tool.call(tool_input, timeout=30)
                observation = {
                    "success": True,
                    "data": result
                }
            except TimeoutError:
                observation = {
                    "success": False,
                    "error": "tool timeout"
                }
            except Exception as e:
                observation = {
                    "success": False,
                    "error": str(e)
                }

        scratchpad.append({
            "thought": decision.get("thought"),
            "action": tool_name,
            "action_input": tool_input,
            "observation": observation
        })

    return "暂时无法完成该任务,请稍后重试或缩小任务范围。"

这里有三个关键点:

  • MAX_ITERATIONS 防止 Agent 无限循环;
  • 工具返回必须区分 successerror
  • 异常不能直接当作正常结果塞回模型。

生产环境里,ReAct 还需要加早停策略。例如连续两次选择同一个工具、输入参数也完全相同,并且结果没有变化,就可以判定进入无效循环。

Demo 能跑,不代表生产能稳

Agent 在演示环境里经常表现很好,因为演示通常走的是 happy path:输入清晰,工具正常,网络稳定,任务轮数少。

生产环境完全不同。用户输入可能不完整,API(Application Programming Interface,应用程序编程接口)可能超时,工具可能返回脏数据,多轮对话会让上下文越来越长。Agent 的不稳定,通常来自四类问题。

1. 错误传播:一步错,后面都偏

Agent 是多步执行系统。某一步工具返回错误,如果没有显式标记失败,模型可能会把错误文本当作正常观察结果继续推理。

错误传播的典型链路是:

flowchart LR
    A[工具调用失败] --> B[错误信息被当成正常结果]
    B --> C[LLM 基于错误结果继续推理]
    C --> D[选择错误工具或生成错误答案]
    D --> E[任务整体失败]

解决方式是让所有工具返回统一结构:

{
  "success": false,
  "error_code": "TIMEOUT",
  "error_message": "weather_api timeout after 30s",
  "retryable": true
}

Agent 看到 success: false 后,要进入错误处理分支,而不是继续按正常流程推理。

关键步骤还要加校验:

校验点示例
输出格式JSON 字段是否完整
参数合法性城市名、日期、金额是否符合业务规则
业务逻辑库存不能为负,退款金额不能超过订单金额
权限用户是否有权调用该工具或读取该数据

2. 工具选择漂移:工具越多,越容易选错

工具选择漂移指的是 Agent 在执行过程中逐渐偏离原始目标,或者在多个相似工具之间选错。

例如同时提供这些工具:

  • search_user_order
  • search_order_log
  • query_order_status
  • get_order_detail

如果 description 写得都很像,模型很可能选错。

解决方式包括:

  • 单次任务只暴露必要工具;
  • 工具命名保持明确,不要语义重叠;
  • description 写清“适用场景”和“不适用场景”;
  • 用 Router 先分流,再提供小工具集。

工具 description 可以这样写:

get_order_detail:
根据订单 ID 查询订单的基础信息,包括商品、金额、收货地址和当前状态。
不用于查询操作日志,不用于查询退款进度。

“不用于什么”很重要,它能减少模型在相近工具之间误选。

3. 上下文爆炸:历史越多,关键信息越淡

多轮 Agent 很容易遇到 Context Explosion,也就是上下文爆炸。问题不是只有“超出模型上下文长度”,还包括“重要信息被大量无关内容稀释”。

可用三层策略处理:

策略做法适合内容
滑动窗口保留最近 N 轮对话最新约束、当前任务状态
对话摘要定期压缩旧历史已确认的需求、阶段性结论
分层记忆热信息进上下文,冷信息进向量库用户偏好、历史任务、知识库

不要把向量检索当成万能记忆。长期记忆写入前要判断是否值得保存,检索后也要做相关性过滤,否则会把噪声重新带回上下文。

4. 缺少降级:外部依赖一挂,Agent 就停

Agent 依赖模型、工具、数据库、网络和第三方服务。任何一环不稳定,都会影响整体结果。

生产级 Agent 至少要有三类降级设计:

机制作用示例
超时避免单个工具阻塞整个任务单次工具调用 30 秒超时
熔断避免持续调用已故障服务连续失败 3 次后临时禁用工具
兜底在无法完成时给出可解释结果告知用户当前能力不可用,并提供替代路径

非幂等操作不能随便重试。例如扣款、下单、发送邮件这类操作,重试可能造成重复执行。重试前要确认操作是否幂等,必要时引入请求 ID 做去重。

Agent 框架怎么选

LangGraph、CrewAI、AutoGen 的定位不一样,不应该只按热度选。

维度LangGraphCrewAIAutoGen
设计理念图结构工作流角色协作团队对话式协作
学习曲线较陡较平缓中等
状态控制中等中等
适合场景复杂流程、状态机、生产编排快速原型、多角色任务模拟代码生成、迭代式任务
生产适配更适合精细控制适合较快验证业务想法需要关注迁移和维护状态

选型可以按任务特征判断:

flowchart TD
    A[Agent 项目需求] --> B{需要精细控制状态和流程?}
    B -- 是 --> LG[LangGraph]
    B -- 否 --> C{需要快速搭多角色协作原型?}
    C -- 是 --> Crew[CrewAI]
    C -- 否 --> D{主要做代码生成和迭代修复?}
    D -- 是 --> Auto[AutoGen]
    D -- 否 --> E[选择更轻量的自定义 ReAct 或函数调用流程]

更具体一些:

  • 复杂企业流程、需要断点续传、状态回放、分支控制时,LangGraph 更合适;
  • 需要快速定义“研究员、分析师、执行员”这类角色协作时,CrewAI 上手更快;
  • 代码生成、执行、修复循环比较重时,AutoGen 的对话式协作模式有优势。

如果团队已经在使用 LangChain 技术栈,LangGraph 的集成成本会更低。对于 AutoGen,还要关注 Microsoft 相关 Agent Framework 的迁移方向;如果是新项目,要把后续维护和迁移成本算进去。

不同框架的记忆机制也有差异:

框架记忆实现思路
LangGraph通过 Checkpointing 保存状态,可接 Redis、MongoDB 等存储
CrewAI围绕角色和任务组织记忆,支持 RAG(Retrieval-Augmented Generation,检索增强生成)
AutoGen以多轮对话历史作为主要记忆载体

生产级 Agent 的分层架构

企业级智能助手不能只是一段调用模型的脚本。它至少需要接入层、编排层、能力层和基础设施层。

flowchart TB
    subgraph Access[接入层]
        Web[Web]
        API[API]
        IM[企业 IM]
        Auth[身份与权限]
        RateLimit[限流]
    end

    subgraph Orchestration[编排层]
        Router[Router 意图路由]
        Orchestrator[Orchestrator 执行编排]
        State[State Manager 状态管理]
    end

    subgraph Capability[能力层]
        ToolPool[工具池]
        Memory[短期记忆 + 长期记忆]
        ModelPool[主模型 + 备用模型]
    end

    subgraph Infra[基础设施层]
        Tracing[Tracing 全链路追踪]
        Eval[评估系统]
        Monitor[监控告警]
        Storage[Redis / MongoDB / 向量库]
    end

    Access --> Orchestration
    Orchestration --> Capability
    Capability --> Infra
    Infra --> Orchestration

接入层:处理请求进入系统之前的问题

接入层负责多渠道入口和基础安全控制:

  • Web、API、企业 IM(Instant Messaging,即时通讯)等多入口接入;
  • Session ID 管理;
  • 用户身份识别;
  • 权限校验;
  • 敏感内容过滤;
  • 请求限流。

权限校验不能放到工具内部才做。Agent 在规划阶段就应该知道用户能调用哪些工具、能读取哪些数据,否则会生成无法执行的计划。

编排层:生产级 Agent 的核心

编排层决定任务怎么走。

模块职责
Router判断任务类型,分配 Agent 或工具集
Orchestrator控制执行流程,包括分支、循环、并行、重试
State Manager保存任务状态,支持中断恢复和问题排查

LangGraph 适合放在这一层,因为它用图结构表达流程,节点、边、状态都比较清晰。对于需要审批、回滚、人工介入的企业流程,图结构比单纯 prompt 串联更容易维护。

能力层:模型、工具和记忆的组合

能力层提供 Agent 真正能调用的能力:

  • 工具池按领域分组,每组控制在 8 ~ 10 个工具以内;
  • 短期记忆可以放 Redis,并设置 TTL(Time To Live,存活时间),例如 24 小时;
  • 长期记忆可以使用 MongoDB 向量索引、专用向量数据库或知识图谱;
  • 模型池要支持主备切换,避免单一模型服务故障导致整体不可用。

常见配置可以参考:

决策点可选方案
主模型GPT-4o / Claude 3.5 Sonnet
编排框架LangGraph
短期记忆Redis,TTL 24h
长期记忆MongoDB + 向量索引,或专用向量数据库
可观测性LangSmith / Langfuse / OpenTelemetry
评估自动评估覆盖主体场景,人工抽检高风险场景

基础设施层:没有可观测性就不要上线

Agent 的问题很难只靠最终答案排查。一次任务里可能包含多次模型调用、多次工具调用、检索、重试和降级。如果没有 Tracing(全链路追踪),线上出错时很难定位是哪一步偏了。

至少要记录这些信息:

记录项作用
用户请求还原任务输入
Router 决策判断任务是否分错类
Prompt 和模型输出排查模型是否误解
工具调用参数判断工具输入是否正确
工具返回结果判断外部依赖是否异常
每一步耗时找出性能瓶颈
Token 消耗控制成本
最终答案和评分做质量评估

评估系统也要提前设计。常见做法是 LLM-as-Judge(用大语言模型做评审)加人工抽检。自动评估适合覆盖格式、完整性、事实一致性等高频场景;人工抽检适合高风险业务,例如财务、法务、医疗和关键企业操作。

稳定性 Checklist

生产级 Agent 至少要满足这些约束:

项目推荐配置
单次工具调用超时30 秒左右,根据业务调整
最大 ReAct 迭代次数10 ~ 15
工具调用重试最多 2 次,非幂等操作不自动重试
熔断阈值连续失败 3 次后触发
单次对话 Token 预算8k ~ 16k,按模型和成本调整
工具返回格式必须包含 success / error
上下文管理滑动窗口 + 摘要 + 长期记忆
可观测性每次模型调用和工具调用都可追踪
降级响应明确告诉用户当前无法完成什么,并给替代方案

还应该专门构造失败用例来测试 Agent:

1. 工具超时
2. 工具返回空数据
3. 工具返回格式错误
4. 用户输入缺少必要参数
5. 用户要求越权操作
6. 上下文超过预算
7. 多个工具描述相似
8. 模型连续选择同一个无效工具

只测正常路径没有意义。Agent 是否可靠,主要看它在异常路径里能不能停得住、说得清、可恢复。

判断 Agent 水平,看这几件事

会调用框架只能说明能搭 Demo,不能说明能做生产系统。真正的 Agent 开发能力,体现在几个问题上:

  • 能不能说清 LLM、规划、记忆、工具各自负责什么;
  • 能不能解释 ReAct 的 Thought、Action、Observation 循环;
  • 能不能处理工具失败、上下文爆炸、工具漂移和降级;
  • 能不能把工具池、状态管理、Tracing 和评估系统设计完整;
  • 能不能根据业务复杂度选择 LangGraph、CrewAI、AutoGen 或自定义流程。

Agent 的核心不是“用了哪个框架”,而是让一个不确定的大语言模型,在受控的流程、清晰的工具、可追踪的状态和可恢复的异常处理里完成任务。框架会变,工程约束不会变。


评论