上次清理旧项目时,我翻出一个当时觉得特别得意的对话机器人。它能记住用户三天前说过喜欢什么口味的咖啡,连上周聊到一半的电影都能接上话。但当我让它把桌面上一个 CSV 按规则清洗后发到我邮箱时,它卡住了,只回了一句“我可以帮你,但我没有访问你桌面的权限”。那一刻我特别清醒:我一直在做的是典型的“记忆型 AI”——它记性越好,越像一个只会复读备忘录的聊伴,离“能开工”十万八千里。于是我们花几天时间,把手上一个个人 Agent 从“能聊”改造成了“能干活”。这篇文章想把这段过程完整记录下来:为什么记忆不是 Agent 的核心、架构上做了哪些取舍、实操中怎么落地工具和记忆,以及新手最容易掉进去的坑。
1. 为什么“记忆型 AI”撑不起一个能开工的个人 Agent
1.1 “记性好”不等于“会办事”
市面上很多号称 AI 助理的产品,本质上就是“大模型 + 聊天记录 + 向量数据库”。你问它“我上周提过的项目 deadline 是什么”,它可以准确回答;但你说“帮我把上周那个项目的周报按模板写好,再发到群里”,它就不知道怎么下手了。
原因在于,记忆型 AI 的产品模型是“对话式信息管理”:接收信息、储存信息、检索信息、生成文本。这个链条里缺少一个关键环节——动作。动作意味着改变外部世界:写文件、调接口、发请求、执行命令、决策下一步做什么。如果没有动作能力,记忆再丰富也只是一堆没人调用的档案。
我把个人 Agent 的“能开工”重新定义为三个层次:
- 能接收一个模糊目标,并将其拆成可执行的小步骤;
- 每一步都能调用对应工具,并根据工具返回结果调整后续动作;
- 偶发失败时能自己重试或明确求助,而不是卡死在同一句话上。
对照这个标准,我之前做的“记忆型 AI”连第一个层次都过不了。它更像一个擅长对答案的百科全书,而不是能动手解决问题的实习生。
1.2 记忆是手段,不是目的
很多团队做 Agent 时容易本末倒置,一上来就花大量时间搭知识库、做向量检索、设计 embedding 流程。这些工作当然有价值,但必须清楚:记忆服务于行动,不是为了让人感叹“它居然记得”。
打个比方。你招一个实习生,真正关心的是他能不能把事情办好。他记忆力好当然加分,但如果他只会背制度流程、不会实际操作,你一样会把他退回人力资源部。Agent 也是一样的逻辑:用户感知的是“任务有没有完成”“过程是不是顺畅”“出错能不能补救”,而不是“向量库里存了多少历史记录”。
所以在后续开发中,我把精力从“如何记得更多”转移到“如何用得更准”:先解决工具调用和任务编排,再把记忆作为行动后的反馈沉淀机制。这个顺序调转之后,Agent 的可用性提升非常明显。
2. 整体设计:先有工具链,再谈 Agent 架构
2.1 主循环拆解:感知、决策、行动、观察
一个能开工的 Agent,无论用 LangGraph、AutoGPT 还是自研框架,底层都逃不开一个主循环。我习惯把它简化成四个步骤:
- 感知:收集当前任务状态、用户原始指令、工具返回的信息;
- 决策:让大模型根据现状给出下一步动作;
- 行动:执行具体工具,比如读文件、调用 API、发邮件;
- 观察:把工具执行结果放回上下文,供下一轮决策使用。
这一步最简单的最小代码骨架大概是这样的:
context = [{"role": "user", "content": user_task}] while not finished: response = llm.chat(context) if response.action == "call_tool": result = run_tool(response.tool_name, response.args) context.append(response.message) context.append({"role": "tool", "content": result}) elif response.action == "finish": finished = True else: context.append({"role": "assistant", "content": response.answer})实际生产里,我会用状态图替代 while 循环,给 Agent 增加明确的开始、暂停、重试、终止节点。但核心思想完全一致:Agent 的每次决策都必须依赖“刚刚发生了什么”,而不是只依赖模型大脑里的预训练知识。
2.2 工具层:没有工具的 Agent 只是嘴强王者
如果说大模型是 Agent 的“大脑”,工具层就是它的“手脚”。我见过不少项目把模型换到 GPT-4o、Claude 甚至更强的新模型,但效果依然感人,原因就是工具层太薄弱。
工具设计我遵循四个原则:
- 一个工具只做一件事。不要把“处理文件”做成十大功能聚合,拆成读写、压缩、格式化反而更容易被模型调用。
- 描述要写清触发条件。模型通过工具描述决定何时调用,描述模糊等于让模型瞎猜。
- 参数必须结构化。用 JSON Schema 定义参数,而不是让模型自由发挥。
- 返回结果要能被消费。工具尽量返回结构化的文本或 JSON,减少模型二次解析的开销。
最开始我给 Agent 配的工具集很小,只有六个:读取文件、写入文件、请求 URL、执行简单脚本、发送邮件、查询本地日历。这六个工具已经能覆盖大量“个人助理”场景。后面根据需要逐步加工具,每加一个都要先在测试任务上人工验证三轮,再开放给 Agent 自动调用。
2.3 记忆分层:不要把一切塞进同一个向量库
早期我犯过一个特别蠢的错误:把用户所有聊天记录、文件内容、任务历史全部切块塞进向量库,以为这样 Agent 就“什么都知道”。结果它确实什么都知道一点,但每样都记不准,检索出来的内容经常互相矛盾。
后来参考认知科学里的工作记忆和长期记忆概念,重新把记忆拆成了三个层次:
| 记忆类型 | 存什么 | 什么时候写入 | 什么时候读取 |
|---|---|---|---|
| 工作记忆 | 当前任务的临时上下文、工具返回 | 每轮实时更新 | 每次模型推理时 |
| 情景记忆 | 之前完成过的任务记录和结果 | 任务结束后 | 新任务开始前 |
| 技能记忆 | 可复用的操作流程、指令模板 | 任务成功复盘后 | 发现同类任务时 |
工作记忆直接拼在 prompt 里,不需要存数据库;情景记忆和技能记忆则落地成文件或结构化记录。这个改动的关键点在于:长期记忆不是“越全越好”,而是“越可复用越好”。我只存任务成功之后的经验,失败的过程只保留摘要用于排查,不去占用宝贵的检索空间。
3. 实操:把普通 API 包装成能开工的个人 Agent
3.1 环境准备与最小工具集
这次实践没有用特别重的框架,技术栈只有三样:Python、LLM 接口、少量工具函数。如果你想复现,环境准备工作其实很少:
pip install openai pydantic requests mkdir -p ~/.agent_memory/skills mkdir -p ~/.agent_memory/episodes mkdir -p ~/.agent_memory/preferences然后定义工具。以“读取文件”为例,一个工具在代码里往往是这样:
def read_file(path: str, max_lines: int = 100): if not os.path.exists(path): return {"error": f"文件不存在: {path}"} with open(path, "r", encoding="utf-8") as f: lines = f.readlines()[:max_lines] return {"content": "".join(lines)} TOOLS = { "read_file": { "name": "read_file", "description": "读取本地文本文件前 N 行,适合查看配置、代码或日志。", "parameters": { "type": "object", "properties": { "path": {"type": "string"}, "max_lines": {"type": "integer", "default": 100} }, "required": ["path"] }, "function": read_file, } }这里特别要注意函数实现里的防御逻辑:文件不存在、权限不足、编码错误都要返回清晰的错误信息,而不是抛异常。Agent 看到异常信息时还能自救,看到“Process crashed”这种信息就只能卡死。
3.2 让 Agent 学会规划:任务清单模式的提示词设计
给模型加工具之后,最常出现的问题是:它跳过规划,直接尝试“一步到位”,然后持续出错。比如让它整理一个文件夹里的图片,它不会“先列出目录,再读取文件信息,再决定压缩策略”,而是直接编造一个成功结果。
所以我改进了系统提示词,强制要求 Agent 先输出任务清单:
你是个人助理 Agent。每当收到一个目标,先输出 plan 列表。 plan 的每一项都必须对应一个可用工具,格式为: 1. 工具名: 要做什么 如果某一步当前没有工具支持,明确写“需要人工处理”。 执行完一项后,基于实际返回结果决定是继续还是调整计划。这个提示词的关键不是让模型表现得更聪明,而是给它的行为加护栏。相当于给实习生一份工作清单,让他每一步都跟你对齐,而不是任他自由发挥。实践下来,光是这一步,就让任务完成率提高了非常多——因为模型一旦开始“假装成功”,你根本没法追踪问题在哪。
3.3 校验、确认与超时:从能跑变成可靠
个人 Agent 能“跑通 demo”和能“稳定开工”之间的差距,主要差在工程细节上。我补了三块保障机制:
- 参数校验。模型生成的工具参数经常缺字段、类型不对。我在执行任何工具前先用 JSON Schema 校验,不通过就把错误信息返回给模型,让它重新生成参数。这比在工具函数内部抛出异常要友好得多。
- 危险操作确认。涉及发邮件、删除文件、执行 Shell 命令这些动作,我会让 Agent 先输出“操作预览”并暂停,由我确认后继续。这个设计一开始有点繁琐,但很有必要——我见过不止一次 Agent 把测试邮件发给了真实客户。
- 超时与重试。每个工具调用都设了最长执行时间,超时后返回超时错误;同时限制整个 Agent 任务的最大步数,防止模型陷入死循环。
这三块看起来不起眼,却是从“偶尔成功”迈向“可靠交付”的分水岭。没有它们,Agent 就像一辆没有刹车的车,快是快,但不敢上路。
3.4 记忆落地:用 Markdown 文件代替数据库
记忆系统我选择先用最朴素的方案:文件系统。
在~/.agent_memory/skills/下,每个技能存成一个 Markdown 文件,内容是“任务类型 + 操作步骤 + 注意事项”。比如整理下载目录这个任务,技能文件大概长这样:
# 整理下载目录 ## 适用场景 用户说“把下载目录整理一下”或“分类一下文件” ## 操作步骤 1. 使用 list_dir 列出下载目录 2. 按文件扩展名分类,常见类型:图片、文档、压缩包、安装包 3. 不认识的扩展名归入“其他” 4. 移动前先打印计划,请用户确认 ## 注意事项 - 不要移动正在使用的文件 - 不要移动隐藏文件任务结束后,我用一个专门的“记忆抽取 Prompt”把本次成功经验浓缩成 200 字以内的条目,追加到对应技能文件中。下一次遇到同类任务,Agent 会在开始前先读取相关技能文件,再决定执行步骤。
这个方法最大的好处是透明、易调试。记忆文件是纯文本,我可以直接看到 Agent 学到了什么;出了问题也能立刻定位是哪条记忆害的。用向量库虽然检索更强,但出了问题很难查。
4. 效果复盘:连续“开工”三小时的实验记录
4.1 测试任务与成功率对比
为了验证改造效果,我准备了三类任务,分别对比“纯聊天模型”“记忆型 AI(只加记忆不接工具)”“行动型 Agent(工具 + 规划 + 记忆)”的表现:
| 测试任务 | 纯聊天 | 记忆型 AI | 行动型 Agent |
|---|---|---|---|
| 抓取网页表格并整理成 Excel 发邮箱 | 无法完成 | 输出操作步骤建议,没有实际交付物 | 一次成功上传、发送 |
| 按规则压缩目录里的图片到指定尺寸 | 无法完成 | 自己编造了“已压缩”的假结果 | 稳定完成,且保存了压缩日志 |
| 根据模板生成周报并同步到项目 API | 无法完成 | 只生成文本,需要人工复制粘贴 | 调用 API 成功,并自动校验返回 ID |
第二列“记忆型 AI”的表现特别值得警惕:它比纯聊天看起来更可信,因为它会引用“我记得你说过”,但一旦涉及真实操作,它更倾向于编造成功。这个现象是推动我彻底转向行动导向的直接原因。
4.2 三个让我意外的地方
在整个改造过程中,有几个结果完全超出了我的预期。
第一个意外是,工具返回结果的结构化程度比模型本身更能影响成功率。同样一个模型,当工具返回的是乱七八糟的文本时,它经常错误解析;当我让工具统一返回 JSON 格式后,几乎不需要增加任何 prompt 成本,成功率就明显上升了。
第二个意外是,给模型看过往成功案例后,纠错次数显著降低。纯粹的提示词工程能做到的改进有限,但给它一个“上次你是怎么做成的”参考,它能很快复刻正确路径。这可能是因为技能记忆提供了稳定的行为锚点,减少了模型的自由发挥空间。
第三个意外是,大多数失败发生在工具链边界,而不是模型推理本身。参数传错、文件路径不存在、外部 API 限流,这些工程层面的问题占了失败总数的八成。换句话说,Agent 能不能开工,很多时候取决于你对工具的打磨程度,取决于你选择什么模型。
4.3 现在能开几种“工”
经过几天的调整,我现在这个 Agent 已经能稳定处理几种实际工作:
- 每天早上抓取几个固定网站的新文章,汇总成摘要发到邮箱;
- 把一个文件夹里的图片批量压缩到指定尺寸,并按规则重命名;
- 把会议纪要里的待办事项解析出来,同步到项目管理接口;
- 根据指定的 Excel 模板,把零散数据填进去生成报表。
需要说明的是,它目前还是“半自动”状态。遇到需要登录第三方系统、识别验证码这类高复杂度操作,它会主动停下来告诉我“需要人工介入”。我认为这不是缺陷,反而是可靠的表现——知道自己做不到比硬着头皮假装成功更重要。
5. 常见问题与排查技巧实录
5.1 为什么 Agent 会在同一个错误上反复打转
这是最常被吐槽的问题:Agent 反复调用同一个工具,拿到同样的错误,然后继续尝试。原因通常是模型没把工具返回结果真正吸收进上下文,而是基于自己的猜测继续行动。
我的排查方法是在循环里加上“最近一次工具返回摘要”。如果模型连续三次对同样的工具返回同样的参数,就让主循环强制切换策略——要么修改参数,要么换工具,要么直接要求人工介入。同时设置最大步数限制,比如 20 步,超过就自动终止并保存诊断日志。
5.2 工具参数幻觉:模型编造不存在的文件路径
模型很会“一本正经地说瞎话”。让它读文件时,它能给出一个看起来合理但其实不存在的路径。原因在于模型没有实时访问文件系统的能力,只能根据训练数据猜测路径结构。
这个问题没有完全消除的办法,只能靠工具层兜底。我的工具实现会先检查路径存在性,并把“文件不存在 + 当前目录下最相似的文件名列表”返回给模型。这样模型通常能在下一轮修正路径。这个技巧比单纯报错有用得多。
5.3 记忆污染:旧经验误导新任务
记忆系统上线后出现了新问题:Agent 把上一次任务的经验生搬硬套到新任务里。比如它上次用某个脚本处理了图片压缩,这次遇到文档转换时也尝试运行同一个脚本,结果当然是失败。
对策是给每条记忆加“适用场景”字段,并在检索时做双重过滤:先匹配任务类型关键词,再对比记忆里的样例任务描述与当前任务描述的相似度。我还会给记忆加上时间戳和来源标签,超过一定时间没有被复用的记忆会自动降权,避免过期经验干扰新任务。
5.4 上下文窗口很快被塞满
个人 Agent 长时间运行时,工作记忆会不断膨胀——工具返回、历史对话、中间结果全部堆在 prompt 里,很快就把上下文窗口占满,既费钱又影响推理质量。
解决方案是分层压缩。工具返回结果在执行后先做摘要,只保留结论性信息;超过 N 轮的历史对话裁剪掉细节,只保留当前任务目标和最近一轮关键决策;长期记忆则放在外部存储,按需读取,不进入基础上下文。
把这个机制做好之后,Agent 连续工作三小时也不会出现上下文超限的问题。
5.5 怎么避免它干出危险事
个人 Agent 一旦接了发邮件、执行命令这类工具,就绕不开安全问题。我的原则是“最小权限 + 关键操作确认”。
具体做法是:跑命令前先在沙箱环境模拟;发送外部消息前强制打印完整内容预览;涉及文件删除一律先移动到回收站而不是直接删除;每一步操作写日志,方便事后回溯。更重要的是,我把工具权限和用户确认机制绑定——Agent 可以提议,但“谁能真正执行”始终掌握在人手里。
6. 一些操作体会和后续的扩展方向
这次改造给我最大的启发是:个人 Agent 能不能“开工”,不取决于模型智商有多高,而取决于你有没有把工程边界划清楚。工具层要可靠,规划层要有约束,记忆层要服务行动,三层缺一不可。
我现在养成了一个习惯:把 Agent 当成新来的实习生带。给它明确的工作清单,要求它每一步汇报结果,犯了错就复盘并把经验写进技能库。这套管理方式听上去不像做 AI,但效果比换更大参数的模型还要明显。
后续我打算给这个 Agent 加两样东西。一是事件触发机制,让它不是被动等指令,而是根据日历、邮件、监控任务主动开工;二是更细粒度的权限控制,把工具使用范围进一步收敛,做到“越权必须停”。这样才能让它从一个“能干活”的助手,变成一个“值得放心交活”的搭档。