Skip to content

feat(embedding): embedding works by default on store and retrieve (ticket 6) + hashing fallback rejected (ticket 7 candidate) - #20

Merged
wefio merged 4 commits into
mainfrom
feat/embedding-default-on
Sep 3, 2026
Merged

feat(embedding): embedding works by default on store and retrieve (ticket 6) + hashing fallback rejected (ticket 7 candidate)#20
wefio merged 4 commits into
mainfrom
feat/embedding-default-on

Conversation

@wefio

@wefio wefio commented Sep 3, 2026

Copy link
Copy Markdown
Owner

变更描述

What(改了什么)
实现工单 6(embedding 默认在存储与检索路径生效)核心 + 关闭 hashing 语义检索兜底 + 立项 SimHash 词法层候选(工单 7)。

Why(为什么改 / 解决什么问题)
实测故障:embedding 配置只放仓库 .env(daemon 不读),重启即丢 key;一次 free-tier 429 把索引标 failedlast_succeeded_at = null → 搜索静默退化纯词法数天。修复方向经业界调研确认(mcp-memory-ts async post-write embedding;openclaw degrade-on-persistent-failure)。

Changes(关键改动点)

  • src/core/embedding-sync.ts:sync 引擎支持 { maxBatches } —— 限量补缺,跑到批上限即返回(索引保持 mid-flight、记录仍排队),不一次性全量 backfill 撞限流。
  • src/cli/service.ts#drainEmbeddings 改为每次 remember/search(经 #signalMaintenance)触发、每 target 限量 1 批;去掉 NMG_EMBED_AUTO_SYNC(有 provider 即自动 sync);provider 失败设 30s 冷却(down 的 provider 不卡每次查询),整链成功清除冷却。
  • src/integration/search.ts:部分索引即可 hybrid(vector LEFT JOIN 保 lexical 结果、只对有向量记录加分),failed/429 索引不再是词法死路;新增 degradedReason 通道,冷却期搜索报 degraded: true + reason。
  • src/core/types.tsretrieval.reason 放宽为 string(冷却原因可自由传)。
  • 文档:工单 6 checklist(1/2/4/5 完成);hashing 语义兜底 rejected ADR(双语,实测 256 维零增益 + NUMEN 维度瓶颈 + 高维不可承受;明确只拒语义角色);工单 7(SimHash 词法层候选,含 claude-memory-system 先例)。
  • 测试:限量 drain(maxBatches)、provider 存在即 hybrid(无 AUTO_SYNC env)、不可达 provider degrade 不阻塞 —— sync/chaos/service 套件全绿。

完成检查项

本地质量检查

  • npm run agent:verify 通过(check / test:product / build / verify:static 含 verify:packages + complexity gate / docs:check 109 files 0 errors)
  • 目标测试全过(embedding-sync 5、chaos embedding-outage 2、service embedding 相关 47 文件级全绿)
  • npm run check / lint / format:check 通过
  • 未提交可再生产物(lib/dist/generated/scope 均不入库)

RCP(Repository Control Plane)

  • 黑板 in-flight goal:repo-development 频道两条(工单 6 实施 1788430068893_000011)已 resolve
  • 改动路由 reconcile 通过:npm run agent:verify(core-memory / daemon-cli / ci-and-tests 全 blocking 绿)
  • 只提交本 PR 自有文件(src/core、src/cli、src/integration、tests、docs/design、docs/decisions)

CI 完成确认

  • nmg-rcp forge-status --pr <PR号>All checks passed 为 SUCCESS
  • CodeFactor 通过
  • Static job 通过(含 verify:static 全部子检查 + Dependency audit)

CI Status Snapshot 是 GitHub 状态的只读观察(authority: observation-only),合并仍需显式操作。

…trieve

Ticket records the operator-observed incident (embedding config only in a repo
.env the daemon never reads; a single 429 marked the index failed and search
silently fell back to lexical for days), the agreed fix (per-operation bounded
drain without threshold batching, rate-limit = pause not fail, local hashing
degrade chain with reason, drop the AUTO_SYNC gate, deployment-layer config
persistence), and the industry practice it follows (async post-write embedding
in mcp-memory-ts; degrade-on-persistent-failure lifecycle in openclaw).
…cket 6)

Implements the core of ticket 6 so embeddings are present on the normal write
and search paths without a fragile config toggle:

- sync engine: syncEmbeddingTarget accepts { maxBatches } and stops after N
  batches, leaving the index mid-flight (records still queued) instead of
  blocking on a full backfill or marking it complete prematurely.
- service: #drainEmbeddings now fires on every remember/search via
  #signalMaintenance (no writeThreshold/accessThreshold batching), runs one
  bounded batch per target, and drops the NMG_EMBED_AUTO_SYNC gate — a
  configured provider (+key) implies auto-sync. Provider failures start a 30s
  cooldown so a down/rate-limited provider cannot hang every query; a full
  successful drain clears it.
- search: searchMemoryContext serves hybrid from a *partial* index (the
  vector LEFT JOIN keeps lexical results and lifts only indexed records), so a
  previously failed/429'd index is no longer a permanent lexical dead end; and
  accepts a degradedReason so a cooldown search reports degraded:true instead
  of silently re-attempting the provider.

Tests: bounded-drain (maxBatches), provider-presence-implies-sync hybrid, and
degraded-unreachable-provider cases; sync/chaos/service suites pass.
Ticket 6 checklist updated (local-hashing blend remains as the open slice).

Verified: npm run agent:verify all blocking checks green (check,
test:product 778, build, verify:static incl. verify:packages + complexity).
…al candidate (ticket 7)

The local-hashing retrieval fallback was measured (real store) and rejected:
256-d nmg-hashing-v1 blended retrieval is byte-identical to pure lexical
(self-recall 45/154 both arms; scores ≈ 0), matching the published
dimensionality bottleneck; the NUMEN-style fix (16K-32K dims, beats BM25) is
unaffordable for a local SQLite store.

Recorded as a rejected decision with an explicit scope note: only the semantic-
retrieval role is rejected. Word-level uses (SimHash/feature hashing for
near-duplicate candidate recall and spelling-tolerant matching) are a separate
candidate, opened as ticket 7 with research precedent
(claude-memory-system issue 53: 64-bit SimHash, Hamming <= 3, pre-filter
before an LLM dedup judge). Ticket 6 marked done-evaluated; ticket 7 lists the
recall-gap measurement gates adoption.

docs: 109 files, 0 errors.
Design doc for the word-level SimHash candidate (ticket 7): closes the gap
where supersedeCandidates' word-level exact matching (instr substring +
token normalization + word-set Jaccard) recalls zero for spelling / word-form
variants ("embedding" vs "embeddings", "colour" vs "color"). Proposes a
64-bit SimHash fingerprint (one INTEGER column, Hamming <= 3 recall channel
inside supersedeCandidates, judge still decides) with a measurement-first
experiment gate: generate word-form variants over the real store and measure
whether the fingerprint channel recalls what Jaccard misses, before any index
is built. Boundaries kept: no semantic judgment, no search-ranking role
(rejected decision), no external dependency.
@wefio
wefio merged commit 8ed1403 into main Sep 3, 2026
7 checks passed
@wefio
wefio deleted the feat/embedding-default-on branch September 3, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant