Repository navigation
Conversation
The original tokenizer splits CJK into single characters, so phrase-level keyword recall fails for Chinese: query 检索 matches a document that merely contains 检 and 索 inside different words, and that decoy can outrank the genuinely relevant chunk. - new loci.tokenizers registry: "default" (original behavior, no deps) and "jieba" (word-level segmentation, lazy import, fails open to default when the optional package is missing) - BM25 accepts an injected tokenizer; module-level tokenize() kept as a backward-compatible alias (conftest FakeEmbedder relies on it) - [bm25] tokenizer config section wired through Retriever; unknown names warn and fall back to default at load time - bench gains --tokenizer so A/B runs need no config edits A/B on a decoy corpus (query 检索; doc b contains 检+索 in other words): default ranks decoy 0.416 > correct 0.325; jieba ranks correct 0.595 and zeroes the decoy. 10 new tests in tests/test_tokenizers.py.
…extra) - src/loci/webui.py: chat pane + per-answer evidence panel reusing build()/make_retriever() from the CLI pipeline; ask_fn keeps the last four turns as history; format_evidence() renders source > section - cli: new `webui` subcommand with --host/--port overrides - config: [webui] section (host 127.0.0.1, port 7860) - pyproject: ui = ["gradio>=4"] optional extra (no new runtime deps) - tests/test_webui.py: 10 offline tests (evidence formatting, ask callback, history pass-through, app construction, config defaults)
…g prompt, --top_k alias, missing-extra guidance - retriever: max_per_doc (default 2) caps chunks per source document in one result list, applied after RRF fusion/rerank so surplus chunks give way to other docs; [retrieval] max_per_doc config (0 = no cap) - retriever: SYSTEM_PROMPT rewritten as 4 strict rules — excerpts only, explicit "not in KB" answer, bullet structure, mandatory Sources line - cli: search/ask/bench accept -k/--top_k alias (dest=k keeps compat) - webui: evidence panel shows document name > section (full path stays in the answer citations) - loaders/tokenizers: missing pdf/docx/ocr/jieba extras now warn once per run with the exact pip install command instead of skipping silently - tests: +14 (diversity cap, prompt rules, --top_k wiring, dep warnings)
|
Taking the tokenizer half of this in #4, with your commit intact and co-authored — the registry, config validation, One thing I changed, with numbers. So Also fixed the same bug in the latin half: Three things I still want your read on, from the rest of this PR:
Thanks for the careful commit messages — they are why this took an hour instead of a day. 分词器这部分我接手了,开在 #4,你的 commit 原样保留、co-author 也是你(注册表、配置校验、 |
…ecision for recall Ports the tokenizer registry from @hulinming's #2 (`[bm25] tokenizer` in config, `bench --tokenizer` for A/B runs, fail-open on missing extras), and changes the CJK implementation that ships as the recommended option. Word-level segmentation that relies on a dictionary only matches on the dictionary's terms. jieba splits 全文检索 into one token, so a 检索 query returns nothing at all — the opposite failure to the single-char tokenizer, which matches 检 + 索 inside 定期检查索引 and ranks that decoy above the real document. Overlapping bigrams fix both without a dependency: 检索 → ['检索'] 全文检索 → ['全文', '文检', '检索'] ← the phrase is still in there 定期检查索引 → ['定期','期检','检查','查索','索引'] ← no 检索 bigram Measured on the two-document decoy corpus: single-char puts decoy 0.379 above correct 0.351, jieba scores correct 0.0 (no match), bigrams score correct 0.666 and drop the decoy entirely. jieba's own advertised case (混合检索) still passes under bigrams. `default` also indexes the joined form of hyphenated compounds now, so a `wifi` query finds `Wi-Fi` — the two spellings were mutually invisible before, which is the same class of bug in the latin half. - tokenizers.py: registry + default / cjk_bigram / jieba, all dependency-free except jieba (optional, fails open with a one-time install hint) - bm25.py: accept an injected tokenizer, keep `tokenize()` as the default alias so existing callers and conftest's FakeEmbedder are untouched - retriever.py/config.py/cli.py: wire [bm25] tokenizer, validate the name with a stderr fallback, and add `bench --tokenizer` for same-cases A/B - tests: 21 tokenizer cases — decoy regression, phrase-inside-word recall, single-char runs, latin runs, hyphen joins, registry and config fallback Co-authored-by: hlm <2514266720@qq.com>
…ecision for recall Ports the tokenizer registry from @hulinming's #2 (`[bm25] tokenizer` in config, `bench --tokenizer` for A/B runs, fail-open on missing extras), and changes the CJK implementation that ships as the recommended option. Word-level segmentation that relies on a dictionary only matches on the dictionary's terms. jieba splits 全文检索 into one token, so a 检索 query returns nothing at all — the opposite failure to the single-char tokenizer, which matches 检 + 索 inside 定期检查索引 and ranks that decoy above the real document. Overlapping bigrams fix both without a dependency: 检索 → ['检索'] 全文检索 → ['全文', '文检', '检索'] ← the phrase is still in there 定期检查索引 → ['定期','期检','检查','查索','索引'] ← no 检索 bigram Measured on the two-document decoy corpus: single-char puts decoy 0.379 above correct 0.351, jieba scores correct 0.0 (no match), bigrams score correct 0.666 and drop the decoy entirely. jieba's own advertised case (混合检索) still passes under bigrams. `default` also indexes the joined form of hyphenated compounds now, so a `wifi` query finds `Wi-Fi` — the two spellings were mutually invisible before, which is the same class of bug in the latin half. - tokenizers.py: registry + default / cjk_bigram / jieba, all dependency-free except jieba (optional, fails open with a one-time install hint) - bm25.py: accept an injected tokenizer, keep `tokenize()` as the default alias so existing callers and conftest's FakeEmbedder are untouched - retriever.py/config.py/cli.py: wire [bm25] tokenizer, validate the name with a stderr fallback, and add `bench --tokenizer` for same-cases A/B - tests: 21 tokenizer cases — decoy regression, phrase-inside-word recall, single-char runs, latin runs, hyphen joins, registry and config fallback Co-authored-by: hlm <2514266720@qq.com>
…ecision for recall Ports the tokenizer registry from @hulinming's #2 (`[bm25] tokenizer` in config, `bench --tokenizer` for A/B runs, fail-open on missing extras), and changes the CJK implementation that ships as the recommended option. Word-level segmentation that relies on a dictionary only matches on the dictionary's terms. jieba splits 全文检索 into one token, so a 检索 query returns nothing at all — the opposite failure to the single-char tokenizer, which matches 检 + 索 inside 定期检查索引 and ranks that decoy above the real document. Overlapping bigrams fix both without a dependency: 检索 → ['检索'] 全文检索 → ['全文', '文检', '检索'] ← the phrase is still in there 定期检查索引 → ['定期','期检','检查','查索','索引'] ← no 检索 bigram Measured on the two-document decoy corpus: single-char puts decoy 0.379 above correct 0.351, jieba scores correct 0.0 (no match), bigrams score correct 0.666 and drop the decoy entirely. jieba's own advertised case (混合检索) still passes under bigrams. `default` also indexes the joined form of hyphenated compounds now, so a `wifi` query finds `Wi-Fi` — the two spellings were mutually invisible before, which is the same class of bug in the latin half. - tokenizers.py: registry + default / cjk_bigram / jieba, all dependency-free except jieba (optional, fails open with a one-time install hint) - bm25.py: accept an injected tokenizer, keep `tokenize()` as the default alias so existing callers and conftest's FakeEmbedder are untouched - retriever.py/config.py/cli.py: wire [bm25] tokenizer, validate the name with a stderr fallback, and add `bench --tokenizer` for same-cases A/B - tests: 21 tokenizer cases — decoy regression, phrase-inside-word recall, single-char runs, latin runs, hyphen joins, registry and config fallback Co-authored-by: hlm <2514266720@qq.com>
|
Landed in #4 — your commit is on Closing this one as superseded, not rejected. The reason is purely mechanical: the branch's base commit ( Three things from the rest of this PR are still worth doing, and I would rather keep them than lose them:
I am happy to open those three, or to hand you the branches if you would rather write them yourself — say which and I will set it up. Thanks for the care in those commit messages. A PR that sat for three weeks and still ported in an hour is a PR written by someone who thought about the reviewer. 同样的话用中文再说一遍:#4 已合并,你那个 commit 以你的名义在
|
Summary
A batch of enhancements focused on Chinese-language retrieval and usability. All changes are backward compatible — the original dependency-free single-char CJK tokenizer stays the default, and every addition ships as an optional extra.
1. Pluggable BM25 tokenizers with jieba Chinese word segmentation
src/loci/tokenizers.py: tiny registry (default/jieba); jieba is imported lazily and falls back todefaultwith a one-time install hint (fail-open).[bm25] tokenizer = "default" | "jieba"; unknown names warn and fall back todefault.pip install 'loci-rag[zh]'— no new runtime dependency.2. Per-document diversity cap
Retrieverkeeps at mostmax_per_docchunks (default 2) per source document in one result list, applied after RRF fusion/rerank/filters so surplus chunks give way to other relevant documents.[retrieval] max_per_doc(0 = no cap, preserves the original behavior).3. Gradio Web UI (optional)
loci webuicommand: chat pane + per-answer evidence panel that reusesbuild()/make_retriever()(same pipeline as the CLI and HTTP API); evidence shows document name > section.pip install 'loci-rag[ui]'.4. Smaller items
SYSTEM_PROMPTrewritten as strict grounding rules: excerpts only, explicit "not in KB" answer instead of guessing, bullet structure, mandatorySources:citations.search/ask/benchaccept--top_kas a long-form alias of-k(sameargs.k, no behavior change).pip installcommand instead of skipping files silently..gitignore: added.venv/.Tests
importorskip-guarded), +24 new tests in this branch.