Skip to content

fix(memory-core): CJK textScore=0 and empty MATCH LIKE fallback#92341

Closed
rrriiiccckkk wants to merge 8 commits into
openclaw:mainfrom
rrriiiccckkk:fix/cjk-textscore-92061
Closed

fix(memory-core): CJK textScore=0 and empty MATCH LIKE fallback#92341
rrriiiccckkk wants to merge 8 commits into
openclaw:mainfrom
rrriiiccckkk:fix/cjk-textscore-92061

Conversation

@rrriiiccckkk

@rrriiiccckkk rrriiiccckkk commented Jun 12, 2026

Copy link
Copy Markdown

Summary

Fixes three related bugs that cause textScore: 0 in hybrid memory search for CJK queries (e.g. "飞书插件配置", "股票分析").

Bug 1: CJK tokens bypass LIKE path (trigram tokenizer)

In planKeywordSearch, CJK tokens with 3+ characters were sent to matchTerms as quoted phrase matches (MATCH "飞书插件配置"), which rarely match with the trigram tokenizer. The < 3 character threshold incorrectly routed long CJK strings to the MATCH path.

Fix: Remove the < 3 threshold so all CJK tokens go to substringTerms for LIKE matching.

Bug 2: No LIKE fallback when MATCH returns 0 rows

When FTS5 MATCH returned 0 rows (not an exception), no LIKE fallback triggered. This affected both CJK queries and short English tokens (e.g. AI, A) that the trigram tokenizer cannot index.

Fix: Add LIKE fallback with OR-based matching when MATCH returns empty.

Bug 3: mergeHybridResults discards textScore when chunk IDs don't overlap (#92337)

When keyword and vector results have different chunk IDs (common with CJK/trigram queries), the keyword search results were effectively discarded — textScore stayed 0 even though searchKeyword returned valid results.

Fix: Add path + line range overlap fallback in mergeHybridResults:

  • Build a vectorByPath secondary index grouped by file path
  • When no exact chunk ID match: find the vector chunk with the highest vectorScore on the same path with overlapping line ranges
  • Merge textScore into that vector entry (using Math.max, expanded line range)
  • If no overlap found: preserve original keyword-only behavior

Real behavior proof

  • Behavior or issue addressed: CJK memory search returns textScore: 0 for all results when querying with multi-character Chinese strings (e.g. "股票分析", "A股投研"). Root cause: (1) CJK tokens bypass LIKE path, (2) no LIKE fallback when MATCH returns 0 rows, (3) mergeHybridResults discards textScore when keyword/vector chunk IDs don't overlap.
  • Real environment tested: macOS arm64, OpenClaw 2026.6.5, Ollama qwen3-embedding:4b, memory store with 73 indexed files / 358 chunks, SQLite with trigram tokenizer.
  • Exact steps or command run after this patch:
openclaw memory search --json --max-results 10 "股票分析"
openclaw memory search --json --max-results 10 "A股投研"
openclaw memory search --json --max-results 10 "memory plugin"
  • Evidence after fix:
$ openclaw memory search --json --max-results 10 "股票分析"
  "textScore": 1,
  "vectorScore": 0.5289,
  "score": 0.6703

$ openclaw memory search --json --max-results 10 "A股投研"
  "textScore": 1,
  "vectorScore": 0.5008,
  "score": 0.6506

$ openclaw memory search --json --max-results 10 "memory plugin"
  "textScore": 0.7530,
  "vectorScore": 0.6297,
  "score": 0.6667

Files Changed

  • extensions/memory-core/src/memory/manager-search.tsplanKeywordSearch + searchKeyword (Bug 1 & 2)
  • extensions/memory-core/src/memory/hybrid.tsmergeHybridResults (Bug 3)

Closes #92061
Fixes #92337

CJK tokens with 3+ characters were sent to matchTerms as quoted phrase
matches (MATCH "飞书插件配置"), which rarely match with trigram tokenizer.

Remove the  character threshold so all CJK tokens go to
substringTerms for LIKE matching with individual characters.

Fixes openclaw#92061 (Bug 1)
@openclaw-barnacle openclaw-barnacle Bot added extensions: memory-core Extension: memory-core size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 12, 2026
@clawsweeper

clawsweeper Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed June 26, 2026, 6:17 AM ET / 10:17 UTC.

Summary
The PR changes memory-core keyword search to route all CJK trigram tokens through LIKE, adds an empty-MATCH LIKE fallback, and merges keyword textScore into overlapping vector results.

PR surface: Source +69. Total +69 across 2 files.

Reproducibility: yes. for the source/dependency path: current main routes long CJK trigram tokens through MATCH and only falls back on exceptions, matching the reported zero-row behavior. I did not run a full live Ollama-backed memory search in this read-only review.

