feat(search): grep fallback when semantic recall is empty - #2938
feat(search): grep fallback when semantic recall is empty#2938lg320531124 wants to merge 1 commit into
Conversation
|
CI note: the The failure is identical across all currently-open PRs (#2874, #2936, #2937, #2938) and reproduces on #2874 which predates my other work:
This PR is pure-Python (touches only Evidence that maintainers are not blocking on this flake: #2934 was merged today (2026-07-02 01:07 UTC) with the same Local unit tests for this PR pass (see PR body). Happy to help root-cause the maturin/upstream flake separately if useful — just let me know. |
MemorySearchTool.execute returned [] as soon as semantic search yielded
no memories, leaving the agent with no escape hatch — there is no grep
tool exposed in the memory toolset (only read/search/ls), so a query
that the embedder or index failed on was a dead end even when the
literal string exists in stored content.
Retry with a literal grep when semantic recall is empty:
- pattern = re.escape(query) # match as literal substring, not regex
- case_insensitive=True # belt-and-suspenders over query casefold
- node_limit = limit + 10 # match the over-sampling search already does
grep matches ({line, uri, content}) are projected onto the {uri, score}
memory shape (score=0.0 honestly marks "no relevance score, literal
match") so optimize_search_result can filter .abstract.md/.overview.md
and truncate uniformly.
Guarded edges:
- empty query skips grep (re.escape('') == '' would match everything)
- grep exceptions are swallowed and logged; the tool still returns []
instead of propagating a partial failure
This is orthogonal to volcengine#2900, which hardens viking_fs.grep itself
(VikingDB timeout/empty -> fs). This PR is the Tool-layer complement:
when semantic search *as a whole* returns nothing, drop to grep.
Tests: tests/unit/test_search_grep_fallback.py (6 cases, all pass) cover
results-present (no grep), empty->grep mapping, both-empty, grep raises,
empty query, and regex-escaping of metacharacters.
94c1d32 to
c649a42
Compare
|
@qin-ctx 更正我上一版关系说明:#2874 本身是独立的 tool-input-compaction PR,不是本 PR 的 issue;#2938 是独立的 semantic-search→grep fallback PR。 我对 exact head c649a42 重新做了 current-main(4295dfd) 合成验证:无冲突,focused tests 6/6 通过,Ruff format 通过。但目前有一个运行时 blocker:ToolContext.default_search_uris 的真实类型是 List[str],而 VikingFS.grep(uri=...) 只接受 str;新增测试把 default_search_uris 模拟成了字符串,掩盖了这个类型错配。请按每个 scope URI 逐一 grep(或明确选择一个 URI并说明语义),并用真实 ToolContext 覆盖多 URI/空 URI;另外删除未使用的 pytest 导入,当前 Ruff check 报 F401。 这些修正完成后再请求 review;当前不建议合并。 |
Problem
MemorySearchTool.executereturns[]the moment semantic search yields no memories. The agent has no escape hatch: the memory toolset exposes onlyread/search/ls(nogrep), so a query the embedder or index fumbled is a dead end even when the literal string exists verbatim in stored content.Change
When semantic recall is empty, retry with a literal grep:
re.escape(query)→ matches as a literal substring, not interpreted as regex (a query likea.b*c+would otherwise be a regex that matches almost nothing, or worse, everything)case_insensitive=Trueis redundant over the query-side casefold from feat(search): normalize semantic query (NFKC + casefold) #2937 but harmless — they act on different sides (query vs indexed content)score=0.0honestly marks "no relevance score, literal match" rather than fabricating a ranking scoreoptimize_search_resultthen filters.abstract.md/.overview.mdand truncates tolimituniformly — no new code path in the optimizerGuarded edges
re.escape("") == ""matches everything; guard prevents that[], not a propagated crashRelationship to #2900
Orthogonal, not overlapping:
viking_fs.grepitself (VikingDB timeout / empty-candidates → fs fallback at the storage layer)Both can land independently; together they make the full path robust.
Tests
tests/unit/test_search_grep_fallback.py— 6 cases, all pass:{uri, score}; pattern is escaped; case_insensitive=True[][], no propagationa.b*c+→a\.b\*c\+Pure unit tests (mocked
ctx.viking_fs), no vectordb/embedder fixture — the tool logic is side-effect-free string/URI handling.Scope
+37 lines in
tools.py(one method body), 1 new test file. No new tool class, no public API change, no config knob.Related
Search-recall robustness line: #2937 (query normalization) → this PR (semantic→grep fallback) → #2900 (grep self-hardening, storage layer).