构建自己的AI Agent心得
折腾了一段时间AI Agent开发,从最初调API写demo,到后来用LangChain搭workflow,再到最后决定自己撸一个轻量框架——这篇文章算是把踩过的坑和想明白的事儿做个总结。如果你也是那种”用别人的框架总觉得哪里不对劲”的开发者,这篇可能对你有点用。
具体代码参考: https://github.com/sunyanyan/learn-ai-agent
为何需要自建Agent框架
先说一个最根本的问题:LLM和Agent到底是什么关系?
用一句话概括:LLM是大脑,Agent是完整的人。 LLM负责”想”——理解语言、推理、生成内容;Agent负责”做”——感知环境、调用工具、规划步骤、从错误中恢复。你可以把LLM类比为CPU,Agent就是一台完整的电脑:CPU再快,没有总线、内存、IO接口,也跑不了任何程序。
市面上已经有LangChain、LangGraph、AutoGen这些成熟框架了,为什么还要自己从零搭一个?我在实际开发中遇到的痛点很具体:
- 过度抽象的复杂性。用LangChain做点简单的事,你得先理解Chain、Agent、Tool、Memory、Retriever等十几个概念。每个概念都有自己的抽象层,学习曲线陡得离谱。
- API迭代太快。商业化框架为了抢市场,接口天天变。今天写的代码,明天升级可能就跑不了了。
- 黑盒实现。核心逻辑封装得太严,出了问题只能翻文档和等社区回复。想深度定制?很难。
- 依赖地狱。成熟框架拽一堆依赖包,跟其他项目混用的时候冲突搞到你怀疑人生。
自建框架的过程本身也是一次能力跃迁——从”使用者”变成”构建者”。你亲手实现每个组件之后,才会真正理解Agent的思考过程、工具调用机制为什么这样设计、各种范式的优缺点到底差在哪。更实际的是,生产环境中对响应时间、内存占用、并发能力都有严格要求,通用框架的”一刀切”方案往往不够用,你迟早要做二次开发,不如一开始就自己掌控每一行代码。
当然,我不是说自建框架就一定比开源框架好。如果你只是想快速搭一个原型验证,LangChain、Dify、Coze 这类工具完全够用。但如果你要深入理解Agent的工作原理,或者有强烈的定制化需求,自己搭一遍是值得的。
我参考了 Datawhale 社区的 Hello-Agents 教程,这个教程的核心理念我是认同的:从核心原理出发,亲手构建,穿透框架表象。 他们做了一个叫 HelloAgents 的教学框架,设计上做了几个关键的简化决策——基于OpenAI标准API构建(不重新发明轮子)、极简依赖(除了OpenAI SDK几乎不引别的)、”万物皆为工具”的统一抽象。这些思路我后面会展开讲。
Agent范式的框架化实现
Agent 不是一坨代码乱炖出来的,它有经典范式。这里说三种我认为最实用的:ReAct、Plan-and-Solve、Reflection。
ReAct:边想边做
思路:ReAct 的核心是”Thought → Action → Observation”的循环。每一步让模型先”想”清楚当前需要什么信息或做什么操作(Thought),然后执行一个动作(Action,通常是调用工具),拿到结果后(Observation)再进入下一轮思考,直到能给出最终答案。
使用场景:需要多步推理 + 工具调用的任务。比如”搜索最新的AI新闻,并计算相关公司的市值总和”——模型需要先搜索新闻、识别公司、查市值、最后做计算。
优点:
- 逻辑清晰,每一步的”思考过程”都可观测,出了问题容易定位
- 灵活性强,模型能根据中间结果动态调整策略
缺点:
- 每次只走一步,如果任务步骤多,往返轮次开销大(每步都要调一次LLM)
- 有时候模型会”原地打转”——反复调用同一个工具或陷入无效推理
- 依赖良好的提示词工程来保证格式输出的稳定性
改进建议:
- 设置
max_steps上限防止无限循环 - 提示词里明确”每次只能执行一个步骤”,避免模型一口气把多步混在一起
- 加入历史记录截断机制,避免 context 窗口被中间步骤撑爆
Plan-and-Solve:先规划再执行
思路:先让模型对整个任务做一个整体规划(Plan),拆解成若干子任务,然后依次执行每个子任务(Solve)。思想很简单——“磨刀不误砍柴工”。
使用场景:步骤明确、相对复杂的任务。比如”写一份关于XX主题的调研报告”——需要先规划:收集资料、分析数据、组织结构、撰写正文。
优点:
- 对复杂任务有全局观,不会迷失在细节中
- 规划阶段可以暴露思路缺陷,提前发现而不是闷头走到最后才发现跑偏了
缺点:
- 如果规划阶段判断失误,后面执行全跟着错。而且执行阶段不容易纠正前期的错误规划
- 对简单任务来说,多一个规划步骤反而是额外开销
- 真实世界的情况可能变化,静态规划无法应对执行过程中的新信息
改进建议:
- 在执行过程中加入”re-plan”机制,每完成一个子任务后重新评估剩余计划
- 对简单任务跳过规划步骤,直接走 ReAct 模式
- 规划粒度不要太细,给出高层方向就好,具体步骤让执行阶段灵活处理
Reflection:自我反思
思路:让Agent在完成任务后,对自己的输出进行一轮”审查和反思”,找出不足之处,然后修正输出。本质上是”生成-评价-改进”的三步闭环。
使用场景:对输出质量要求高的任务,比如代码生成、文本写作、逻辑推理。模型生成初稿后,触发自我审查机制,发现漏洞并修正。
优点:
- 能显著提升输出质量,相当于模型自带了一轮”code review”
- 不需要额外的外部反馈,模型自审自纠
缺点:
- 需要额外的LLM调用,计算成本翻倍
- 模型”反思”的能力有上限,有时候反思后改出来的反而更差(幻觉式反思)
- 对于事实性错误,自我反思未必能发现(原来就想错了,再想一遍还是错)
改进建议:
- 结合外部反馈,不要只靠模型自审——可以引入测试结果、用户反馈等客观信号
- 限制反思轮数,一两轮就够了,否则可能越改越糟
- 对简单输出跳过反思(你回复个”你好”不需要reflective thinking)
框架化小结
这三种范式不是排他的,可以组合使用。比如 Plan-and-Solve 的规划阶段可以嵌套 ReAct 的逐步推理,最后再用 Reflection 做质量自检。关键是要根据任务复杂度选择——简单任务用 SimpleAgent 直接对话就够了,别过度设计。
工具系统
工具系统是Agent框架中最重要的基础设施之一。为什么?因为Agent的”能力边界”基本就是由它能调用什么工具决定的。没有工具的Agent只能聊天,有了工具的Agent可以搜索、计算、查数据库、操作文件系统——从”说”变成”做”。
设计思路
工具系统的核心设计目标是:统一的抽象 + 标准化的注册机制 + 灵活的自定义能力。
我把工具系统拆成三层:
Tool基类:抽象出
run(parameters) -> str的统一接口。所有工具都是这个接口的实现,入参字典、出参字符串,简单粗暴但管用。工具还具备自描述能力——通过get_parameters()方法告诉调用者自己需要什么参数,这为自动化文档生成和参数校验提供了基础。ToolRegistry注册表:工具的管理中枢。支持两种注册方式——复杂的Tool对象注册(完整参数定义和验证)和简单的函数直接注册(快速集成现有函数)。注册后,Registry能生成所有工具的格式化描述字符串,直接用于构建Agent的提示词,让Agent知道”我有哪些工具可以用”。
内置工具集:提供 CalculatorTool(数学计算)、SearchTool(搜索)等开箱即用的工具,降低上手门槛。
使用场景
工具系统的使用场景贯穿Agent的整个生命周期:
- ReAct模式:模型在Thought阶段决定调用哪个工具,Action阶段执行调用
- SimpleAgent模式:可选的工具调用增强,通过文本解析
[TOOL_CALL:tool_name:parameters]格式来触发工具 - Function Calling:ToolRegistry能转换为OpenAI function calling schema格式,让工具被OpenAI原生function calling直接使用
优缺点
优点:
- 工具接口统一,新工具开发只需要实现
run方法,调用方无需关心内部实现 - 注册机制灵活,支持从简单函数到复杂对象的渐进式开发
- 工具消费端(各种Agent)不需要知道工具的具体类型,降低耦合
缺点:
run方法返回字符串虽然通用,但对结构化数据的处理不够优雅(需要序列化/反序列化)- 工具的描述质量直接影响模型的选择准确率——描述写得不好,模型压根不会用你的工具
- 工具集膨胀后,”选哪个工具”本身就成了一个难题。如果连人类开发者都说不准用哪个工具,别指望智能体做得更好
改进建议
- 精心管理”最小可行工具集”(MVTS):工具不是越多越好。每个工具职责单一、相互低重叠、接口语义清晰是基本要求。
- 工具描述要下功夫写:这是模型选择工具的唯一依据。描述要包括:工具做什么、什么情况下该用、什么情况下不该用、参数的含义。
- 加入工具调用结果缓存:对于幂等操作(比如搜索相同的query),不重复调用。
- 异步执行器:对于可能耗时的工具(比如网络请求),支持异步执行避免阻塞。
上下文工程
这是我认为2025年最被低估的概念。
Prompt Engineering 的尽头,是 Context Engineering
如果2024年人人都在研究提示词工程,那2025年你应该研究的是上下文工程。区别在哪?
提示词工程关注”怎么写好一条instruction”——系统提示的措辞、结构、格式。
上下文工程关注的是”在每次模型调用前,如何系统化地构造最优的信息集合”——不仅包含提示本身,还包含工具定义、消息历史、检索到的外部数据、记忆等一切进入上下文窗口的信息。
为什么这个转变是必然的?因为Agent运行在多轮循环中,上下文在不断增长。LLM的上下文窗口虽然越来越大,但一个残酷的事实是:上下文是一种有限资源,而且边际收益递减。 随着上下文中的token增加,模型准确回忆信息的能力反而下降——这个现象叫做”上下文腐蚀(context rot)”。
模型就像人一样,给它太多信息,它反而会”走神”。每新增一个token,都在消耗它的”注意力预算”。所以核心问题不是”能不能把信息都塞进去”,而是”哪些信息值得放进去”。
核心方法论:GSSC 流水线
参考 HelloAgents 的设计,上下文工程可以拆成一条清晰的流水线:
1. Gather(汇集):从多个来源收集候选信息。对话历史、系统指令、记忆系统检索结果、RAG检索结果、自定义信息包。关键设计:容错机制——每个数据源的调用都包 try-except,单个源的失败不影响整体。系统指令标记最高优先级,始终保留。
2. Select(选择):从候选信息中选出最相关的。核心是评分机制——综合分数 = 相关性权重 × 相关性 + 新近性权重 × 新近性。按分数降序排序,贪心地按分数从高到低填充,直到达到token上限。低于最小相关性阈值的信息直接过滤掉。
3. Structure(结构化):将选中的信息组织成固定的分区模板。比如:
1 | [Role & Policies] — 角色定位和行为准则 |
这种结构化输出有几个好处:可读性强(人和模型都好理解)、便于调试(哪个区域的信息有问题一目了然)、便于A/B测试。
4. Compress(压缩):对超限上下文的兜底处理。按分区逐段检查,完整保留优先级高的区块,对超限部分做截断并标注”内容已压缩”。生产环境中可以用LLM做高质量摘要替代简单截断。
长时程任务的上下文策略
对于跨数小时甚至数天的长时程任务(比如大型代码库迁移、系统性研究),光靠扩大上下文窗口是没用的,需要专门的工程手段:
压缩整合(Compaction):对话接近上下文上限时,做高保真总结,保留架构决策和关键细节,丢弃冗余的工具输出。用压缩摘要重启一个新的上下文窗口继续。
结构化笔记(Structured Note-taking):Agent以固定频率将关键信息写入上下文外的持久化存储——比如一个
NOTES.md文件 + TODO列表。跨多轮工具调用和上下文重置仍能保持进度一致性。代价极低:你只需要在工作记忆外维护一个轻量索引。子代理架构(Sub-agent architectures):主代理负责高层规划和综合,多个专长子代理在”干净的上下文窗口”中各自深挖。子代理处理完只回传凝练摘要(1,000–2,000 tokens),庞杂的搜索上下文留在子代理内部不污染主上下文。
优缺点
优点:
- 系统化地管理上下文,不再”凭感觉”拼prompt
- 上下文质量可度量、可调参、可A/B测试
- 长时程任务保持连贯性
缺点:
- 实现复杂度不低——GSSC流水线的每一步都有调参空间
- 相关性计算如果用简单关键词重叠(Jaccard相似度),精度有限。生产环境需要换向量相似度
- 增加了每次调用的额外计算开销(评分、排序、压缩)
改进建议
- JIT(Just-in-Time)上下文:不要预先加载所有相关数据。维护轻量化引用(文件路径、URL、查询语句),运行时通过工具动态加载。认知模式更像人——我们不会死记所有信息,而是用索引按需提取。
- 混合策略:前置加载少量”高价值”上下文保证速度,然后允许Agent按需自主探索。
- 相关性计算升级:从关键词重叠升级为向量相似度,显著提升检索质量。
- 动态token预算:简单任务用小预算,复杂任务开大预算。不要一刀切。
智能体通信协议
前面讲的都是”单个Agent自己怎么干好活”。但真实世界的系统是复杂的——你需要让Agent跟外部世界交互,需要让多个Agent协作。这就引出了通信协议。
参考 Datawhale 的智能体通信协议教程,目前业界主流的三种协议设计目标不同,解决不同层次的问题:
MCP(Model Context Protocol)—— 智能体与工具的桥梁
MCP 由 Anthropic 团队提出,核心理念是标准化智能体与外部工具/资源的通信方式。
设计思路:想象一下,你的Agent需要访问文件系统、数据库、GitHub、Slack等十几种服务。传统做法是为每个服务写专门的适配器——处理不同的API、认证方式、错误处理,工作量巨大且无法复用。MCP就像”USB-C统一了各种设备的连接方式”一样,统一了Agent与工具的交互方式。
MCP采用三层架构:
- Host(宿主层):用户直接交互的界面,管理整个对话流程
- Client(客户端层):负责与MCP Server建立连接、发送请求、接收响应
- Server(服务器层):执行具体功能操作,如文件扫描、数据库查询
MCP与Function Calling的关系不是竞争而是互补:Function Calling是LLM”学会打电话”的能力(何时拨号、如何沟通),MCP是”全球统一的电话通信标准”(任何一部电话能拨通另一部)。有了MCP,你不需要为OpenAI和Claude分别写不同的工具定义格式——一套MCP Server通吃。
使用场景:需要访问外部服务(文件、数据库、API)的Agent。尤其是需要切换不同LLM提供商的场景——只要模型支持MCP,就能无缝访问相同工具。
优点:
- 标准化接口,不同开发者的工具可以无缝集成
- 动态发现——运行时自动发现服务器提供的工具,不需要预先配置
- 社区生态丰富——Anthropic和社区已经创建大量现成MCP Server(文件系统、GitHub、PostgreSQL、Playwright等),拿来即用
缺点:
- 协议还处于发展早期,工具的时效性依赖维护者
- 需要额外部署和维护MCP Server
- 增加了一层通信抽象,简单场景下显得过重
改进建议:
- 优先选择大公司背书的MCP工具(如Anthropic官方维护的servers),社区维护的要评估活跃度
- 对内部服务,只暴露Agent真正需要的工具——别一股脑全注册
- 工具描述(description)要写清楚——这是LLM决定”用哪个工具”的唯一依据
A2A(Agent-to-Agent Protocol)—— 智能体间的对话
A2A 由 Google 团队提出,核心理念是实现智能体之间的点对点通信。
设计思路:当任务需要多个专业智能体协作(如研究员+撰写员+编辑),传统的中央协调器方案有三个硬伤:单点故障(协调器挂了全系统瘫痪)、性能瓶颈(所有通信过中心)、扩展困难。A2A采用P2P架构,让Agent直接对话,像人类团队一样协商和协作。
A2A的核心抽象概念是任务(Task)和工件(Artifact)。任务有标准化生命周期:创建→协商→代理→执行→完成/失败。通过这个机制,Agent可以进行任务协商、进度跟踪和异常处理。
使用场景:多智能体协作的复杂场景。比如一个智能文档助手中的分工协作——Agent1搜索GitHub项目,Agent2根据搜索结果生成Markdown报告,各自专业领域独立工作,通过A2A传递中间结果。
优点:
- 去中心化,避免单点故障
- 对等通信,每个Agent既是服务提供者也是消费者
- 标准化的任务生命周期管理
缺点:
- Python实现目前还比较繁琐
- 协议处于早期,成熟实现不多,大部分还是Sample Code级别
- 对于简单的多Agent场景,直接函数调用就够了,引入A2A反而增加复杂度
写到这里,回到最本质的一点:构建Agent不是在写一个聊天机器人,而是在设计一个能感知环境、规划行动、使用工具、从错误中恢复的软件实体。 范式选择、工具系统设计、上下文工程、通信协议——这四个维度构成了Agent框架的骨架。每一个维度都有经典理论和最佳实践,但最终能不能跑通、跑得好不好,还是得自己亲手实现一遍才知道。
2025年确实是Agent元年。与其被各种框架的复杂性吓退,不如从一个简单的ReAct循环开始,一步步往上走。自己搭一遍,你就什么都懂了。