聊《Agent 核心原理:把学习路线变成作品集》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。
摘要
本文概述文章目标、核心观点和实践价值。
最近带团队做了几期 Agentic AI 的内部 PoC(概念验证),最大的感触是:市面上很多教程把 Agent 写得像是在变魔术——输入指令,吐出完美结果。但在实际工程里,Agent 更像是一个刚入职、有点聪明但经常犯迷糊的新手。它需要明确的 SOP(标准作业程序)、严格的权限控制,以及最重要的:一套能兜底的监控机制。
很多人问怎么在简历里体现 Agent 经验?别只放个“调用 LangChain”的代码。真正的竞争力在于:当 LLM 幻觉导致工具调用错误时,你的系统是如何感知、记录并恢复的?当上下文窗口溢出时,你是粗暴截断还是智能摘要?
今天不聊虚的概念,我们直接拆解一个生产级 Agent 必须具备的三个骨架:工具调用的确定性、记忆的层级化管理、以及基于日志的任务规划。我会结合我们在金融数据查询场景下的踩坑经历,把这些抽象原理变成你可以直接复用的代码结构和设计思路。
目录
- Agent 的本质:从聊天机器人到执行者
- 规划能力:ReAct 模式下的协作与日志
- 工具调用:契约精神与参数校验
- 记忆系统:分层存储的艺术
- 失败恢复:当 Agent 卡住时怎么办?
- 总结:构建可维护的 Agent 工程
Agent 的本质:从聊天机器人到执行者

传统 Chatbot 是“问答系统”,Agent 是“行动系统”。
在项目中,我们区分这两者的界限非常清晰:Chatbot 的目标是生成一段通顺的文字,而 Agent 的目标是完成一个具体的业务动作。比如,“帮我查一下上周订单量”对 Chatbot 来说是语义理解,对 Agent 来说则是触发 SQL 查询接口 -> 获取数据 -> 格式化表格 -> 发送通知。
我的取舍建议:
不要为了用 Agent 而用 Agent。如果一个任务可以通过简单的规则引擎或 API 调用解决,千万不要引入 LLM。只有当任务存在不确定性(如意图模糊、需要多步推理、动态组合工具)时,Agent 架构才有价值。
在团队落地中,我常强调一点:Agent 的核心不是“聪明”,而是“可控”。这意味着我们要把黑盒变成灰盒,通过结构化输出和中间状态追踪,让每一步决策都可审计。
规划能力:ReAct 模式下的协作与日志

目前业界最稳妥的规划范式依然是 ReAct (Reasoning + Acting)。即:思考 -> 行动 -> 观察 -> 再次思考。
但在企业级应用中,单纯的 ReAct 容易陷入死循环或无限重试。我们需要引入显式的状态机和详细的执行日志。
实战案例:API 调用超时处理
在一次内部工具集成中,LLM 经常因为网络波动连续重试同一个失败的 API 调用。如果没有外部干预,Agent 会不断消耗 Token 直到达到上限。
解决方案:
我们在规划层加入了一个“中间件层”,专门负责拦截工具的返回值。如果检测到特定错误码(如 503),它会强制中断当前轮次,并将错误详情记录到长期记忆中,同时向用户反馈“服务暂时不可用,请稍后重试”。
以下是一个简化的 Python 伪代码,展示了如何在一个简单的规划循环中加入错误恢复逻辑:
import asyncio
import logging
# 模拟工具库
class ToolRegistry:
def __init__(self):
self.tools = {}
def register(self, name, func):
self.tools[name] = func
async def execute(self, tool_name, args):
if tool_name not in self.tools:
return {"error": f"Tool {tool_name} not found"}
try:
result = await self.tools[tool_name](args)
return {"status": "success", "data": result}
except Exception as e:
# 关键:捕获异常并标准化输出,供 Agent 下一步规划使用
logging.error(f"Tool execution failed: {e}")
return {"status": "error", "message": str(e)}
class SimpleAgent:
def __init__(self, llm_client, registry):
self.llm = llm_client
self.registry = registry
self.history = [] # 短期记忆
async def plan_and_act(self, user_input):
# 1. 构造 Prompt,包含历史记忆
prompt = f"""
User Input: {user_input}
History: {self.history[-3:]} # 只保留最近3轮,防止上下文过载
Please choose a tool or answer directly. Format:
Thought: [你的推理过程]
Action: [工具名]
Action Input: [参数JSON]
"""
response = await self.llm.generate(prompt)
# 2. 解析 LLM 的输出
action, args = self.parse_llm_output(response)
if action == "FINISH":
return response
# 3. 执行工具
result = await self.registry.execute(action, args)
# 4. 更新记忆并继续下一轮
observation = f"Observation: {result}"
self.history.append(observation)
# 这里可以递归调用,或者交由更复杂的 Graph 框架处理
return observation
# 注册一个示例工具
registry = ToolRegistry()
async def get_weather(city):
# 模拟可能超时的网络请求
if city == "fail":
raise ConnectionError("Network timeout")
return f"Weather in {city} is sunny"
registry.register("get_weather", get_weather)
# 使用示例
agent = SimpleAgent(llm_client=None, registry=registry) # 假设已注入 LLM
这段代码虽然简陋,但它揭示了关键点:工具执行的异常必须由框架统一接管,而不是由 LLM 盲目猜测。 在团队开发中,这通常意味着你需要定义一套标准的 ToolOutput 协议,所有工具必须遵守。

