见过太多「把 LLM 套个壳就叫 Agent」的项目了。真正能用的 Agent,需要四个核心组件精密协作,少一个都是半成品。

一句话概括

现代 AI Agent 的架构本质上是一个自我驱动的执行循环:规划器(Planner)做决策,工具(Tools)执行动作,记忆(Memory)存储上下文,评估器(Evaluator)判断进度——四个组件缺一不可,共同构成了 Agent 的「大脑+手+记忆+反馈系统」。


为什么简单 prompt 不够用

先说个我踩过的坑。

刚开始做 LLM 应用时,我的方案极其简单:写一个超级 prompt,「你现在是一个助手,遇到问题先分析,用工具解决,做完了反思一下」。听起来挺像 Agent 的对吧?

实际跑起来,复杂任务全崩:让它写个报告,它在引言里翻来覆去,正文还没写就输出了「完成」;让它同时查三个数据源,它串行执行,明明可以并行的操作硬生生等了 10 分钟;问它做到哪一步了,它说「我已经完成了」——但实际上连一半都没做到。

问题出在哪?prompt 只能给 LLM 「能力」,给不了它「机制」

能力是你告诉它「你会什么」,机制是系统如何确保它「真的做到」。没有机制保障的 LLM,靠的是 LLM 自己根据 prompt 的「印象」来决定行为——这在简单场景下能用,在复杂任务上完全靠不住。

这就是 Agent 架构存在的意义:把「做什么」的决策和「怎么做」的机制分开,让系统层面保证正确性


四大组件详解

1. 规划器(Planner):LLM 的「大脑皮层」

规划器是 Agent 的核心决策单元,负责把复杂任务拆解成可执行的步骤序列。

ReAct 模式是目前最主流的规划范式:

Thought: 我需要解决 X,方法是先做 A,再做 B
Action: 调用工具 A
Observation: 结果是 Y
Thought: Y 表明 A 完成,现在做 B
Action: 调用工具 B
...

但这里有个严重误解:很多人以为 ReAct 里的 Thought 是 LLM「真正在思考」,实际上它是 LLM 输出的一段文本,这段文本描述了它决定做什么。Agent 系统读取这段文本来决定下一步行动。

这意味着规划器的质量直接由 prompt 决定,而 prompt 的质量又直接由你给 LLM 的「上下文」决定。所以规划器不是孤立的,它依赖记忆组件给它提供背景信息。

一个实用的规划器实现

class ReActPlanner:
    def __init__(self, llm, max_iterations=10):
        self.llm = llm
        self.max_iterations = max_iterations
    
    def plan(self, task: str, tools: list, memory: "Memory") -> list[str]:
        """
        返回 action 列表
        """
        history = memory.get_recent(max_turns=10)
        
        system_prompt = f"""你是一个任务规划专家。
当前任务:{task}
可用工具:{[t.name + ': ' + t.description for t in tools]}

你必须严格按照以下格式输出每一步:
Thought: <你对接下来的思考>
Action: <工具名称> | <JSON格式的参数>
Observation: <工具返回结果>

当任务完成时,输出:
Thought: 任务已完成
Final Answer: <任务结果摘要>
"""
        
        messages = [{"role": "system", "content": system_prompt}]
        for h in history:
            messages.append({"role": "user", "content": h.observation})
            if h.action:
                messages.append({"role": "assistant", "content": h.action})
        
        messages.append({"role": "user", "content": "请开始执行任务。"})
        
        response = self.llm.chat(messages)
        return self._parse_actions(response.content)

规划失败的常见原因

  • LLM 在 Thought 里说了要做的工具,但 Action 里忘记调用了(规划-执行不一致)
  • 步骤太多,LLM 在中间几步「跑偏」,开始做和原任务无关的事
  • 工具参数格式和 LLM 理解的不一致,导致调用失败

2. 工具(Tools):Agent 的「手」

工具是 Agent 与外界交互的唯一通道。没有工具的 Agent 只能「想」,不能「做」。

工具设计有几个关键原则:

原则一:工具要有明确的边界。一个工具做一件事,不要设计「万能搜索工具」同时返回天气、股票、新闻——LLM 很难在一次调用里处理这么多不同类型的信息。