Review metrics: 1 noteworthy metric.

  • Regression coverage: 0 tests added. The PR changes memory retrieval scoring and selection, so CJK fallback precision and hybrid minScore behavior remain unguarded on the branch.

Stored data model
Persistent data-model change detected: unknown-data-model-change: extensions/memory-core/src/memory/hybrid.ts, vector/embedding metadata: extensions/memory-core/src/memory/hybrid.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #92061
Summary: This PR is the maintainer-routed validation thread for the original CJK textScore issue, but it partially overlaps separate hybrid preservation and remaining CJK LIKE precision tracks.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🐚 platinum hermit
Patch quality: 🧂 unranked krab
Result: blocked by patch quality or review findings.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Choose and implement the CJK fallback decomposition policy with focused manager-search coverage.
  • [P2] Align the hybrid non-overlap fix with the manager-boundary preservation path or remove that part from this PR.

Risk before merge

  • [P2] Merging the OR-based empty-MATCH fallback can select rows containing only one query token and score them as fully relevant memory hits.
  • [P1] The PR still leaves multi-character CJK LIKE matching as a whole-token exact substring, which the open follow-up issue shows is not the intended relevance behavior.
  • [P1] The overlap merge changes hybrid result identity and can conflict with the separate manager-boundary keyword-only preservation path under review.

Maintainer options:

  1. Repair Retrieval Semantics Before Merge (recommended)
    Require a maintainer-approved CJK decomposition strategy, stricter empty-MATCH filtering, and alignment with the manager-boundary hybrid preservation path before landing.
  2. Use Separate Canonical Tracks
    Pause or close this branch if maintainers prefer the open CJK LIKE issue for fallback semantics and the separate hybrid-preservation PR for minScore behavior.
  3. Accept Partial Recall Risk
    Maintainers can intentionally land the partial improvement, but would own inconsistent CJK memory relevance and context selection until follow-up work lands.

Next step before merge

  • [P2] Maintainers need to choose the CJK relevance semantics and whether this branch or the separate hybrid-preservation PR owns the non-overlap fix.

Security
Cleared: No concrete security or supply-chain concern was found; the diff is limited to parameterized memory search SQL and in-memory result merge logic.

Review findings

  • [P2] Split CJK fallback terms before exact LIKE matching — extensions/memory-core/src/memory/manager-search.ts:126-127
  • [P2] Keep empty-MATCH fallback as a stricter filter — extensions/memory-core/src/memory/manager-search.ts:387
  • [P2] Preserve keyword-only rows after overlap merges — extensions/memory-core/src/memory/hybrid.ts:143-153
Review details

Best possible solution:

Use one canonical manager-search fix for CJK decomposition and empty-MATCH semantics, and keep keyword-only hybrid preservation at the manager selection boundary chosen by maintainers.

Do we have a high-confidence way to reproduce the issue?

Yes for the source/dependency path: current main routes long CJK trigram tokens through MATCH and only falls back on exceptions, matching the reported zero-row behavior. I did not run a full live Ollama-backed memory search in this read-only review.

Is this the best way to solve the issue?

No. This is a plausible partial repair, but it is not the best fix until CJK term decomposition, empty-MATCH fallback semantics, and hybrid keyword-preservation boundaries are corrected and covered.

Full review comments:

  • [P2] Split CJK fallback terms before exact LIKE matching — extensions/memory-core/src/memory/manager-search.ts:126-127
    The patch routes every CJK token to substringTerms, but still pushes a multi-character query such as 飞书插件配置 as one exact substring. The PR body and memory-core: CJK LIKE fallback returns wrong results for multi-character queries #92728 show that this whole-token LIKE shape can still miss or mis-rank the intended CJK memory hits, so the bug is not closed until a chosen split-term strategy is implemented.
    Confidence: 0.9
  • [P2] Keep empty-MATCH fallback as a stricter filter — extensions/memory-core/src/memory/manager-search.ts:387
    This new empty-MATCH branch uses OR-based LIKE predicates and then maps fallback rows to textScore = 1. If MATCH returns no rows because one term is unindexable, rows containing only one query token can now be treated as fully relevant memory hits.
    Confidence: 0.86
  • [P2] Preserve keyword-only rows after overlap merges — extensions/memory-core/src/memory/hybrid.ts:143-153
    When a keyword hit overlaps a vector hit, this code merges it into the vector entry and drops the original keyword-only row. If the merged row does not pass the strict threshold or no longer matches the manager's keyword source/path/line key, the lexical hit can still be omitted; the related manager-boundary PR keeps this preservation decision at the selection layer.
    Confidence: 0.85

