feat(memory): per-turn injection controls (maxInjectedRecords + scopes allow-list) - #1149
Open
Chasen-Liao wants to merge 7 commits into
Open
feat(memory): per-turn injection controls (maxInjectedRecords + scopes allow-list)#1149Chasen-Liao wants to merge 7 commits into
Chasen-Liao wants to merge 7 commits into
Conversation
…n cap The config field was declared and surfaced in the manifest but never read; all three retrieval call sites hard-coded limit: 8. FileMemoryStore.retrieve now computes effectiveLimit = min(input.limit ?? cap, cap) from the store's maxInjectedRecords, and the core loop / agent-sdk / cursor-sdk call sites stop passing limit so the config can both lower and raise injection. The retrieve limit and the manager RPC schema field become optional so remote mode round-trips an omitted limit without a zod strict-parse failure. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
config.scopes was declared but never read: all active memories were injection candidates regardless of scope. It now gates which scopes are eligible for per-turn injection; user-scope identity memory is dropped when 'user' is excluded. Extract DEFAULT_SCOPES for the config default and cover the scope matrix in unit + manager-level integration tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ignore per-machine editor/MCP tooling config (codegraph index, Claude settings, Cursor MCP, root .mcp.json) so git status stays clean without excluding the tracked .claude/ and .cursor/ openspec sync files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two P0 defects in memory retrieval:
1. User-scope memories were injected unconditionally and ranked first, so
once their count reached maxInjectedRecords they filled the whole
per-turn budget and squeezed out scored workspace/project hits.
retrieve() now reserves max(1, floor(cap/2)) slots for the scored pool
when it has hits and backfills unused slots in priority order.
2. Continuation turns ("继续", "fix this") scored zero hits because the
retrieval query was the bare turn prompt, sharing no n-grams with
stored content. The query now composes the prompt with the active goal
and recent user/assistant/tool history, and retrieve() retries the
fallback query before falling back to recent-first records for
degenerate (<2 gram) queries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Drop the dead `fallbackQuery` retry: the active goal is already part of the composed query and its n-grams are a subset of the query's, so the store-side retry could never score a hit the primary query missed. - Move the degenerate-prompt decision up to the resolver: it flags the recency fallback on the bare prompt (< 2 n-grams), fixing the previous gate that counted n-grams on the composed query and never fired. - Collapse allocateQuota to a deficit-flow slice, reuse toolResultTextWithoutImages, and name the query budget constants. - Add the missing manager-path quota regression test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confidence half-life decay never took effect in production: minConfidence defaulted to 0 (so isMemoryActive always passed) and memory_create hard-coded provenance kind 'user', which skips decay entirely. - Add minConfidence (default 0.2) to MemoryCapabilityConfig, surfaced through the manifest, example config, and renderer contract. - FileMemoryStore reads config.minConfidence instead of an unwired option. - memory_create writes kind 'tool' (confidence 0.7) so agent-derived facts fade over the 180-day half-life; explicit user corrections stay anchored at kind 'user' / confidence 1. - Tests: decay filtering, minConfidence=0 opt-out, kind 'tool' provenance, and config bounds / manifest round-trip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Author
Code reviewFound 3 issues:
Kun/kun/src/loop/turn-context-resolver.ts Lines 14 to 17 in f36179b
Kun/kun/src/memory/memory-store.ts Lines 113 to 118 in f36179b
Kun/kun/src/memory/memory-store.ts Lines 176 to 219 in f36179b 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…uota edge - move ngrams to domain/memory-scoring.ts so loop/ no longer imports a node:fs-backed store (kun-contributing §2 layering) - add MemoryRecord.anchored to orthogonalize decay governance from provenance.kind; memory_update passes source:'agent' so an agent-side correction can no longer re-anchor a decayed memory; readAll lazily migrates legacy records - drop max(1,…) in allocateQuota so limit=1 no longer starves user memories, and align the "unconditionally injected" comment with the quota behaviour Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
记忆系统的每轮注入控制 + 三个 P0 缺陷修复,共 6 个 commit。核心思路:自动读取、显式写入——每轮按当前上下文自动注入相关记忆,agent 经工具显式写记忆,记忆随置信度衰减自动淡出。
改动内容
每轮注入上限(maxInjectedRecords)
此前
maxInjectedRecords配置声明了但从未生效,三处调用硬编码limit: 8。现在retrieve()计算effectiveLimit = min(input.limit ?? cap, cap),配置可调小(省 token/降噪)也能调大(召回更多长尾记忆)。作用域注入白名单(scopes allow-list)
此前
config.scopes未生效,所有记忆都是注入候选。现在只注入白名单内的作用域(user / workspace / project);把user移出即关闭身份记忆。scopes: []停用注入但保留读写。P0-1:user 记忆配额分档
user 记忆无条件注入且排最前,一旦活跃数达到上限(默认 8)会把 workspace/project 记忆整体挤出。现在
retrieve()为评分池保底max(1, floor(cap/2))槽位、剩余回填,项目相关记忆不再被身份记忆挤掉。P0-3:组合查询 + 退化兜底
检索 query 此前只用当前 turn 的 prompt,续接轮("继续"、"接着做")与存储内容零 n-gram 重叠导致漏检。现在 query 由
prompt + active goal + 最近对话/工具事实组合(查询只用于内部评分、不花 prompt token);退化 prompt(<2 gram)由 resolver 标记、store 兜底注入最近更新记录。简化阶段删除了死代码fallbackQuery(goal 已在主 query、重试必空),并把退化判定上移到 resolver。P0-2:置信度衰减接线
置信度半衰期此前在生产从未生效(
minConfidence恒 0 +memory_create硬编码kind='user'短路衰减)。现在新增minConfidence配置(默认 0.2),memory_create写入kind='tool'(confidence 0.7),agent 记录随 180 天半衰期淡出;用户显式更正的记忆保持kind='user' + confidence=1不衰减锚点。工具清理
.gitignore增加本地编辑器/MCP 工具配置(codegraph 索引、Claude 设置、Cursor MCP 等),保持git status干净。行为变化说明
scopes现在真正生效(此前是惰性配置)测试验证
design-svg-tool.test.ts1 个失败为 pre-existing,与本次无关,已用 stash 隔离验证)tsc --noEmit类型检查通过🤖 Generated with Claude Code