最近陆续有人问我:AI agent 到底怎么做、从哪学起、用哪个框架、怎么评估效果。这个问题确实值得单独聊。AI agent 这波热度,和单纯调 AI 模型完全是两码事。如果你只是把大模型接进对话框,那还不叫 agent;真正的 agent 至少要有自己的记忆、会调用工具、能自己做规划、能根据结果调整下一步。换句话说,它从一个“问答工具”变成了“能干活的小助手”。这篇文章我把自己在 agent 方向上的实践、踩坑、以及目前比较靠谱的落地路径整理出来,适合刚接触 agent 开发的人,也适合已经跑了几个 demo 但觉得“不太稳定、不知道下一步怎么办”的朋友。
1. 别急着写代码,先把 agent 拆明白
1.1 agent 不是“AI模型加个循环”,核心是三个闭环
很多初学者把 agent 理解为“模型跑循环”:用户输入任务,模型生成结果,如果没完成就再生成一次。这种理解太粗暴,而且跑出来的东西基本不可用。真正的 agent 核心有三个闭环:感知、决策、行动。
感知环节负责理解当前状态。这不只是读一遍用户输入,而是要把工具返回的结果、多轮对话的历史、当前任务进展都汇总起来,让模型知道“我现在到底在哪一步”。决策环节是模型发挥核心作用的地方,它要决定下一步调用哪个工具、工具传什么参数、还是直接给用户回答。行动环节则是实际执行工具调用,拿到结果再回到感知,形成循环。
我见过很多人把这三个环节混在一起写,代码里到处是 if 判断,结果模型一旦说出预期之外的话,整个流程就崩。我自己的做法是严格拆分:感知阶段统一做“上下文打包”,决策阶段只让模型输出结构化指令,行动阶段由一个调度器去执行。“上下文打包”是我们自己的说法,但思想就是让模型只需要关注它该关注的,别把无关日志和报错全塞给它。
1.2 从大模型到智能体的关键升级:工具、记忆、规划
大模型本身是一个“聪明的知识库”,它知道自己不知道什么,但没手没脚。agent 的升级主要是三件事:给模型工具、给模型记忆、让模型学会规划。
工具是 agent 触达外部世界的接口。比如查天气、读文件、调 API、发邮件。模型的文本生成能力负责把用户需求“翻译”成一次工具调用。工具定义写得好不好,直接决定 agent 能不能顺利完成多步操作。一个很常见的坑是工具描述写得太笼统,模型不知道该在什么条件下用,或者参数说明不清楚,导致模型反复编造参数。
记忆是另一个明显的分水岭。没有记忆的 agent 每轮对话都是“失忆状态”,它记不住用户偏好,也记不住之前查到的中间结果。加了记忆之后,agent 才会越用越顺手。规划能力则是把大任务拆成小步骤,这一步不能光靠模型瞎猜,最好配合明确的任务模板和步骤约束。
我曾经把这三件事放在一张图上:底部是模型,中间是记忆和工具层,顶部是规划和任务执行层。每层各司其职,出了问题也能快速定位。很多人一上来就去追最新的模型、最复杂的框架,结果忽略了这三层基础能力,最后做出来的 agent 只是“长得像智能体的聊天机器人”。
2. 技术选型:框架、方案与编排怎么搭
2.1 先明白“Agent框架”和“编排”到底是什么
市面上 agent 框架特别多,而且更新极快。有些框架天生带工具调度,有些只是把多步提示词组织起来,还有些把整套 agent 交互、记忆、评估都做了。如果分不清这些,选型就是纯看热度。
可以这样区分:一个框架如果只提供“给模型加工具”的能力,这叫工具调用框架;如果它能管理任务队列、资源分配、多个 agent 之间的协作,这才叫编排框架。编排解决的是“多个步骤和多个智能体之间怎么排队、怎么依赖、怎么传结果”的问题。很多人问 harness 和 agent 的区别,我一直觉得 harness 更偏向“脚手架/执行环境”,它告诉你事情按什么顺序跑,而 agent 更强调“决策者”的角色。在实际项目里,你可以用 harness 做执行底座,用 agent 做决策大脑,两者不是替代关系。
还有一个常见疑问:skill 和 agent 的区别。Skill 是 agent 可调用的一项具体能力模块,比如“总结邮件”“生成图片”都可以是 skill;agent 则是负责判断什么时候调用哪个 skill 的完整系统。你把 skill 写好,不一定能组成好 agent,因为少了决策层。这个点经常被忽略。
2.2 主流框架怎么挑:给个不吹不黑的对比
我这里不拉踩具体框架,只分享一下我的选择逻辑。先看框架是否活跃、文档是否完整;再看它默认的模型接口是否容易替换;最后看它有没有处理记忆或者评估的插件位,这关系到后期扩展。
用表格整理一下我筛选框架时的判断维度:
| 维度 | 说明 | 我的关注点 |
|---|---|---|
| 更新活跃度 | 框架最近一年提交频率 | 太冷门不选,避免文档过时 |
| 模型适配 | 是否支持主流大模型接口 | 至少能用环境变量切换接入 |
| 工具注册方式 | 函数定义还是装饰器 | 装饰器方式写起来更直观 |
| 记忆扩展 | 是否提供存储会话和向量的接口 | 没有的话后续改造麻烦 |
| 评估生态 | 是否有 eval 相关模块 | 没有也能用,但会额外做功 |
| 调试体验 | 日志、trace、可视化程度 | 日志必须能看全工具调用链 |
说个经验:我不建议一上来就上“全家桶”框架。如果只是做一个整理文件、查资料的小助手,用轻量方案完全够。所谓轻量方案,就是自己准备几个函数、定义好工具 JSON Schema,然后用大模型的 function calling 能力直接调用。这个方案最稳,因为它逼你理解工具调用细节,不会被框架隐藏掉。等真需要多 agent 协作、复杂记忆、大量并发了,再考虑更重的编排框架也不迟。
2.3 为什么我建议从“单 agent + 人工编排”开始
很多人一听到 agent 就想做多智能体系统:一个 agent 负责拆分任务,几个子 agent 并行干活,最后一个汇总。这种架构演示效果很好,但实际做起来很容易失控。子 agent 之间传话会出错,上下文互相污染,最终结果的质量还不如单个 agent 认真跑一遍。
我从几个实际项目里得到的结论是:优先做“单 agent + 人工编排”。也就是一个 agent 负责核心决策和执行,之外的任务拆分、检查、兜底先由人来控制。这样有几个好处:第一,问题定位容易,出错时知道是模型决策错还是工具执行错;第二,成本可控,不会因为多个 agent 反复对话烧掉太多 token;第三,便于从简单场景积累经验,然后逐步增加 agent。
举个例子,做一个“网页资料整理助手”,单 agent 就够了:用户给一个网址,agent 抓取内容,调用摘要工具,把结果写进笔记。后期如果发现单 agent 一次处理太多资料容易漏,再引入“清单检查 agent”来复核。一步一步进化,比一开始就搭一个华丽的多 agent 系统靠谱得多。
3. 记忆设计:agent 能力的隐形瓶颈
3.1 记忆必须分层,别把向量库当万能药
记忆是 agent 方向里最容易被低估的难点。我见过太多人一上来就说“给 agent 加向量数据库,让它永久记忆”,结果做完发现效果很差。向量检索只是记忆的一种索引方式,不是记忆本身。
记忆要分层,我习惯分成四层:
- 会话记忆:当前对话中已经发生的事情,直接放进上下文,保证前后一致。
- 工作记忆:当前任务运行过程中产生的临时结果,比如查询到的中间数据、上一步的输出。
- 长期记忆:跨会话保留的信息,比如用户偏好、项目背景、历史结论。
- 程序记忆:agent 对“怎么做某事”的经验,比如某个场景下优先使用哪个工具。
不同层级对应不同的技术方案。会话记忆和工作记忆一般靠消息列表和临时变量;长期记忆才考虑向量库或者数据库;程序记忆更复杂,可能要定期把成功案例总结成规则。一上来就做长期记忆,反而是最费力的。
3.2 一场实操:临时记忆和会话记忆怎么落地
我做过一个“会议纪要 agent”,它需要记住用户在会前提过的关注点,并在生成纪要时重点保留。以前我直接傻乎乎地把所有历史消息塞进上下文,结果模型被无关消息干扰,总结抓不住重点。
后来改成这样:用一个历史消息数组保存过去 10 轮对话,用一个临时变量表保存“用户关注点”。在生成纪要前,系统先把关注点提取出来,再和会议记录拼接成新的提示词。这样 agent 不用自己从头翻聊天记录,省 token 又准确。
印象最深的问题在这:用户说“这次别写太多细节,我只关心决策项”,这句话本身是一条即时指令,不能存储为长期偏好,否则下次会议也受影响。所以我处理指令时分成“一次性指令”和“长期偏好”,一次性指令只在当前会话生效,长期偏好才写入记忆库。这个细节看起来简单,但很影响实际体验。
3.3 记忆安全:小心“记忆投毒”和敏感信息泄露
记忆不是存进去就完事。如果你不加控制地把用户输入全部写进长期记忆,后面某次对话里出现的错误信息就可能永久污染 agent 的判断。这也就是所谓“记忆投毒”。更危险的是敏感信息:用户可能在闲聊中透露邮箱、密码、内部项目代号,agent 如果都存下来,以后每次调用都会暴露这些信息,风险极高。
我现在的做法是增加记忆写入审核。系统在把任何内容写入长期记忆前,先做一轮规则校验,关键词规则加上简单的模型分类,把明显敏感、临时、情绪化的信息过滤掉。另外,每次调取长期记忆时,只返回与当前问题相关的片段,不把整个记忆库塞给模型。
研究圈里也有人在专门做这个方向,比如 A-MemGuard 这类针对 LLM agent 记忆的主动防御框架,听起来很学术,但思路可以参考:对记忆的写入和读取都做持续监控,而不是一次性授权。我现在做任何 agent 项目,都会把“记忆安全”列入第一版需求,而不是后续补丁。
4. 让 agent 真正干活的实操过程
4.1 从零到一个能跑的“资料整理小助手”
动手做 agent 最好的方式不是反复看教程,而是找一个边角任务直接跑通。我推荐从“资料整理小助手”开始:它需要读取链接或者上传文件、抽取关键信息、按模板输出摘要,流程完整又不会太难。
整个流程大概分四步:
- 定义工具:写一个
fetch_page获取网页内容,一个summarize_text摘要,一个append_note写入本地笔记。 - 写工具描述:每个工具都要写清楚用途、参数、返回结果。比如
fetch_page的用途是“获取网页正文,去除广告和导航”,参数是url,返回是纯文本。 - 搭循环:先让模型输出工具调用意图,代码解析后执行工具,再把工具结果赋回上下文,直到模型认为任务完成。
- 设计提示词:提示词里说清楚任务背景、可用工具、输出格式,还有遇到异常时该怎么做。
我实际跑的时候,最常遇到的问题是模型把“获取网页失败”当成最终结果直接输出,而不会重试。后来我在系统提示词里补了一句“工具返回错误时,请基于错误信息再尝试一次,或向用户说明具体原因”,效果立刻改善。
4.2 关键参数:temperature、max_steps、tool call 的边界
同样是写一段 agent 循环,参数调不好,结果差很多。先看几个最常见的参数:
temperature控制随机性。工具调用场景建议设置为 0 或接近 0,因为工具调用必须精确,不需要创造性。如果温度太高,模型可能会把函数名拼错,或者给参数加一些不存在的字段。摘要生成阶段可以把温度调到 0.3 左右,让表达更自然。
max_steps控制最大循环次数。没有这个限制,agent 可能在错误路径上反复转圈,直到 token 耗尽。我通常会根据任务复杂度设置,简单任务 5 步以内,复杂任务最多 15 步。超过上限后调用中断逻辑,把已获得的中间结果输出给用户,而不是强制继续。
tool call 边界也很重要。有些 agent 工具能读文件、能写文件、能执行命令,如果不加限制,模型可能做出意料之外的操作。我在工具层加权限标记,比如“只读”“仅当前目录”“需要二次确认”,超出权限的操作直接拒绝。
下面是我常用的一段工具调用解析代码结构,不是完整代码,思路可以参考:
def run_agent(task, tools, max_steps): messages = [{"role": "system", "content": SYSTEM_PROMPT}] messages.append({"role": "user", "content": task}) for step in range(max_steps): response = llm.chat(messages, tools=tools, temperature=0) if response.tool_calls: messages.append(response.message) for call in response.tool_calls: result = execute_tool(call.name, call.arguments) messages.append(format_tool_result(call.id, result)) else: return response.content return "任务步骤过多,已停止"这个循环是 agent 最基本的心脏,框架能帮你封装,但理解它永远不过时。
4.3 agent evals:不评估就别上线
“感觉回答变好了”不叫评估。我个人非常看重 agent evals,也就是给 agent 建一套可重复测试集。没有 evals,你根本说不清某个提示词改动是变好了还是变坏了。
评估可以从三个层面做:单步工具调用是否正确选择工具、参数是否正确;多步任务是否完成目标、流程步骤是否合理;输出质量是否满足用户要求、格式是否规范。第一个层面最容易自动化,后面两个层面需要人工打分或模型打分。
我每个项目都会准备 20 到 50 条测试任务,放在固定数据集里,每次修改代码就跑一遍,记录通过率。比如“资料整理小助手”的测试集包含不同网站、不同长度的文章、异常链接。刚开始通过率可能只有 60%,随着提示词和工具描述优化能稳定到 90% 以上。这个过程很土,但特别有效,比凭感觉调试可靠多了。
还需要注意,eval 不能只测“正常情况”,必须加入异常情况:工具超时、页面无内容、模型输出非法 JSON、记忆为空。这些字段在测试集里占比我一般控制在 20% 左右,用来发现问题。
5. 常见问题与排查技巧实录
5.1 典型报错与排查思路
在 agent 开发中,错误不一定来自代码,还可能来自模型和工具之间的协作。这里列几个我高频遇到的情况:
| 问题 | 原因 | 排查步骤 |
|---|---|---|
| agent 反复调用同一个工具 | 工具返回结果未被模型理解,或步骤判断缺失 | 检查工具描述是否清晰、结果是否太长被截断 |
| 工具参数凭空出现 | 模型幻觉,生成了不存在的字段 | 降低温度、加强 JSON Schema 校验 |
| 任务未完成但直接结束 | 系统提示词没说明“继续执行” | 增加 max_steps,并明确结束条件 |
| 上下文越来越长,费用飙升 | 每轮都灌入全部历史 | 做会话裁剪,只保留关键信息和最近几轮 |
| 多个工具结果互相矛盾 | 中间数据没有汇总和取舍 | 增加一个“结果裁决”步骤,让模型解释选择 |
遇到问题不要只盯代码。先用日志把每次工具调用、每条上下文、每一步模型输出都记录下来。很多时候一看日志就明白:模型根本不是不会,而是被不够清晰的信息带偏了。
5.2 稳定性和可维护性的经验
agent 跑起来不难,难的是长时间稳定。我踩过的几个坑,总结成三条经验:
第一,工具函数必须做防御性处理。你永远不知道模型会传什么参数,工具端要处理缺参数、空结果、超时,否则模型一拿到异常就乱了套。我的每个工具返回值都固定成{ success: true/false, data: ... },这样模型能明确知道调用是否成功。
第二,提示词里最好给模型“退路”。当模型不确定时,允许它说“信息不足,请补充”,而不是硬着头皮猜。这样能显著减少幻觉式工具调用。
第三,定期回归 eval。每换一次模型版本,都要从头跑一遍测试集。同一个 agent,换模型后表现可能差距很大。我以前遇到过换了新模型后工具调用格式变了,整个流程直接崩掉,幸好有 eval 提前发现问题。
5.3 成本控制与迭代节奏
agent 的 token 消耗比普通对话多很多,因为每一轮工具调用都要把中间结果放回上下文。我的建议是:先做小步快跑,不要一开始就追求“无限能力”。
控制成本有几个土办法:限制 max_steps;会话历史只保留最近的 10 条;工具结果做截断或摘要;对长时间运行的任务设置超时中断。还有一个办法是规划时先让模型输出“步骤计划”,再逐步执行,避免它边跑边想,反复横跳。
迭代节奏上,我习惯按“一周一个可演示版本”推进。第一周只做单工具调用,验证模型输出和工具执行闭环;第二周加记忆和 eval;第三周再考虑多 agent 或复杂编排。按这个节奏,每个阶段都有可衡量的结果,不容易陷入“一直在写代码但不知道 agent 是否变聪明”的状态。
最后再分享一个小技巧:把你调通后的 agent 场景写成内部文档,特别是工具描述、典型 prompt 和 eval 结果。这些经验会积累成团队统一的 agent 开发资产,比每次从零开始重新踩坑高效得多。做 agent 方向,最值钱的不是你模型调得多花哨,而是你对自己 agent 的每次决策都有把握,能解释、能评估、能修正。