AI Agent 完全指南:从 ReAct 到 Multi-Agent 的工程架构

Agent 是 2024 年最热也最被滥用的词。"AI Agent""LangGraph Agent""自主 Agent""Multi-Agent" —— 听起来花样很多,但拆开看,所有 Agent 系统的核心都是同一个循环:感知 → 决策 → 行动 → 观察 → 再决策。这篇文章把 Agent 从最基础的 ReAct 模式讲到 Plan-and-Execute、Multi-Agent、Reflective Agent,讲清楚不同模式适合什么场景,以及生产级 Agent 必须解决的工程问题。

什么是 Agent

简化定义:能自主决策"下一步做什么"的 AI 程序。区别于:

  • 普通 LLM 对话:用户问一句,LLM 答一句,没有"做事"。
  • Workflow:固定流程,A → B → C,LLM 只是在某些节点参与。
  • Agent:LLM 决定整个流程,根据上下文自主选择工具、决定下一步、判断何时停止。

Anthropic 在《Building Effective Agents》里给的判断标准最实用:"当任务的解决路径不能预先确定时,才需要 Agent;路径确定时,用 Workflow 更可靠"。

最简单的 Agent:ReAct 模式

ReAct = Reasoning + Acting。Google 2022 年提出。基本流程:

循环:
    Thought:LLM 思考"接下来该做什么"
    Action:LLM 选择一个工具调用
    Observation:程序执行工具,把结果给 LLM
    (再循环)
    Final Answer:LLM 决定任务完成,给出最终答案
def react_agent(question, tools, max_steps=10):
    messages = [
        {"role": "system", "content": REACT_SYSTEM_PROMPT},
        {"role": "user", "content": question}
    ]

    for step in range(max_steps):
        response = llm(messages, tools=tools)

        if response.tool_calls:
            for call in response.tool_calls:
                result = execute(call)
                messages.append({
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": json.dumps(result),
                })
            messages.append(response.message)
        else:
            # LLM 没调工具,说明它认为任务完成了
            return response.message.content

    return "达到最大步数,任务未完成"

REACT_SYSTEM_PROMPT = """
你是一个 AI 助手,可以使用工具完成任务。

工作流程:
1. 思考用户问题需要哪些信息
2. 调用合适的工具获取信息
3. 基于工具返回的结果继续思考或得出最终答案
4. 不要编造工具不返回的信息

如果你已经有足够信息回答,直接给出最终回答,不再调用工具。
"""

ReAct 简单但强大。Cursor、Claude Code、ChatGPT 的 GPT-4 都是 ReAct 的变体。

实战:做一个能查文档、查代码、查数据库的 Agent

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_docs",
            "description": "在内部文档库搜索。用于查找概念、规范、流程类信息。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_code",
            "description": "在代码库搜索。用于查找函数定义、类实现、代码示例。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "file_pattern": {"type": "string", "description": "如 *.py"}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "query_db",
            "description": "查询业务数据库。返回数据用于回答业务问题。",
            "parameters": {
                "type": "object",
                "properties": {
                    "sql": {"type": "string", "description": "只允许 SELECT 语句"}
                },
                "required": ["sql"]
            }
        }
    },
]

def execute(call):
    name = call.function.name
    args = json.loads(call.function.arguments)
    if name == "search_docs":
        return rag_search(args["query"])
    elif name == "search_code":
        return ripgrep_search(args["query"], args.get("file_pattern", "*"))
    elif name == "query_db":
        if not args["sql"].strip().upper().startswith("SELECT"):
            return {"error": "只允许 SELECT"}
        return db.execute(args["sql"])

# 用户问:"上个月有多少订单是 VIP 用户下的?"
react_agent("上个月有多少订单是 VIP 用户下的?", tools)

# Agent 内部:
# Step 1: query_db("SELECT user_level FROM users LIMIT 1") - 探索表结构
# Step 2: query_db("SELECT COUNT(*) FROM orders WHERE user_level='VIP' AND created_at >= ...")
# Final Answer: "上个月有 1234 单 VIP 订单"

Plan-and-Execute:先规划再执行

ReAct 是"边想边做",对复杂任务可能走偏。Plan-and-Execute 是"先做完整计划,再逐步执行":

# 第 1 步:Planner 生成计划
plan_prompt = f"""
任务:{user_task}
请生成完成这个任务的步骤列表,每个步骤可以调一个工具。

可用工具:{tool_descriptions}

输出 JSON:
{{
  "steps": [
    {{"step_id": 1, "description": "...", "tool": "..."}},
    ...
  ]
}}
"""

plan = llm(plan_prompt).parse_json()