原则二:工具描述即文档。工具的 description 是 LLM 决定是否调用它的唯一依据。我在工程中发现,很多 Tool Calling 失败的根本原因是 description 写得太模糊,LLM 不知道什么时候该用它。

# ❌ 差的描述
def search(query: str):
    """搜索"""
    pass

# ✅ 好的描述
def search(query: str, source: str = "web", max_results: int = 5) -> list[dict]:
    """
    在指定来源中搜索信息。

    适用场景:
    - 需要获取最新新闻、事件或公开数据时
    - 需要验证某个事实是否准确时
    - 需要了解某个话题的公开信息时
    
    不适用场景:
    - 需要访问私有/内部数据(用 get_internal_doc)
    - 需要执行操作而非获取信息(用 send_notification)
    
    参数:
    - query: 搜索关键词,1-100字符
    - source: 数据源,web/news/wiki/internal 之一
    - max_results: 返回结果数量,1-10,默认为5
    """
    pass

原则三:工具要有错误处理和降级策略。网络超时、API 限流、返回格式异常——这些在生产环境都会遇到。工具层面要能做重试、返回错误信息,而不是直接崩溃让整个 Agent 卡死。

3. 记忆(Memory):Agent 的「海马体」

记忆组件解决的是「LLM 的 context window 有限,但任务可能很长」的问题。

一个实用的多级记忆架构:

class HierarchicalMemory:
    """
    三层记忆架构:
    - Working Memory: 当前任务的上下文(最近 N 轮对话)
    - Short-Term Memory: 今天/本周的会话摘要
    - Long-Term Memory: 持久化的知识、偏好、经验
    """
    
    def __init__(self, vector_store, session_id: str):
        self.vector_store = vector_store  # 存储向量,用于语义检索
        self.session_id = session_id
        self.working_memory = deque(maxlen=20)  # 最近 20 轮
        self.short_term = self._load_short_term(session_id)
        self._summarize_and_consolidate()
    
    def add_turn(self, thought: str, action: str, observation: str):
        turn = Turn(thought, action, observation)
        self.working_memory.append(turn)
        
        # 同时写入向量数据库(长期记忆)
        self.vector_store.add(
            text=f"Task: {thought}\nAction: {action}\nResult: {observation}",
            metadata={"session": self.session_id, "timestamp": time.time()}
        )
    
    def get_context(self, query: str, max_tokens: int = 8000) -> str:
        """
        为 LLM 构造上下文:优先 working memory,补充 long-term memory
        """
        context_parts = []
        remaining = max_tokens
        
        # 1. 先加当前任务的工作记忆(逆序,最近的在前)
        for turn in reversed(self.working_memory):
            turn_text = turn.to_string()
            if len(turn_text) > remaining:
                break
            context_parts.insert(0, turn_text)
            remaining -= len(turn_text)
        
        # 2. 相关性检索补充长期记忆
        if remaining > 500:
            relevant = self.vector_store.search(query, top_k=3)
            context_parts.append("=== 相关历史经验 ===")
            context_parts.extend([r.text for r in relevant])
        
        return "\n".join(context_parts)
    
    def _summarize_and_consolidate(self):
        """定期把 working memory 压缩为摘要存入 short-term"""
        if len(self.working_memory) >= 15:
            summary = self._make_summary(self.working_memory)
            self.short_term.append(summary)
            self.working_memory.clear()

记忆什么时候写入比写入什么更重要。我倾向于「每次工具调用后都写记忆」,而不是「等任务结束再总结」——因为中途 Agent 可能被打断(超时、用户中断),如果记忆没有及时持久化,这些上下文就丢了。

4. 评估器(Evaluator):Agent 的「前额叶」

评估器可能是四个组件里最容易被忽略的一个——但也是最能区分「能用」和「好用」的关键。

评估器负责三件事:

第一,进度判断:当前状态离目标还有多远?是否已经完成?

第二,质量判断:工具返回的结果质量如何?是否需要重试或换方法?