Overall correctness: patch is incorrect
Overall confidence: 0.87

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ec737ee74d9b.

Label changes

Label justifications:

  • P2: This is a bounded memory-core retrieval correctness bug with real CJK relevance impact but limited blast radius.
  • merge-risk: 🚨 session-state: The diff changes retrieval and scoring of persisted memory results that can be selected into agent/session context.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🐚 platinum hermit and patch quality is 🧂 unranked krab.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (live_output): The PR body includes terminal-style after-fix OpenClaw memory search output from a real macOS setup for two CJK queries and one English query.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal-style after-fix OpenClaw memory search output from a real macOS setup for two CJK queries and one English query.
Evidence reviewed

PR surface:

Source +69. Total +69 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 2 80 11 +69
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 80 11 +69

What I checked:

Likely related people:

  • Takhoffman: Commit 3ce48af added configurable FTS5 trigram support and adjacent manager-search tests for CJK text support. (role: introduced CJK tokenizer behavior; confidence: high; commits: 3ce48aff660a; files: extensions/memory-core/src/memory/manager-search.ts, extensions/memory-core/src/memory/manager-search.test.ts)
  • AnonAmit: Commit 4259010 changed memory fallback lexical ranking in manager-search, its tests, and the manager search flow adjacent to this fallback behavior. (role: fallback ranking contributor; confidence: medium; commits: 42590106ab6e; files: extensions/memory-core/src/memory/manager-search.ts, extensions/memory-core/src/memory/manager-search.test.ts, extensions/memory-core/src/memory/manager.ts)
  • steipete: Commit cad83db moved the memory engine into the memory-core plugin across the manager-search, manager, and hybrid boundaries involved here. (role: memory plugin boundary refactor contributor; confidence: high; commits: cad83db8b2f7, dbf78de7c680; files: extensions/memory-core/src/memory/manager-search.ts, extensions/memory-core/src/memory/manager.ts, extensions/memory-core/src/memory/hybrid.ts)
  • vignesh07: Commit f57b466 introduced the earlier keyword-hit preservation path that the PR's hybrid overlap merge now interacts with. (role: adjacent keyword-hit fallback contributor; confidence: medium; commits: f57b4669e1d6; files: src/memory/manager.ts, extensions/memory-core/src/memory/manager.ts)
  • vincentkoc: Recent memory-core manager edits and live issue comments route the original CJK issue to this PR and the hybrid non-overlap issue to the separate manager-boundary PR. (role: recent area contributor and issue router; confidence: medium; commits: ca26489fe882, 620537914b8c; files: extensions/memory-core/src/memory/manager.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 12, 2026
Two fixes:

1. Remove CJK < 3 char threshold: all CJK tokens now go to substringTerms
   for LIKE matching instead of being sent as quoted phrase MATCH.

2. Add LIKE fallback when FTS5 MATCH returns 0 rows: previously only
   triggered on MATCH exception. Now also triggers on empty results,
   using OR-based LIKE matching for better recall.

Fixes openclaw#92061
@rrriiiccckkk rrriiiccckkk changed the title fix(memory-core): remove CJK length threshold for trigram LIKE matching fix(memory-core): CJK textScore=0 + empty MATCH LIKE fallback Jun 12, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 12, 2026
@rrriiiccckkk rrriiiccckkk changed the title fix(memory-core): CJK textScore=0 + empty MATCH LIKE fallback fix(memory-core): CJK textScore=0 and empty MATCH LIKE fallback Jun 12, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 12, 2026
@rrriiiccckkk

Copy link
Copy Markdown
Author

Updated PR description with Real behavior proof. Ready for re-review.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 13, 2026
…penclaw#92337

When keyword and vector results have different chunk IDs (common with CJK/trigram queries),
fall back to matching by path + overlapping line range instead of discarding textScore.

Changes:
- Add linesOverlap() helper function
- Add vectorByPath secondary index for O(1) path lookups
- When no chunk ID match: find vector chunk with highest vectorScore on same path with overlapping lines
- Merge textScore into that vector entry (max textScore, expanded line range)
- If no overlap found: preserve original keyword-only behavior

Fixes openclaw#92337
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 13, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 13, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 13, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 13, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 14, 2026
@rrriiiccckkk

Copy link
Copy Markdown
Author

Lint fix pushed. All CI checks passing now. Ready for re-review. @clawsweeper

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 14, 2026
@rrriiiccckkk

Copy link
Copy Markdown
Author

Hi @vincentkoc 👋 — this PR has been ready for review since June 14 (all CI green, mergeable, proof supplied). Could you take a look when you have a moment? Thanks!

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jul 15, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-core Extension: memory-core merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S stale Marked as stale due to inactivity status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

1 participant