# 第 2 步:Executor 逐个执行
results = []
for step in plan["steps"]:
    result = execute_step(step, prev_results=results)
    results.append(result)

# 第 3 步:Synthesizer 整合
final = llm(f"基于以下结果总结答案:{results}")

适用场景:

  • 任务步骤多、依赖明确(行程规划、研究报告生成)。
  • 每步成本高,需要先看到全局再决定要不要执行。

缺点:计划可能不完整,执行中遇到意外难以调整。改进:做"动态 replan" —— 每执行一步,检查"原计划还合理吗?",不合理就重新 plan。

Reflective Agent:让 Agent 自我反思

Agent 执行完任务后,让它自己检查是否真的完成了:

def reflective_agent(question, tools):
    answer = react_agent(question, tools)

    # 反思
    reflect_prompt = f"""
任务:{question}
当前回答:{answer}

请检查:
1. 回答是否完整解决了用户问题?
2. 是否有信息缺失或不准确?
3. 是否需要进一步操作?

如果回答不够好,说明缺什么,并给出改进方向(不要直接改答案)。
如果回答 OK,输出 "OK"。
"""
    critique = llm(reflect_prompt)
    if "OK" in critique:
        return answer

    # 用批评作为新的 prompt,让 agent 再做一轮
    return react_agent(f"{question}\n\n之前的回答不够好,问题:{critique}\n请重新解决。", tools)

这种"反思 + 重做"在写代码、写报告、做研究等开放性任务上效果显著。代价是 token 翻倍。

Multi-Agent:多个角色协作

当任务涉及多种专业能力,可以让多个 Agent 协作:

# 例:写技术文章
agents = {
    "researcher": "你是研究员,负责搜集资料、确认事实",
    "writer": "你是作者,基于资料撰写连贯文章",
    "reviewer": "你是审稿人,挑出文章里的问题",
    "editor": "你是编辑,基于审稿意见改稿",
}

# 协作流程
draft_topic = "如何配置 K8s HPA"

facts = run_agent("researcher", f"研究 {draft_topic} 的关键事实", tools=[search, rag])
draft = run_agent("writer", f"基于以下资料写一篇关于 {draft_topic} 的文章:\n{facts}")
critique = run_agent("reviewer", f"审查这篇文章:\n{draft}")
final = run_agent("editor", f"基于审稿意见改稿:\n原文:{draft}\n意见:{critique}")

Multi-Agent 适合:

  • 角色专业化能提升质量(代码生成 + 安全审计 + 性能优化分开)。
  • 视角多样性能减少偏见(支持方 + 反对方辩论)。

但代价:

  • Token 成本几倍。
  • 调试困难(一个 bug 可能源于多个 agent 之间的交互)。
  • 容易"无限对话",Agent 之间互相挑刺、改稿不收敛。

实战经验:能用单 Agent 就用单 Agent。Multi-Agent 在复杂任务有价值,但简单任务上往往是过度设计。

Agent 的关键工程挑战

1. 内存管理

长会话 / 多步骤 Agent 的对话历史会膨胀。策略:

  • 滑动窗口:只保留最近 N 轮。
  • 摘要:超过阈值时,把早期对话摘要为短文本。
  • 向量记忆:把对话历史 embed 进向量库,需要时检索回来。

2. 状态持久化

Agent 执行可能跨越多次请求(用户上次说一半,过几分钟回来继续)。需要把 Agent 状态(对话历史、中间结果、当前计划)持久化到数据库,重新拉起来时能恢复。LangGraph、Inngest 都提供 checkpoint 机制。

3. 可观测性

Agent 走了几十步,出错时不知道哪一步出问题。必须有:

  • 每一步的 trace:输入、输出、tool call、token 数、耗时。
  • 可视化界面:LangSmith / Weave / Helicone 等工具能展示 Agent 执行链。
  • 关键指标告警:总 token、总耗时、错误率、用户反馈。

4. 成本控制

Agent 平均一次任务调 5-20 次 LLM,token 成本累积快。控制方法:

  • 小任务用便宜模型(Haiku / GPT-4o-mini),复杂决策才用大模型。
  • 限制每次任务的最大循环数 + 最大 token 数。
  • 缓存常见请求结果。
  • 用 Prompt Caching 减少重复 system prompt 的成本。

5. 安全

Agent 能调危险工具(执行代码、改数据库、发邮件)。必须:

  • 权限隔离:每个 Agent 只能调它该调的工具,数据库只读、文件系统沙箱。
  • 人工审批:破坏性操作要 human-in-the-loop 确认。
  • Prompt Injection 防御:用户输入 / 外部数据可能包含恶意指令,要清洗或用 system prompt 加固。