第三,异常检测:是否陷入了循环?是否产生了幻觉?是否调用了错误的目标?

class Evaluator:
    def __init__(self, llm):
        self.llm = llm
    
    def evaluate(self, state: AgentState) -> EvaluationResult:
        """
        评估当前 Agent 状态
        """
        # 检查循环:最近 5 步是否完全相同?
        recent_actions = [t.action for t in state.history[-5:]]
        if len(set(recent_actions)) == 1 and len(recent_actions) == 5:
            return EvaluationResult(
                status="loop_detected",
                recommendation="replan",
                reason="连续5步执行了相同的 action,可能陷入循环"
            )
        
        # 检查完成度
        completion_score = self._assess_completion(state)
        if completion_score > 0.9:
            return EvaluationResult(
                status="near_complete",
                recommendation="finish",
                reason=f"任务完成度约 {int(completion_score*100)}%"
            )
        
        # 检查结果质量
        last_result = state.history[-1].observation if state.history else ""
        quality = self._assess_quality(last_result)
        if quality < 0.3:
            return EvaluationResult(
                status="low_quality",
                recommendation="retry_with_different_tool",
                reason=f"最后一步结果质量较低({quality:.2f}),建议换工具"
            )
        
        return EvaluationResult(status="in_progress", recommendation="continue")
    
    def _assess_quality(self, result: str) -> float:
        """
        用 LLM 打分:结果是否有实质内容,是否与任务相关
        """
        prompt = f"""评估以下工具返回结果的质量,0-1分:
        
结果:
{result}

评分标准:
0.0 = 空结果、错误信息、不相关内容
0.5 = 有内容但不完整,或有明显遗漏
1.0 = 完整、准确、直接可用

只输出一个数字,例如:0.7
"""
        response = self.llm.chat([{"role": "user", "content": prompt}])
        try:
            return float(response.content.strip())
        except:
            return 0.5

组件如何串联:完整的 Agent Loop

四个组件不是孤立的,它们在一个循环中协作:

用户输入任务
    ↓
规划器接收任务 + 读取记忆 → 制定执行计划
    ↓
执行计划第一步 → 调用工具
    ↓
评估器检查结果 → 质量OK? 完成度够高?
    ↓ ↓
Yes → 结束
No  → 记忆组件记录本轮结果 → 规划器更新计划 → 继续下一轮

这个循环的终止条件可以是:

  • 评估器判断完成
  • 达到最大迭代次数(防止死循环)
  • 用户主动中断

我踩过的那些坑

坑一:规划器和执行器用了不同的 LLM

为了省成本,我试过用小模型做规划、大模型做执行。结果发现规划质量差得离谱——小模型根本搞不清楚复杂任务的步骤依赖关系,规划出来的计划漏洞百出。后来统一换成同款模型,质量稳定多了。

坑二:记忆没有分层,所有东西都塞 working memory

早期我只有 working memory,每次任务跑完 context 就清空了。结果遇到「用户问了一个上上个小时讨论过的类似问题」,Agent 完全不记得,白白重新执行了一遍。教训是:三层记忆不是可选项,是必选项

坑三:评估器太宽容,让 Agent 在错误方向上走了十几步

评估器打分主观性很强,如果 prompt 没设计好,LLM 会倾向于「还行,凑合能用」,而不是「结果质量差,建议重试」。后来我把质量评估改成了更具体的维度:完整性、准确性、相关性,每个维度单独打分,有一个低于阈值就触发重试。


写在最后

Agent 架构设计没有银弹。四组件模式是目前工程验证最充分的方向,但它也不是完美的——ReAct 的循环在长任务上 token 消耗很大,记忆的检索质量直接影响规划器的决策,评估器的打分标准很难统一。

真正做产品的时候,你会发现瓶颈往往不在「架构设计」本身,而在「工程细节」:工具的响应速度、错误处理的完善程度、记忆检索的召回率。架构给你方向,工程给你精度。

讨论问题:在你的 Agent 开发实践中,哪个组件你觉得最难调优?是规划器总是「想太多做太少」,还是评估器总是「差不多先生」,还是记忆总是「记了等于没记」?评论区来聊聊。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