这几天把个人 Agent 从“能聊天”推进到“真能干活”的状态最大的感受就一句话我以前做的充其量是“带记忆的聊天机器人”现在才勉强算是在做一个真正的 Agent。这篇想把我这几天的取舍、架构思路、代码实现和踩过的坑整理出来给同样在折腾 Agent 开发的朋友一个参考。你要是正准备动手做一个个人 Agent或者已经做了一版但发现它“只会说不回做”这篇文章应该能对你有直接的帮助。我不打算从概念定义开始讲那些东西看文档就够了。我更想聊的是什么才叫“开工”怎么把一个 Agent 从“能对话、能记住你叫什么”推进到“交办一个几步骤的任务它能自己跑完、产出结果、并且出错时能兜底”。这里面有记忆设计的问题有工具调用的细节也有评估和排查的方法论。1. 为什么我不想要一个“记忆型 AI”1.1 从“什么都会”到“什么都不敢交代”先说一个我真实的感受。以前我做一个助手类项目核心卖点是“记忆”——能记住用户的名字、说过的话题、喜欢的中午吃什么。演示的时候确实好看你问它“我喜欢喝什么”它能回答“你之前提过喜欢美式”。但真到要用的时候你让它“帮我安排下周的会议”它就只会给你一段建议文本什么实际动作都完成不了。会议室不会自动订参会人不会自动通知日程不会自动更新。这种体验让人很挫败。你感觉它像那种脑子很好、但手脚完全不动弹的员工。你问什么它都能接上话但你不能给它派活。它就是“记忆型 AI”——记的东西不少行动力等于零。后来我意识到问题出在一个根上它只负责“理解和生成”不负责“感知”和“行动”。1.2 记忆和行动之间的那条沟再往深一层想为什么“有记忆”离“能干活”还差那么远因为一个能干活的任务通常不是一句话问答而是一条动作链。比如“把昨天的销售数据整理成周报发到群里”这里包含读取数据、汇总计算、生成文案、发送消息四个步骤。每一步都要和外部环境打交道读文件、查数据库、调用 IM 接口。纯对话系统没有这些接口它只能靠大模型“脑补”一份并不存在的销售数据。Agent 解决的就是这个缺口给模型装上工具让它能真正操作文件、调用 API、执行命令再根据执行结果继续推理。但这只是第一步。真正把一个任务交给它跑起来你还要处理任务怎么拆解、步骤之间怎么衔接、某一步失败了怎么调整、整个流程怎么保证不跑偏。这也是为什么“加几个工具”不等于“ Agent 能开工”。这几天我花掉的大部分时间都在填这条沟。2. 架构设计Agent 要能开工这几个模块缺一不可2.1 我理解的 Agent 四件套个人 Agent 想达到“可以开工”的状态不能只靠一个模型反复调用。我最后收敛下来的架构是四个模块互相配合记忆模块负责短期上下文、用户画像、任务进度这类状态的存取。工具模块把文件读写、搜索、API 调用等外部能力封装成模型能理解和调用的接口。规划与执行模块把大任务拆成小步骤用循环机制驱动模型一步步执行。评估与兜底模块负责结果校验、错误分类、超时保护以及关键时刻的人工接管。这个结构可以拿“开一个项目组”来类比。大模型像那个有创造力但偶尔发挥不稳定的核心成员记忆模块是项目档案库负责把历史信息归档和调出工具模块是它能用的办公设备和对外接口规划与执行模块是项目经理排的作战计划评估与兜底模块则是质检员和紧急预案。2.2 为什么先跑通闭环而不是一开始就谈“自主决策”刚开始我也犯过这个毛病一上来就想做一个“全自主 Agent”觉得它能自己定目标、自己拆任务、自己调用一切。结果不到半天就翻车了。因为没有环境约束、没有场景边界模型会在各种奇怪的方向上自由发挥。比如我让它“优化我的工作流”它开始尝试删除一个自己看不懂的缓存目录——在测试环境里也就罢了生产环境这种动作就是事故。这里我想分享一个原则先跑通小闭环再谈自主性。具体做法是选一个封闭场景固定工具集、限制最大步数、划定允许操作的范围先把成功率做到九成以上。等这个场景稳定了再逐步放开自主度。市面上的框架再怎么包装底层逻辑都是这个只是有的框架把“开放”做得更激进。对我们自己做个人 Agent 来说克制才是第一位的。2.3 技术选型先看“函数调用”能力再看花哨功能技术选型这块我踩过一轮才明白该怎么选。首先要看的不是框架多炫而是底模有没有原生函数调用function calling能力。工具调用要稳定模型必须能按标准格式输出结构化调用请求。没有这个能力做底子后面加再多工具都会在“参数对不对、格式准不准”上反复出问题。框架层面我一开始试了现成的 Agent 框架确实能快速跑起来但到后期反而被“编排逻辑”裹住了。想插入一个自定义检查逻辑要绕好几层抽象调试一个执行中断日志又堆了一大堆。后来我干脆自己写了一个精简的调度循环整个核心代码不到两百行。用框架还是手写我的判断很简单原型阶段可以用框架进入稳定场景后如果你发现框架在限制你那就果断抽出来自己写。3. 记忆模块怎么设计记什么、怎么记、何时清3.1 三层记忆结构别把所有东西都塞进上下文记忆设计的主要坑是把所有东西都堆进对话上下文。上下文窗口再大也是有限的塞得越满模型越抓不住重点成本也越高。我最后用的是三层记忆结构工作记忆Working Memory只放当前任务相关的临时状态比如“正在处理周报、已经读完了待办文件”。任务结束后直接清空。用户画像记忆Profile Memory放长期稳定的偏好信息比如“默认使用中文、喜欢简洁周报模板、汇报对象是某某”。项目/任务记忆Task Memory放某个进行中项目的背景、约束、历史决策方便跨会话复用。三个层级的存储介质也不一样。工作记忆直接放内存或临时文件用户画像放在 SQLite 或者 JSON 文件里项目记忆可能涉及比较多检索需求才考虑用向量数据库。很多人一上来就配向量库其实早期完全没必要。记忆层级典型内容存储介质生命周期工作记忆当前任务的中间状态、已读文件内容内存 / 临时文件任务结束即清理用户画像姓名、偏好、常用格式SQLite / JSON长期保留项目/任务记忆项目目标、约束、关键决策JSON / 向量库跨会话保留3.2 写入、提取和清理策略记忆这东西写多了乱写少了废。我采用的策略是不是所有对话都值得写入长期记忆。用户明确说“以后都用这个模板”这类指令才写入用户画像每次任务结束后把对下一次执行有帮助的经验沉淀到项目记忆里比如“这个数据源经常超时需要重试两次”。提取侧我采用“按需抽取”会话开始时把用户画像的关键摘要注入系统提示词任务执行过程中根据当前步骤的关键词去检索项目记忆。检索结果不需要多三到五条能影响决策就够了。清理也很重要我会在每个任务结束后做一次整理把过期状态删掉每周自动压缩一轮历史记录只保留摘要和关键结论。3.3 记忆污染比忘事更可怕分享一个我踩过的坑。有一次我让 Agent 记住“默认周报模板改成简洁版”结果它把“简洁版”理解成“周报只保留标题和一句话”导致后续每次生成周报都缺核心内容。当时我还没意识到是记忆写错了以为是提示词有问题排查了好几个小时。这件事让我深刻地意识到记忆污染比忘事可怕得多。忘事顶多重新说一遍写错了它会在后面每个任务里反复犯错污染所有依赖这份记忆的判断。所以现在我对长期记忆的写入动作非常谨慎。凡是不可轻易撤销的信息写入之前都要有一道校验。我在代码里加了个机制重要记忆写入时先把候选记忆交给模型自己做个“价值判断题”再弹给用户确认一秒钟有异议就打回。这个步骤看着笨重但避免了后面一连串错误。4. 工具调用与行动闭环从“只会说”到“能动手”4.1 一个工具的本质描述、参数、执行对模型来说一个工具并不是一个函数而是一份说明文档。它的核心是名字、描述、参数 Schema 三件套。描述要讲清楚“这个工具是干什么的、什么情况下用”参数 Schema 要严格定义参数名、类型、是否必填、可选项。描述写得越清晰模型误调用的概率越低Schema 定义得越严格传参错误就越少。比如我注册一个“读取本地待办文件”的工具大概长这样{ name: read_todo_list, description: 读取本地 todo.md 文件中的待办事项列表适合在生成周报或总结工作前调用, parameters: { type: object, properties: { path: {type: string, description: 待办文件路径默认 todo.md} }, required: [] } }这里有个容易被忽略的重点描述里要写“适合在什么时候调用”。模型不会主动理解业务它只会根据描述做匹配。如果你把描述写得过于宽泛它可能任何任务都想去调这个工具结果就是行为失控。4.2 工具调用的完整数据流与核心循环工具调用的完整链路我用 ReAct 模式来理解推理Reason→ 行动Act→ 观察Observe→ 再推理。模型先根据当前信息判断需要调用什么工具然后输出一个结构化的调用请求调度器拿到请求后实际执行函数执行结果以消息形式回传给模型模型再基于真实结果继续推理直到不需要调用工具为止。下面是我写的精简 Agent 核心循环不是全部代码但把关键流程体现出来了def run_agent(user_request, tools, max_steps10): messages [{role: user, content: user_request}] for step in range(max_steps): response llm.chat(messagesmessages, toolstools) if not response.tool_calls: # 模型没有调用工具直接返回最终回答 return response.content # 先把模型的调用请求加入消息历史 messages.append(response.message) # 逐个执行工具调用 for call in response.tool_calls: result execute_tool(call.function.name, call.function.arguments) # 把工具执行结果作为 tool 消息回传 messages.append({ role: tool, tool_call_id: call.id, content: result }) raise TimeoutError(fAgent 超过最大步数 {max_steps}已终止循环)这个循环里最关键的一点是把工具执行结果原样作为一条tool消息回传。模型下一步的推理必须基于真实执行结果而不是自己脑补。有一次我忘了加这条消息Agent 全程在编造“文件读取成功”我看了半天日志才反应过来是消息传递断掉了。4.3 工具权限给得太宽会出事工具权限这块我想单独拎出来讲因为这是最容易出事但又最容易被忽略的地方。个人 Agent 不像企业级系统有人天天盯着审计很多工具一旦放开就是裸奔状态。我自己有一次调试给 Agent 开放了“删除文件”的能力它在一个临时目录里把一个看起来像“缓存”的文件删了幸好是测试环境不然就酿成事故了。我把工具按风险分了三级只读类工具比如读取文件、搜索、查询列表可以自动执行。改动类工具比如写入文件、修改配置、删除条目需要人工审批。外部副作用类工具比如发邮件、发通知、执行支付默认禁止除非显式开启并做二次确认。这个分级不需要很复杂实现起来就是在工具定义里加一个risk_level字段调度器在执行前判断一下该打停就打停。除此之外我还会在每次工具调用后记录完整日志包括时间、工具名、参数、返回内容和消耗的 token 数。日志不是为了好看是为了后面排查问题时有据可查。5. 实操记录用一个真实任务把它推到“可开工”5.1 选对第一个场景很重要理论讲了一堆真正让 Agent “可开工”的是我选对了一个场景自动整理周报。选这个场景有四个原因第一规则清楚一个周报有固定模板第二它是我日常高频重复劳动第三可以明确检查产出物是否合格第四就算跑错了也不会有灾难性后果。任务拆解下来是这样读取本周的待办事项文件读取项目记录里的进展和状态按照模板生成周报文字把周报写入指定目录的新文档。这四步已经足够检验整个 Agent 闭环了。既有文件读取工具也有文本生成还有写入动作中间还涉及步骤之间的承接。我当时没有给它几十个工具就给了三个读待办、读项目记录、写文档。这个状态就是“最小可用闭环”。5.2 核心代码与配置示例调度循环已经在前文给出这里展示一下工具注册的过程。我用的是装饰器方式让每个工具函数自动转成模型可识别的 JSON Schematool_registry [] def register_tool(name, description, parameters_schema): def decorator(func): tool_registry.append({ name: name, description: description, parameters: parameters_schema, func: func }) return func return decorator register_tool( nameread_file, description读取指定路径的文本文件适合读取待办和项目记录, parameters_schema{ type: object, properties: { path: {type: string, description: 文件路径} }, required: [path] } ) def read_file(path): try: with open(path, r, encodingutf-8) as f: content f.read() return f文件内容如下\n{content} except FileNotFoundError: return 错误文件不存在请检查路径这里有个实操细节工具函数的错误处理一定要“温和”。宁可返回错误字符串也不要抛异常。因为一旦抛异常调度循环可能直接中断Agent 就没有机会根据错误信息调整下一步。让模型看到“文件不存在”这样的提示它可能会换成正确的路径或者向用户求助这比整个流程崩溃好太多。5.3 怎么评估它真的能干活做个小的 Agent Evals做 Agent 最容易陷入的误区是“看起来能跑就算成功”。我这次特意加了一个很轻量的评测方案准备 10 个不同团队的周报任务每个任务有不同的待办文件、不同的项目记录、不同的输出目录。跑完后人工检查三件事文件是否生成、内容是否包含该团队的真实待办、格式是否符合模板标准。不能有任何误操作。我跑了两轮第一轮成功率只有 60%。失败集中在两个地方一是模型偶尔会把“待办事项”当成“已完成事项”写进周报判断依据是文件里“待办”两个字而不是“完成状态”二是写入路径偶尔会编造一个不存在的目录。这两个问题没有一个是模型“变笨”导致的全是工具上下文信息给得不够明确。我把两个工具的 description 分别改成了“读取待办事项文件注意区分待办和已完成项输出中请标注状态”和“写入周报文件目标目录不允许自动创建目录不存在时应报错”成功率立刻提升到 90%。这次经历让我更确信Agent 能不能稳定干活很多时候不是模型的问题而是你对工具的“使用说明”写得好不好。6. 这几天踩过的坑6.1 按现象整理的问题速查表我把这几天最典型的几个问题整理成一张表方便你以后遇到了能直接对着排查现象根本原因解决方案工具调用提示参数格式错误模型传参类型和 Schema 不匹配在 Schema 里严格声明类型并在描述中写明示例Agent 反复调用同一个工具不推进循环缺少终止条件或结果没变化设置最大步数增加“无变化检测”关键工具结果被模型忽略上下文过长早期消息被淹没任务中途压缩历史只保留关键摘要记忆里的错误信息影响后续任务写入策略太激进没做校验重要记忆写入前加到人工确认工具执行环境不一致导致路径错误本地环境、容器环境配置不同用固定工作目录写成配置文件统一注入Agent 自己编造文件内容工具执行结果没正确回传确认 tool 消息完整原样传递结果6.2 我的排查三板斧遇到 Agent 行为异常我的排查顺序固定有三步效率很高。第一日志优先。先看消息历史和工具调用记录确定问题出在哪个环节。是在模型判断阶段选错了工具还是在执行阶段参数不对或者是结果回传阶段消息丢了这一步能排除八成的问题。我强烈建议从一开始就给 Agent 加上日志输出每轮记录模型输入、工具调用、返回结果、耗时和 token 消耗。没有日志排查 Agent 问题就像闭着眼修电路。第二最小复现。把任务尽量简化比如只保留一个工具、一个步骤看它能不能跑对。如果简化后跑对了再逐步增加复杂度往往能很快定位到是哪个环节引入了偏差。这一步对“记忆污染”类问题特别有效。第三人工走查。我会把整个任务手动模拟一遍把自己当成 Agent读待办、读项目记录、生成周报、写文件按同样顺序走一遍。走查时特别留意哪一步信息不完整、哪个描述会让我产生歧义那里往往就是模型也会犯错的点。这个“人肉模拟”看起来很笨但却是发现设计缺陷最快的方法。我最后再分享一点经验个人 Agent 能不能“开工”靠的不是模型的智商突飞猛进而是你能不能把记忆、工具、评估这些外围系统做扎实。模型负责聪明你负责让它靠谱。先找一个你每天都在做、又非常规则的小任务把闭环跑通再慢慢加功能这是我目前觉得最稳的推进方式。希望这篇能让你少踩几个坑。