feat(memo): 接入 LLM 自动提取与后台调度#246
Conversation
…egration - use rune-safe title truncation for auto extracted memo entries - guard memo remember/recall tools against nil service and cap recall output - limit runtime memo extraction input and add bounded async execution - add regression tests for utf-8 truncation, nil guards, output limit, and runtime hook Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Yumiue <188874804+Yumiue@users.noreply.github.com>
fix(memo): address review findings in PR 1024XEngineer#245
# Conflicts: # internal/runtime/runtime.go # internal/runtime/runtime_test.go
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
/code 解决冲突,添加测试文件 |
处理结果已完成核查与验证,当前 PR 分支无需额外冲突修复或补测提交:
未执行项
|
|
/code 增加测试覆盖率,解决这个PR合并的冲突 |
处理结果已按你的要求完成两件事:补测试提升覆盖率,并处理合并冲突检查。
|
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Yumiue <188874804+Yumiue@users.noreply.github.com>
|
/code 解决PR合并冲突 |
处理结果已完成本次“解决 PR 合并冲突”。我在当前 PR 分支执行了与 已完成
验证
|
- align runtime package with main split structure - keep memo auto-extract scheduling behavior on runtime path - update related runtime tests and helpers Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Yumiue <188874804+Yumiue@users.noreply.github.com>
|
/review |
| state.idleTimer = nil | ||
| state.mu.Unlock() | ||
|
|
||
| a.mu.Lock() |
There was a problem hiding this comment.
handleIdle can delete a.states[sessionID] based on a stale check.
Race window: after releasing state.mu (line 188) and before/while this block acquires a.mu, another goroutine can call Schedule(...), set state.pending, and arm a new timer. This function will still delete the state entry because it never re-validates running/pending under state.mu before deletion.
Result: active state can be detached from the map, so later schedules create a second state for the same session, breaking debounce/serialization guarantees and risking duplicate background runs.
Please re-check state liveness just before deletion (e.g., reacquire state.mu after taking a.mu, or use a tombstone/version guard).
| continue | ||
| } | ||
|
|
||
| added, err := a.svc.addAutoExtractIfAbsent(ctx, entry) |
There was a problem hiding this comment.
extractAndStore persists entries one-by-one via addAutoExtractIfAbsent, and each call goes through saveEntryLocked (LoadIndex + SaveTopic + SaveIndex).
When one extraction returns multiple entries, this becomes repeated full-index read/write cycles in a tight loop. That scales poorly with memo size and can dominate runtime I/O.
Consider batching accepted entries into a single index load/save transaction (or at least amortizing index reads/writes per extraction run).
|
/simplify |
处理结果已完成你本次 本次实际改动:
历史评论评估结论
验证
提交与推送
|
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Yumiue <188874804+Yumiue@users.noreply.github.com>
变更说明
本 PR 对 memo 子系统做了一轮自动提取能力升级,将原来的规则匹配提取改为基于模型的自动提取,并把提取时机从 runtime 里的临时异步调用收敛到 memo 子系统内部的后台调度器中。
这次改动保持现有
MEMO.md + topic 文件存储格式、memo_remember/memo_recall工具和索引结构不变,不引入 schema/frontmatter 迁移。主要改动
1. 接入 LLMExtractor
memo.TextGenerator接口memo.LLMExtractor,替代默认自动提取实现userfeedbackprojectreferencetool消息参与提取[]type合法title/content非空title走NormalizeTitlekeywords去空白去重2. 新增 AutoExtractor 后台调度器
memo子系统内部新增后台调度器,封装Extractor + ServicesessionID做 2 秒 debouncememo_remember成功写入记忆,则自动提取跳过Type + NormalizeTitle(Title) + trimmed Content完全相同时跳过Service内部加锁原子完成,避免并发 session 重复落盘3. 调整 runtime 调度时机
runtime.MemoExtractor改为调度接口:Schedule(sessionID, messages, skip)Run生命周期内跟踪是否成功执行过memo_remember4. 新增 app 层 provider 适配
internal/app新增基于当前 provider/model 的TextGenerator适配BuildRuntime默认装配LLMExtractor + AutoExtractor测试
补充并通过了以下测试:
internal/memo/llm_extractor_test.gointernal/memo/auto_extractor_test.goskip=true跳过调度internal/app/provider_text_generator_test.gomessage_done的错误处理internal/runtime/runtime_test.gomemo_remember成功后自动提取跳过验证
已执行并通过:
go test ./internal/memo/... ./internal/runtime/... ./internal/app/...go test ./...go build ./...说明
RuleExtractor保留作为回退/测试实现,但默认路径已切换到LLMExtractor