Skip to content

feat(knowledge): retrieval quality improvements (verbatim search, rerank topN, per-query topK, qwen3 prefix)#17166

Draft
eeee0717 wants to merge 5 commits into
mainfrom
review-enterpriserag-bench
Draft

feat(knowledge): retrieval quality improvements (verbatim search, rerank topN, per-query topK, qwen3 prefix)#17166
eeee0717 wants to merge 5 commits into
mainfrom
review-enterpriserag-bench

Conversation

@eeee0717

Copy link
Copy Markdown
Collaborator

What this PR does

Bundles four focused knowledge-base retrieval-quality improvements that were developed and validated together on a retrieval benchmark.

Before this PR:

  • kb_search rewrote the query into keywords before ever trying the user's verbatim text, so exact-match passages were often missed on the first search.
  • Rerank topN was decoupled from the search topK, so reranking could operate over a different candidate count than intended.
  • Callers could not set a per-query result count for kb_search; every query used the base default.
  • Query embeddings for instruction-aware asymmetric models (Qwen3-Embedding) were embedded as plain text — off the model's training distribution — weakening query/document alignment.

After this PR:

  • kb_search tries the verbatim query first, then falls back to keyword rewriting.
  • Rerank topN is aligned with the search topK.
  • Callers can pass a per-query topK (result count) to kb_search; default behavior is unchanged when omitted.
  • Qwen3-Embedding queries carry the model's official instruct prefix; documents stay plain, so existing indexes remain valid and no re-embedding is needed.

Fixes # N/A

Why we need it and why it was done in this way

All four target the same goal — higher first-pass retrieval recall for the knowledge base — and were measured on a 500-question retrieval benchmark. The query-side Qwen3 instruct prefix alone lifted overall Recall@10 by ~4pt with no regression.

The following tradeoffs were made:

  • Qwen3 prefix is query-only. Instruction-aware models (Qwen3) train the instruct on the query while documents stay plain. Applying it only in embedKnowledgeQuery keeps indexed chunks plain, so existing indexes stay valid and the corpus does not need re-embedding.
  • Per-model prefix registry. The prefix lives in a small provider::model registry so more query-prefix models can be added with one entry, without touching the embedding path.
  • Per-query topK is optional. Omitting it preserves the previous default, so existing IPC/tool callers are unaffected.

The following alternatives were considered:

  • Both-side prefix models (E5, nomic). Deliberately excluded from the registry: they also need a document-side prefix, which requires prefixing documents at index time and re-embedding the corpus — a separate, heavier change.

Links to places where the discussion took place: N/A

Breaking changes

None. The Qwen3 prefix affects query embeddings only (indexes unchanged); the per-query topK is an optional parameter that defaults to prior behavior.

Special notes for your reviewer

Kept as a draft while the associated benchmark round finishes. The four commits are individually scoped (verbatim-first search, rerank topN alignment, per-query topK, Qwen3 query prefix) and can be split into separate PRs if preferred for review.

Checklist

  • Branch: This PR targets the correct branch — main for active development, v1 for v1 maintenance fixes
  • PR: The PR description is expressive enough and will help future contributors
  • Code: Write code that humans can understand and Keep it simple
  • Refactor: You have left the code cleaner than you found it (Boy Scout Rule)
  • Upgrade: Impact of this change on upgrade flows was considered and addressed if required
  • Documentation: A user-guide update was considered and is present (link) or not required.
  • Self-review: I have reviewed my own code before requesting review from others

Release note

Improve knowledge base retrieval quality: verbatim-first search in the kb_search tool, an optional per-query result count, rerank topN aligned with search topK, and an instruction prefix for Qwen3-Embedding query embeddings (query-only, no re-embedding required).

eeee0717 and others added 5 commits July 17, 2026 11:34
The kb_search tool schema taught the model to keyword-ise and split
queries: describe said "Self-contained keyword search" and the query
max(200) error told it to "break long questions into multiple searches".
Against the hybrid (vector + BM25) retrieval pipeline this discards the
model's best vector input — the user's full natural-language question —
and the 200-char cap hard-blocks longer questions outright.

Re-guide both the shared schema and KNOWLEDGE_SEARCH_DESCRIPTION to
search the full question verbatim first, refining with keywords /
synonyms / sub-questions only when the first results fall short, and
raise the query cap to 500. Add coverage guidance so multi-entity /
exhaustive questions gather evidence per sub-question instead of
stopping at the first batch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
rerankKnowledgeSearchResults resolved its topN from a second, independent
`documentCount ?? DEFAULT_DOCUMENT_COUNT` (6), while KnowledgeService.search
trims to `documentCount ?? 10`. When documentCount is unset the reranker
was asked for only 6 candidates, silently capping search results at 6
instead of 10 whenever a rerank model was configured.

Pass the caller's resolved topK into the reranker and drop the internal
fallback so the request size and the trim can never diverge. Add a
regression test covering the documentCount-unset case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
kb_search always returned the base's fixed documentCount (or 10) results,
so a caller could not ask for more on questions that need broad coverage
— multiple entities, project-level roundups, exhaustive answers — where a
handful of results is not enough evidence.

Thread an optional per-query count into KnowledgeService.search, honored by
every caller of that method with one precedence: `topK ?? documentCount ??
10`. When set it overrides the base documentCount for that call; when omitted
the base default is unchanged. The three search surfaces now agree:

- kb_search tool: new optional `topK` (1–50) on the input. Mirror the kb_list
  two-schema split so the strict AI-SDK path keeps topK in `required` via
  `.nullable()` while the MCP/renderer path may omit it.
- `knowledge.search` IPC route: new optional `topK` (canonical doc-count schema).
- REST `/knowledge-bases/search`: `document_count` now feeds the per-base search
  (previously ignored there) and is optional (previously a forced default of 5).
  It still adds a cross-base cap when provided — a flat-list concern specific to
  the gateway that the tool path, which reads results itself, does not need.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Qwen3-Embedding is an instruction-aware, asymmetric retrieval model:
queries carry a task instruct prefix while documents stay plain. We were
embedding queries plain — off the model's training distribution — which
weakened query-document alignment.

Add the official instruct prefix to query embeddings only, via a small
per-model registry so other query-prefix models can be added with one
entry. Documents are untouched (embedKnowledgeTexts unchanged), so
existing indexes stay valid and no re-embedding is needed. Models that
also need a document-side prefix (E5, nomic) are intentionally excluded:
they require re-embedding the corpus and are a separate change.

On EnterpriseRAG-Bench (500q standalone retrieval, qwen3-embedding:0.6b)
this lifts overall Recall@10 +4.34pt, semantic +3.2pt, basic +6.85pt,
MRR +4.79pt vs no prefix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Generate bounded, single-span retrieval projections during vector indexing with the configured Quick Model while failing open to authoritative raw indexing when generation is unavailable or invalid.

Store projections in the per-base index and use their best cosine score per raw unit without exposing generated text to BM25, reranking, or answer evidence.

Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
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