Agent架构设计核心:规划器、工具、记忆、评估器,一个都不能少
见过太多「把 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 开发实践中,哪个组件你觉得最难调优?是规划器总是「想太多做太少」,还是评估器总是「差不多先生」,还是记忆总是「记了等于没记」?评论区来聊聊。
更多推荐



所有评论(0)