feat(memo): 新增跨会话持久记忆系统——模型工具与自动提取#245
Conversation
Agent 可通过工具调用主动读写跨会话记忆: - memo_remember: 保存记忆条目(type/title/content/keywords) - memo_recall: 按关键词搜索并加载记忆详情 - 工具通过构造函数注入 memo.Service,条件注册(仅 Memo.Enabled) - MicroCompactPolicy=PreserveHistory,保留记忆读写结果 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 新增 RuleExtractor:检测用户消息中的显式信号词(记住/偏好/avoid 等) - runtime.Service 新增 MemoExtractor 接口与 SetMemoExtractor 方法 - EventAgentDone 后异步调用 ExtractAndStore,失败静默处理 - bootstrap 条件注入(仅 AutoExtract 启用时) - 完整单元测试覆盖:信号检测、英文信号、长内容截断、边界条件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
合并 1024XEngineer/neo-code main (2079c91),解决 1 处冲突: - bootstrap.go: 合并 memotool 和 mcp 两个新增 import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
/simplify |
Completed已按请求完成:检查历史评论中的未解决 review 项、修复值得修复的问题,并执行 code-simplifier 复审后继续简化实现。 What I changed
Tests
Git
Not done
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
/review -codex |
| s.emit(ctx, EventAgentDone, input.RunID, session.ID, assistant) | ||
| // 异步提取记忆:不影响主循环,失败静默处理。 | ||
| if s.memoExtractor != nil { | ||
| msgs := make([]providertypes.Message, len(session.Messages)) |
There was a problem hiding this comment.
这里为提取记忆每轮都复制整个 session.Messages。当前提取器只用到“最后一条 user 消息”,全量 make+copy 在长会话会造成不必要的 O(n) 分配与拷贝。建议改为仅传递提取所需最小数据(例如最后一条用户消息)。
| if s.memoExtractor != nil { | ||
| msgs := make([]providertypes.Message, len(session.Messages)) | ||
| copy(msgs, session.Messages) | ||
| go s.memoExtractor.ExtractAndStore(context.Background(), msgs) |
There was a problem hiding this comment.
go ...ExtractAndStore(context.Background(), msgs) 是无背压的 detached goroutine:高频回合会持续创建 goroutine 并触发磁盘写入,且与调用方生命周期脱钩。建议使用受控 worker/队列或显式并发上限,并绑定可管理的上下文。
| // 截断过长内容作为标题 | ||
| title := NormalizeTitle(lastUserMsg) | ||
| if len(title) > 150 { | ||
| title = title[:147] + "..." |
There was a problem hiding this comment.
这里按字节截断字符串(title[:147]),对 UTF-8 多字节字符可能切断 rune,导致标题出现非法编码/替换字符。建议按 rune 数截断。
| { | ||
| Type: TypeUser, | ||
| Title: title, | ||
| Content: lastUserMsg, |
There was a problem hiding this comment.
自动提取会将整条用户消息原文持久化到记忆。结合较宽泛的信号词(如 always/prefer),容易把敏感信息误存为长期数据。建议在入库前增加敏感信息过滤/脱敏策略,或缩窄自动提取触发条件。
| } | ||
| sort.Strings(keys) | ||
|
|
||
| var builder strings.Builder |
There was a problem hiding this comment.
memo_recall 会拼接所有匹配 topic 的完整内容且结果被 PreserveHistory 保留,缺少条目数/字节数上限时会放大上下文与 token 成本。建议增加返回上限与截断策略(并提示“部分结果已省略”)。
fix(memo): address review findings in PR 1024XEngineer#245
概述
为 NeoCode 新增完整的跨会话持久记忆系统(
internal/memo),对标 Claude Code 的记忆架构,使 Agent 能在会话间积累和利用知识。主要变更
核心模块 (
internal/memo/)store.go): 基于文件系统的FileStore,复用 session 的 SHA1 工作区隔离,原子写入index.go): MEMO.md 索引渲染与解析,Topic 文件 frontmatter 格式,NormalizeTitle清洗service.go):Service编排 Add/Remove/List/Search/Recall,互斥锁保护 + 缓存失效回调context_source.go): 实现SectionSource接口,将 MEMO.md 索引注入 system prompt,TTL 缓存types.go): 4 类记忆 (user/feedback/project/reference),Store/Extractor接口extractor.go): 检测用户消息中的显式信号词(记住/偏好/avoid 等),自动提取记忆模型工具 (
internal/tools/memo/)tool_initiatedTUI 命令
/memo— 查看记忆索引/remember <text>— 手动保存记忆/forget <keyword>— 删除匹配记忆Runtime 集成
Service新增MemoExtractor接口与SetMemoExtractor方法EventAgentDone后异步调用ExtractAndStore,失败静默处理AutoExtract启用时注入配置
MemoConfig:enabled/auto_extract/max_index_lines,支持 YAML 持久化enabled=true,auto_extract=true,max_index_lines=200其他改进(前期合并)
存储布局
架构
测试覆盖
internal/memo/全部单元测试(types/store/index/service/context_source/extractor)internal/tools/memo/全部单元测试(remember/recall 正常+边界+异常)internal/config/MemoConfig Clone/Validate/ApplyDefaults 测试internal/context/NewBuilderWithMemo 测试internal/tui/core/app/memo 命令处理函数测试go build ./...&&go test ./...全量通过测试计划
/remember 我喜欢中文注释,重启后/memo仍可见/forget删除后确认不再出现在上下文