ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openclaw平替之nanobot 源码解析(四):Markdown 驱动的记忆系统与 TaoToken 配置骨架

openclaw平替之nanobot 源码解析(四):Markdown 驱动的记忆系统与 TaoToken 配置骨架 1. 为什么我要把 nanobot 的记忆系统拆开看nanobot 是一个用 Markdown 文件承载 Agent 记忆的轻量框架它把长期事实写进MEMORY.md把对话流水写进HISTORY.md再用大模型自己总结、自己更新。适合谁适合那些不想为了一个个人助手去部署向量数据库、又希望随时能打开文件改记忆的开发者。我最初关注它是因为 openclaw 那套方案对个人项目偏重而 nanobot 的“纯文件 LLM 自我固化”路线看起来更可控。但真正落地时会遇到两个问题第一记忆读写、检索、注入这条链路到底在源码里怎么串起来的光看文档说不清第二模型调用需要统一的 Key 和 API 通道否则本地跑通记忆系统后接模型这一步又会卡住。这篇就按源码顺序拆记忆系统再给出一套 TaoToken 的配置骨架让你在本地复现后能直接完成接入自检。核心检索词先摆出来nanobot 记忆系统是什么它是用 Markdown 文件做持久化、用大模型做记忆固化的 Agent 记忆方案。能做什么能读写长期事实、追加历史日志、按阈值自动固化。适合谁适合个人开发者、极客、想研究 Agent 记忆机制的人。2. TaoToken 前置统一 Key 与 API 通道在拆源码之前先把模型通道准备好。nanobot 的记忆固化本质上是一次带 tool schema 的模型调用所以你需要一个能稳定返回结构化工具调用的 API 入口。TaoToken 提供统一的 Key 和 API 通道官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。你需要先拿到 API Key入口在控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。拿到之后nanobot 的模型调用就可以指向这个统一通道不用在多个厂商之间来回切换配置。这里要区分两个概念TaoToken 是模型调用的统一入口不是编辑器替代品也不做任何灰色中转。它的作用是让你在 nanobot 里配置一次 base_url 和 api_key记忆固化、对话、工具调用都走同一条通道。如果你只是想先验证模型能不能正常返回工具调用参数可以直接用模型对话页面试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。长期跑编码或 Agent 任务的话Coding Plan 更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。3. 可复制配置config.toml 与 settings.json 骨架nanobot 的配置分两层config.toml管模型通道settings.json管运行时行为。下面这套骨架可以直接抄改掉 Key 就能用。3.1 config.toml 模型通道配置# ~/.nanobot/config.toml [llm] provider openai-compatible base_url https://taotoken.net/api api_key sk-你的TaoTokenKey model claude-sonnet-4-20250514 timeout 60 max_retries 2 [memory] # 记忆固化阈值达到该数量触发异步固化 memory_window 100 # 记忆文件根目录 workspace ~/.nanobot/workspace # 是否在 /new 时强制固化 consolidate_on_new truebase_url指向 TaoToken 的 API 基址api_key填控制台拿到的 Key。memory_window对应源码里unconsolidated memory_window的判断默认 100本地调试可以调到 10 方便观察固化触发。3.2 settings.json 运行时行为{ agent: { name: nanobot, system_prompt_path: ~/.nanobot/prompts/system.md, memory: { memory_file: ~/.nanobot/workspace/memory/MEMORY.md, history_file: ~/.nanobot/workspace/memory/HISTORY.md, session_dir: ~/.nanobot/workspace/sessions, consolidation: { enabled: true, tool_name: save_memory, history_entry_min_sentences: 2, history_entry_max_sentences: 5 } } }, tools: { read_file: true, edit_file: true, save_memory: true } }memory_file和history_file对应源码里MEMORY.md与HISTORY.md的路径。consolidation.tool_name就是_SAVE_MEMORY_TOOL里定义的工具名必须和源码一致否则模型返回的 tool call 解析不到。3.3 记忆文件初始模板MEMORY.md建议用固定小节方便模型合并时定位# Long-term Memory This file stores important information that should persist across sessions. ## User Information (Important facts about the user) ## Preferences (User preferences learned over time) ## Project Context (Information about ongoing projects) ## Important Notes (Things to remember) --- *This file is automatically updated by nanobot when important information should be remembered.*HISTORY.md初始留空即可固化时按[YYYY-MM-DD HH:MM]前缀追加。4. 源码链路记忆读写、检索与注入4.1 两层架构的读写入口nanobot/agent/memory.py里MEMORY.md是长期事实层HISTORY.md是搜索日志层。读的时候read_file工具直接读MEMORY.md全文注入上下文写的时候edit_file工具按小节做增量更新。HISTORY.md只追加不覆盖靠 grep 做关键词检索。这里有个设计细节MEMORY.md的更新不是追加而是让模型返回“完整更新后的 markdown”源码里memory_update字段的描述就是Full updated long-term memory as markdown。这意味着每次固化模型都要把旧事实和新事实合并成一份完整文件旧事实有误就修正没新东西就原样返回。4.2 主动触发模型自己决定记什么当模型判断当前对话出现需要长期记忆的信息它会先调read_file读MEMORY.md再调edit_file写入。以“我喜欢王楚然”为例执行流程是第一步模型调read_file读取MEMORY.md确认当前记忆状态。第二步模型调edit_file把- Likes Wang Churan (王楚然)写进## Preferences小节。第三步模型回复用户确认。变更对比BEFORE: ## Preferences (User preferences learned over time) AFTER: ## Preferences - Likes Wang Churan (王楚然) (User preferences learned over time)4.3 条件触发记忆固化 Consolidation固化有两个触发条件。第一个是消息阈值在nanobot/agent/loop.py的_process_message里unconsolidated len(session.messages) - session.last_consolidated if unconsolidated memory_window and not consolidation_in_progress: start_async_consolidation(session)第二个是手动/new命令强制对未固化消息做归档式固化再清空当前会话。固化流程本身很“元”nanobot 启动一个专门的记忆固化 Agent给它系统提示词和用户提示词。系统提示词是You are a memory consolidation agent. Call the save_memory tool with your consolidation of the conversation.用户提示词把当前MEMORY.md内容和待处理对话拼在一起明确要求调用save_memory工具并给出history_entry和memory_update两个参数。4.4 save_memory 工具定义_SAVE_MEMORY_TOOL [ { type: function, function: { name: save_memory, description: Save the memory consolidation result to persistent storage., parameters: { type: object, properties: { history_entry: { type: string, description: A paragraph (2-5 sentences) summarizing key events/decisions/topics. Start with [YYYY-MM-DD HH:MM]. Include detail useful for grep search., }, memory_update: { type: string, description: Full updated long-term memory as markdown. Include all existing facts plus new ones. Return unchanged if nothing new., }, }, required: [history_entry, memory_update], }, }, } ]调用模型时传入messages和tools拿到回复后解析工具调用结果。history_entry追加写入HISTORY.mdmemory_update覆盖写入MEMORY.md。4.5 为什么用 Tool Calling 做“假”工具调用这里有个容易看漏的点虽然传入了 tool但并没有后续的 tool 执行步骤。它只是利用 tool 的特性让大模型生成工具调用的参数。本质上和调整提示词让模型输出 JSON 一样但工程上更稳。原因是 LLM 厂商对 Function Calling 接口做了结构化 Schema 约束输出被限制在定义好的结构内天然排除了 Markdown 标记、未闭合括号和前后废话。SDK 直接返回结构化的args字典不用写正则或 JSON 解析器写入文件的内容高度可预测。4.6 检索与注入检索这块 nanobot 走的是关键词精确匹配靠 grep 扫HISTORY.md。注入时MEMORY.md全文进上下文HISTORY.md按需检索片段。这跟向量数据库的语义检索是两条路前者必须字面对应后者能模糊联想。5. 验证请求与成功结果配置好之后先做一次最小验证确认模型通道和工具调用都正常。5.1 验证模型通道curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的TaoTokenKey \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: reply with ok}], max_tokens: 16 }成功时返回结构里choices[0].message.content包含ok。如果返回 401检查 Key 是否复制完整返回 404检查base_url是否漏了/api。5.2 验证记忆固化启动 nanobot 后连续发几条消息然后执行/new。观察~/.nanobot/workspace/memory/目录ls -la ~/.nanobot/workspace/memory/ cat ~/.nanobot/workspace/memory/HISTORY.md cat ~/.nanobot/workspace/memory/MEMORY.md成功时HISTORY.md会多出一条带时间戳的记录格式类似[2026-03-12 11:33] User discussed the drama Liu Zhou Ji starring Wang Churan. User then requested to read their Mac Apple Notes. The assistant accessed the notes list and read the 宝宝知识 note.MEMORY.md的## Preferences小节会多出合并后的事实条目。同时旧对话从workspace/sessions移除返回New session started.。5.3 验证工具调用参数如果想单独看模型返回的 tool call 参数可以在固化流程里打日志或者在模型对话页面用同样的 system prompt 和 user prompt 试一次确认返回的args里有history_entry和memory_update两个字段。6. 本篇常见错排查6.1 固化不触发先看memory_window是不是设太大。本地调试建议设成 10发够消息后看unconsolidated是否达到阈值。如果/new也不触发检查consolidate_on_new是否为true。6.2 save_memory 解析失败最常见的原因是settings.json里的tool_name和源码_SAVE_MEMORY_TOOL里的name不一致。两边都必须是save_memory。另外检查模型是否支持 Function Calling不支持的话会返回纯文本解析不到args。6.3 MEMORY.md 被覆盖成空memory_update要求返回完整 markdown如果模型返回空字符串覆盖后文件就空了。建议在写入前加一层校验memory_update为空或长度小于阈值时跳过写入保留旧内容。6.4 HISTORY.md 时间戳格式不对源码要求[YYYY-MM-DD HH:MM]前缀方便 grep。如果模型返回别的格式检索时会漏。可以在 user prompt 里再强调一次格式要求。6.5 API 返回 401 或 404401 检查 Key404 检查base_url。TaoToken 的 API 基址是https://taotoken.net/api不要多加或少加路径。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的参数说明。6.6 记忆文件路径找不到workspace路径建议用绝对路径~展开在不同 shell 下行为可能不一致。检查~/.nanobot/workspace/memory/目录是否存在不存在就先手动创建。7. 接入自检与后续动作跑通之后建议做一次完整自检发一条包含新事实的消息执行/new确认HISTORY.md追加成功、MEMORY.md合并成功、旧会话清空。三步都过说明记忆系统和模型通道都接好了。如果自检卡在模型调用这一步先去 API Keys 页面确认 Key 状态https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。想单独验证模型返回工具调用的能力用模型对话页面最快https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。长期跑 Agent 任务的话Coding Plan 的额度更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。我自己的习惯是每次改完MEMORY.md模板就git commit一次这样记忆演变有版本记录改错了直接回滚。nanobot 这套 Markdown 驱动的好处就在这记忆是文件文件能进 Git能手动编辑能一眼看懂 AI 记住了什么。
返回列表