- 人工智能
- AI Agent
- 自主智能体
- 桌面应用
- MCP Clients
【免费下载链接】Kun
Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.
导读
本文围绕 Kun 仓库中一次已归档的记忆检索缺陷修复展开:当所有授权候选记忆都缺乏"正向基础相关性"时,检索层必须严格弃权(不注入任何记录),而旧实现仅以lexical > 0作为判定,导致弱词法匹配被错误放行。文章将完整还原该修复的设计决策、语言校准阈值(拉丁 0.40 / CJK 1/3)、双检索模式(SQLite FTS5 与文件系统回退)的一致性机制、回归证据与回滚策略,并辅以 memory-ranking.ts 与 memory-retrieval.ts 的源码佐证,帮助读者理解如何在记忆检索层实现确定、可版本化、可测试的"无相关即不注入"安全边界。
背景:弱词法匹配绕过"无正向相关性即弃权"约束
Kun 的记忆检索有一条 P0 级安全约束:当每一个授权且活跃的候选记忆都缺乏正向基础相关性时,检索层不得注入任何记录(避免把无关记忆当作参考证据混入上下文)。旧实现将这一判定编码为:
candidate.features.lexical > 0 || candidate.features.typeAffinity > 0问题在于lexical > 0的边界过于宽松——只要查询与某条记录存在任意一个公共词或 trigram,词法覆盖率即为正,弱匹配就会被当作"相关"放行。在匿名 v2 开发集上,查询 q033(数据库凭证请求)与 q034(客户名单请求)正是通过常见术语或 trigram 命中了一批无关记录,而 q035(薪资数据)与 q036(执行注入)则正确地返回了空结果。
生产审计记录了修复前的实测特征:q033 与 q034 的领先词法特征约为0.392857与0.304348,均落在"微弱但不为零"的区间;回归夹具(memory-lexical-abstention-regression.v1.json)冻结了这些观测:q033 修复前命中region / rollback / postgres / backup / maintenance等记录(其中postgres、password_rotation被标记为禁止选择),q034 修复前命中localization / alert_severity / accessibility / rollback / user_concise等记录(其中postgres、rate_limit被禁止),q035/q036 修复前后均为空。
核心决策一:共享词法谓词 + 语言校准阈值
修复的核心是引入命名的基础阈值常量,替换"大于零即通过"的判定。在 memory-ranking.ts 中以代码级常量定义:
export const MEMORY_MIN_LEXICAL_RELEVANCE = 0.4 export const MEMORY_MIN_CJK_LEXICAL_RELEVANCE = 1 / 3拉丁语查询采用0.40,由冻结的 v2 开发证据校准,是使 q033/q034 弃权的最低已测试词法覆盖率阈值;包含 CJK 字符的查询采用1/3,延续既有的 n-gram 契约——CJK 回归查询用户叫什么名字在六个双字 n-gram 中有两个相关(1/3),而 q035/q036 零重叠依然弃权。真实谓词实现见 hasPositiveMemoryRelevance:
export function hasPositiveMemoryRelevance(candidate: RankedMemory, query = ''): boolean { const threshold = /[\u3400-\u9fff\u3040-\u30ff\uac00-\ud7af]/u.test(query) ? MEMORY_MIN_CJK_LEXICAL_RELEVANCE : MEMORY_MIN_LEXICAL_RELEVANCE return candidate.features.lexical >= threshold || candidate.features.typeAffinity > 0 }其概念形态与设计文档一致:typeAffinity > 0 || lexical >= lexicalThresholdForQuery(query)。显式类型亲和检索(如"我的名字是什么"命中fact/relationship类型)被完整保留,作用域与生命周期过滤不受影响;谓词由 SQLite FTS5 与文件系统回退两条路径共享,仅查询语言地板不同。
词法覆盖率原理:token 生成与归一化覆盖率
门控所依据的features.lexical并非后端特异性分值,而是统一的 token 覆盖率。在 memory-search-tokens.ts 中,memorySearchTokens()将输入做 NFKC 归一化与小写化后分两路生成 token:
- 拉丁词:整词以
w前缀入列,长度大于 3 的词再切出 3-gram 以g前缀入列; - CJK 连续段:单字符以
c前缀入列,长度大于 1 的段切出 2-gram(如用户叫什么名字生成c用户/c户叫/c叫什/c什么/c么名/c名字)。
覆盖率由 lexicalTokenCoverage 计算:查询 token 中命中记录 token 的数量除以查询 token 总数,即overlap / queryTokens.length。这正是设计文档强调"FTS5 BM25 可能只影响候选窗口排序,门控却收到与文件系统回退相同的覆盖率分值"的原因——后端的 BM25 归一化无法把弱匹配抬过基础阈值。记录侧索引源包括content + tags + type + sources(见 memoryRecordSearchTokens),token 上限为记录 512、查询 128。
双模式一致性:FTS5 与文件系统回退共享同一门控
两种生产模式共享retrieveMemoryRecords()与hasPositiveMemoryRelevance()。retrieveMemoryRecords 的完整流水线为:作用域过滤(memoryInScope)→ 生命周期过滤(memoryLifecycleState === 'active',含 supersede 去重)→ purpose 过滤(tool查找保留 directive,否则剔除 directive 权威记录)→ 请求过滤(scope/type/authority)→ 排序(rankMemory,可注入lexicalOverride与通道标识)→ 相关性谓词过滤 → 按finalScore排序 → 上下文预算截取(applyMemoryContextBudget)→ 生成检索 trace。
设计文档记录了一条关键回归线索:SQLite 集成曾首次暴露出旧 BM25 派生分值可能偏离文件系统回退,索引路径因而改用共享的有界覆盖率分值参与门控。混合集成测试 memory-lexical-abstention-hybrid.test.ts 从匿名记录构建 FTS 投影,对 q033–q036 逐一断言:索引路径与文件系统回退的选中 id 完全一致、trace 模式为sqlite-fts5、selectedIds与返回记录一致、词法特征均落在[0, 1],且 q033/q034 的filtered.irrelevant > 0且两路结果均为空。该测试刻意不修改文件系统回退去补偿索引侧差异,而是要求索引路径收敛到共享覆盖率分值。
核心决策二:在完整开发集上校准,而非"修两个查询"
设计文档明确:不能仅因阈值修好了 q033/q034 就接受它。实现必须对全部 v2 开发类目同时记录基线与门控后的指标,包括Recall@K、Precision@K、MRR、弃权准确率、禁止选择、作用域/生命周期泄漏、确定性 trace 哈希。证据显示0.40在剔除两个误报查询的同时降低了基础 Recall——对安全缺陷修复而言这是有意的权衡,但必须留档供评审;若不存在同时满足质量与安全约束的阈值,则暂停实现并修订设计,而不是静默弱化产品契约。
对应的 v2 门控评估测试见 memory-lexical-abstention-evaluation.test.ts:它以createLexicalSemanticMemoryCandidate({ relevanceMode: 'foundation-v1' })运行runSemanticMemoryEvaluation,将报告指标与冻结的memory-lexical-abstention-evaluation.v1.json门控证据逐项匹配,并校验safetyGatePassed、延迟 P50/P95 为正以及回归查询的selectedIds。从源码结构看,指标表(gate metrics)与零容忍安全门(safety gates)由评估基础设施统一执行,threshold 的每个取值变化都会在指标 diff 中显式可见。
核心决策三:测试双模式而不复制业务逻辑
测试策略分两层:
共享排序测试memory-lexical-abstention-ranking.test.ts:直接针对谓词与常量——低于
0.4 - 0.001的词法候选被拒;恰好等于0.4被接受;typeAffinity > 0且词法为 0 的记录仍被接受(如what is my name命中identity-fact);CJK 地板独立于拉丁安全阈值(1/3恰好通过、1/3 - 0.001拒绝、且对英文密码查询返回 false);MEMORY_MIN_CJK_LEXICAL_RELEVANCE与1/3近似相等。hybrid-store/SQLite 集成测试memory-lexical-abstention-hybrid.test.ts:用匿名 v2 数据集构建
HybridMemoryStore(临时数据目录),createWithId灌入记录、waitForBackfill等待 FTS 回填,再对比store.retrieve(request)与retrieveMemoryRecords({ mode: 'filesystem-fallback' })的选中 id 与门控决策。
回归审计测试 memory-lexical-abstention-audit.test.ts 则校验冻结夹具:foundationThreshold必须等于0.4,四个查询expectedAfterFix全为空、forbiddenIds永不出现,且filtered.irrelevant与修复前的观测数量一致,从而"在不改动冻结的 v1/v2 语义证据前提下,保住修复前观测"。
核心决策四/五:窄修复与历史证据复现
修复刻意保持范围最小且可逆:不新增任何面向用户的配置字段,阈值是代码级基础版本常量;回滚只需单个提交还原门控及其夹具,无需数据迁移。同时,入库的 v1/v2 词法证据描述的是门控前的基线,必须逐字节不变——为此 MemoryRelevanceMode 显式区分两种模式:
export type MemoryRelevanceMode = 'foundation-v1' | 'historical-v1'foundation-v1:走生产阈值(hasPositiveMemoryRelevance),生产检索默认采用;historical-v1:走旧谓词lexical > 0 || typeAffinity > 0(hasHistoricalPositiveMemoryRelevance),仅供评估复现历史工件,Memory store 不使用。
在 memory-retrieval.ts 中,relevanceMode === 'historical-v1'时选择历史谓词,否则选择基础谓词。这保证了新词法弃权评估报告(foundation-v1 门控)与冻结历史证据(historical-v1 基线)同时可复现、互不覆盖。
风险与权衡清单
设计文档明确了四项风险及其缓解措施:
| 风险 | 缓解 |
|---|---|
| 更严格词法门控可能降低弱措辞查询的 Recall@K | 记录完整 v2 开发指标增量,保留类型亲和检索;语义检索留作后续独立决策 |
| 固定阈值在不同语言下表现不同 | 显式拉丁/CJK 双地板,英/中用例同置一个开发矩阵,强制确定性跨模式测试 |
| BM25 归一化可能随 SQLite 版本变化 | 覆盖率地板与共享谓词保持权威,SQLite 路径分值记入集成证据 |
| 现有调用方可能依赖弱匹配 | 遵循已批准的"无关不注入"契约,合并前补充显式回归证据 |
迁移计划与验收
修复按四步落地,每一步都有明确产出:
- 提交 OpenSpec 契约与匿名审计夹具(
memory-lexical-abstention-regression.v1.json,含schemaVersion: 1、foundationThreshold: 0.4、四条查询的expectedIds / forbiddenIds / observedBeforeFix / expectedAfterFix); - 新增共享门控测试与 SQLite/文件系统复现测试;
- 实现校准门控,先跑聚焦测试(ranking + hybrid + audit),再跑完整 v2 开发评估,确认所有零容忍安全门通过且指标 diff 留档;
- 运行仓库门禁、更新 roadmap/证据,创建生产修复 PR。
回滚即还原门控提交;规范记录与索引 schema 保持兼容,无需迁移。整套方案的验收标准可以概括为:q033/q034 在两条检索路径上均弃权、q035/q036 依旧弃权、CJK 回归查询用户叫什么名字保持1/3通过、类型亲和检索不受影响、冻结的历史评估证据逐字节不变。
相关文件速查
- 设计文档:openspec/changes/archive/2026-09-11-fix-kun-memory-lexical-abstention/design.md
- 核心实现:memory-ranking.ts(阈值常量、双谓词、
MemoryRelevanceMode)、memory-retrieval.ts(检索流水线与模式分发)、memory-search-tokens.ts(token 生成与覆盖率) - 测试与夹具:memory-lexical-abstention-ranking.test.ts、memory-lexical-abstention-hybrid.test.ts、memory-lexical-abstention-audit.test.ts、memory-lexical-abstention-evaluation.test.ts、memory-lexical-abstention-regression.v1.json
本文所有数据、阈值与行为描述均来自上述设计文档、源码与测试夹具;如需在本地复现,可在kun目录下运行相应 Vitest 用例(如memory-lexical-abstention-ranking、memory-lexical-abstention-hybrid、memory-lexical-abstention-audit)查看门控行为与冻结证据。
- 人工智能
- AI Agent
- 自主智能体
- 桌面应用
- MCP Clients
【免费下载链接】Kun
Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.
相关推荐
FastLED 跨平台符号分析(Symbol Analysis)工具完全指南:基于 ELF 的固件体积优化实战
FastLED 跨平台符号分析(Symbol Analysis)工具完全指南:基于 ELF 的固件体积优化实战 FastLED 的符号分析工具提供了一套与平台无
人工智能AI Agent自主智能体桌面应用MCP ClientsKun 记忆检索基础(Memory Retrieval Foundation):作用域安全、多语言词汇检索与可复现评估的完整实现
Kun 记忆检索基础(Memory Retrieval Foundation):作用域安全、多语言词汇检索与可复现评估的完整实现 Kun 是一个 Local f
人工智能AI Agent自主智能体桌面应用MCP ClientsAI Agent 记忆的遗忘与老化策略:让记忆保持新鲜、相关且无冗余
AI Agent 记忆的遗忘与老化策略:让记忆保持新鲜、相关且无冗余 导读 遗忘(Forgetting)与老化(Aging)策略是 AI Agent 记忆生命周
文档教程知识库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考