主流 Agent 框架

  • LangGraph(LangChain 出品):基于图的 Agent 编排,支持复杂控制流、checkpoint、人工干预。最灵活也最重。
  • CrewAI:专注 Multi-Agent 角色协作,API 简单。
  • AutoGen(Microsoft):Multi-Agent 对话框架,擅长"群聊式协作"。
  • OpenAI Assistants API:OpenAI 官方的轻量 Agent 框架,内置 thread / file search / code interpreter。
  • Anthropic Claude with MCP:用 MCP 协议接入工具,简单清晰。

不到必要不要急着上框架。最简 ReAct 循环 100 行代码就能跑,先用裸代码理解清楚再考虑框架。

常见错误

错误 1:用 Agent 解决简单问题。 用户问"现在几点",ReAct 转一圈也只是 get_time —— 但启用 Agent 框架后系统复杂度暴涨,debug 成本暴涨。简单问题用 Workflow / 函数。

错误 2:Agent 一直走但不收敛。 工具数量多 + 任务边界模糊 + 没有明确终止条件 → Agent 不知道什么时候停。修复:明确"什么算完成",在 prompt 里写清。

错误 3:把 Agent 当万能。 Agent 不是 LLM 的"放大器" —— 它是 LLM 决策能力的延伸。如果 LLM 本身能力差,Agent 框架只会放大它的失败。

错误 4:不做评估。 Agent 上线后凭感觉调,无数据支撑。必须有自动化评估集:典型任务 50 个,每次改动跑一遍,看完成率、平均步数、token 成本。

Agent 设计的几个反直觉经验

1. 给 Agent 越少工具越好

新手常犯错误:把 30 个工具一股脑给 Agent,期待它"自动选对的"。实际上工具越多,LLM 选错的概率越高。把工具限制在 5-10 个,任务范围对应缩窄,Agent 表现反而更好。需要更多工具时,用 sub-agent 拆分。

2. Prompt 里"指导推理"而不是"指导动作"

不要写"调用 search 工具",而是写"先理解用户的真实意图,再判断什么信息需要查询,然后选合适的工具"。LLM 自己会想到工具,但你要引导它的推理过程

3. 失败信号要明确

工具返回空、404、参数错 —— 必须以 LLM 能立刻理解的方式呈现:

# 不好:返回 None
def search_docs(query):
    results = vector_db.query(query)
    if not results:
        return None    # LLM 看到 null,不知所措

# 好:返回结构化的明确信息
def search_docs(query):
    results = vector_db.query(query)
    if not results:
        return {
            "status": "no_results",
            "message": f"未找到关于 '{query}' 的文档。建议:换个关键词或尝试更宽泛的查询。"
        }
    return {"status": "ok", "results": results}

Human-in-the-Loop:Agent 不该全自动

对涉及钱、对外发消息、修改重要数据的操作,设计"暂停 + 等用户确认"机制:

def execute_with_approval(call):
    if call.function.name in HIGH_STAKES_TOOLS:
        # 暂停 Agent,把意图展示给用户
        approval = await ask_human(
            action=call.function.name,
            args=call.function.arguments,
            preview=f"即将向 {args['to']} 发送邮件:'{args['subject']}'"
        )
        if not approval:
            return {"status": "cancelled_by_user"}
    return execute(call)

LangGraph 的 interrupt 机制就是为这个设计的 —— Agent 流到某个节点时挂起,等外部信号(用户点了批准按钮)再继续。这是生产级 Agent 必备能力。

写在最后

Agent 不是魔法,它是"LLM + Function Calling + 循环"的工程组合。所有看似神奇的 Agent(Devin、Cursor、Claude Code)拆开看,都是这套机制的精心打磨 —— 工具设计、prompt 调优、错误处理、状态管理、可观测性。理解这一点,你看到任何新 Agent 框架都能快速判断它在解决什么、值不值得用。

给一个工程心得:Agent 项目的成败,80% 在"工具设计"和"评估机制",20% 才在"用什么框架"。把工具的描述、参数、返回值都设计成"LLM 容易理解、不易调错"的形式;建一组真实场景的评估集,每次改动有数据反馈。把这两件事做扎实,Agent 自然能跑起来。下一篇我们看 LangChain / LlamaIndex 这些框架的实际用法。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

Function Calling 完全指南:让 LLM 调用工具的工程实战

2026-5-15 16:01:22

技术教程

LangChain 与 LlamaIndex 完全指南:LLM 应用框架的实战选型

2026-5-15 16:01:23

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索