工具调用:契约精神与参数校验
工具调用(Function Calling)是 Agent 落地的第一道门槛。很多开源 Demo 里,参数都是字符串硬编码,这在生产中是灾难。
我的建议:
1. 强类型定义:无论使用 LangChain、LlamaIndex 还是自研框架,务必使用 Pydantic 或 JSON Schema 来定义工具参数。LLM 擅长遵循模式,但不擅长记住复杂的业务逻辑约束。
2. 权限隔离:Agent 的工具权限应该最小化。例如,一个“报表生成助手”不应该拥有“删除数据库表”的工具权限。在架构上,这可以通过不同的 Service Account 或 API Gateway 路由来实现。
3. 参数自动补全:如果用户说“查上个月的销售”,Agent 需要能自动推断出 date_range="last_month"。这需要你在 Prompt 中提供清晰的字段说明,或者在 Tool 层增加一层预处理逻辑。
记忆系统:分层存储的艺术
记忆是 Agent 的“经验”。但在工程中,内存(Context Window)是有限的,硬盘(Vector DB)是昂贵的。如何平衡?
我将记忆分为三层:
1. 工作记忆(Working Memory):存储在变量中,仅保留最近 N 轮对话。用于维持当前任务的连贯性。一旦任务完成,这部分记忆立即销毁。
2. 短期记忆(Short-term Memory):存储在向量数据库中。存储用户的历史偏好、已解决的具体问题片段。检索策略基于相似度,但必须带上时间衰减权重。
3. 长期记忆(Long-term Memory):存储在关系型数据库或知识图谱中。存储结构化的业务实体(如用户ID、订单号、产品SKU)。这部分数据不依赖 LLM 的理解,而是直接关联。
踩坑现场:
我们曾遇到一个问题,Agent 在多次对话后开始混淆不同用户的上下文。原因是向量检索的 Top-K 设置过大,且没有对用户 ID 进行严格的过滤。
修正方案:
在每次检索记忆前,先在关系型数据层通过 user_id 过滤,再在向量层进行语义匹配。这样既保证了数据的隔离性,又利用了语义理解的灵活性。
# 伪代码:混合检索策略
def retrieve_memory(user_id, query):
# 1. 先查关系型数据库,获取该用户相关的实体 ID 列表
relevant_entities = db.query("SELECT entity_id FROM user_entities WHERE user_id = ?", user_id)
# 2. 如果有相关实体,先检索这些实体的详细向量
if relevant_entities:
vector_results = vector_db.search(query, filter={"entity_id": relevant_entities})
else:
# 3. 否则全局检索,但限制时间范围
vector_results = vector_db.search(query, time_filter="7d")
return vector_results
失败恢复:当 Agent 卡住时怎么办?
这是区分玩具项目和生产系统的分水岭。LLM 会犯错,工具会挂掉,网络会超时。你的 Agent 必须有“自愈”能力。
常见的失败模式及对策:
1. 格式解析错误:LLM 输出的 JSON 不合法。
对策:增加重试机制,并在 Prompt 中要求 LLM 输出 Markdown 代码块包裹的 JSON。如果连续三次失败,降级为人工介入或固定回复。
2. 无限循环:Agent 在两个工具之间反复调用(如 A 说查 B,B 说查 A)。
对策:设置最大迭代次数(Max Iterations)。超过次数强制终止,并返回“我无法确定下一步操作,请提供更多细节”。
3. 幻觉输出:工具返回了空数据,但 LLM 编造了结果。
对策:在后处理环节增加校验器。如果工具返回为空,严禁 LLM 基于此生成最终答案,而是触发“追问”流程,让用户补充信息。
总结:构建可维护的 Agent 工程
回到文章开头的观点:学习路线变成作品集,靠的不是跑通几个 Demo,而是展现出你对系统性问题的思考。
在面试或项目复盘中,当你谈论 Agent 时,不要只说“我用了 ReAct”,而要说出:
- 你是如何定义工具接口的?(Pydantic/JSON Schema)
- 当工具调用失败时,系统的恢复策略是什么?(重试/降级/人工)
- 记忆模块是如何解决上下文污染和隐私隔离问题的?(混合检索/用户隔离)
- 你是如何监控 Agent 的执行路径以便进行调试的?(结构化日志/Trace ID)
Agent 技术还在快速演进,LangGraph、AutoGen 等新框架层出不穷。但底层的工程原则是不变的:确定性优于随机性,可观测性优于黑盒,模块化优于单体。
希望这篇复盘能帮你理清思路。如果你正在构建自己的第一个生产级 Agent,不妨从写好一个健壮的错误处理模块开始。那才是你区别于其他初学者的关键一步。
资料展示
下面是我整理的AI大模型学习资料和工具包预览,适合收藏后按主题逐步学习。



如果你想看完整资料目录,可以在评论区留言「资料」;也欢迎告诉我你更关注AI大模型里的哪类内容。


被折叠的 条评论
为什么被折叠?



