docs: realign the public record of how search actually works - #78
Conversation
Semantic search shipped in February and the docs have drifted ever since. July and August improved the corpus (category context, df_keywords, harvested prose, df_notes) while the engine stayed on Ollama, and each document written along the way described a different intended future in the present tense. State, measured: the index is built on demand per provider by `opensdmx embed`; nothing is precomputed and nothing ships. What differs per provider is the corpus, not the capability — only istat.parquet ships, so elsewhere the embedded document is id + title + category context. Latency on ISTAT: ~10 s on the first query while Ollama loads the model, ~1 s after. Correct the claims: keyword default 20 -> 50; "exact substring match on dataset title" -> the scored token match on title, ID and category name; semantic "10 most similar" -> 50; "The model is fixed" -> fixed within a release, and the index does not record which model built it (#57); the 3.4x figure now names its metric. PRD and istat-flow still said search matches "descriptions" — the same wording fixed once on 2026-07-16. Mark the self-contained-backend plan as a direction, not an approved plan: it named embeddinggemma-300m with multilingual-e5-small as fallback and fastembed 0.8.0 exposes neither. Say which model each fastembed result used, so "reverted" (2026-03-31, nomic-embed-text-v1.5-Q) and "validated" (2026-08-04, paraphrase-multilingual-MiniLM-L12-v2) stop reading as a contradiction. `--semantic` no longer drops flags in silence: --category, --page and --all were accepted and ignored, so a user who restricted a search to a category read results drawn from the whole catalogue with no signal. They now fail explicitly. The skill was routing agents to the weaker arm — it offered semantic only after keyword returned fewer than 3 results, and priced it at "10-30 s". On naturally phrased questions the keyword scorer measures MRR 0.041 against 0.252 for semantic. Routing is now conditional on an index existing. Untrack three dated audits of superseded versions, kept public by an accident of .gitignore ordering; evaluation.md still told readers that `search --semantic` needs a Google API key, removed on 2026-04-03. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TekhaUTgxCgKVyiNEBCN36
LOG.md records fastembed tried and reverted (2026-03-31) for poor quality on Italian queries; this section records a fastembed prototype validated (2026-08-04). Different models — nomic-embed-text-v1.5-Q and paraphrase-multilingual-MiniLM-L12-v2 — but neither text said so, so the repo handed a reader two opposite verdicts with nothing to reconcile them. Also mark the plan this prototype was said to confirm: it named embeddinggemma-300m with multilingual-e5-small as fallback, and fastembed 0.8.0 exposes neither, so its premise no longer holds. The prototype was qualitative, on descriptions only, and has never been scored against the targets in docs/search.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TekhaUTgxCgKVyiNEBCN36
|
| Filename | Overview |
|---|---|
| src/opensdmx/cli.py | Rejects unsupported semantic-search options using parameter-source detection, fully addressing the explicit-default bypass. |
| tests/test_cli.py | Adds focused coverage for each rejected option, their combinations, explicit --page 1, and supported --n/--grep. |
| skills/sdmx-explorer/SKILL.md | Revises discovery routing and explicitly continues through the tree-first path when semantic prerequisites are unavailable. |
| README.md | Corrects search defaults, prerequisites, corpus behavior, evaluation claims, and rebuild reproducibility wording. |
| docs/search.md | Documents the current semantic backend, per-provider index, distinct search contracts, measured results, and unresolved backend direction. |
| LOG.md | Records the documentation realignment, semantic flag validation, routing update, and removal of obsolete evaluation records. |
Reviews (2): Last reviewed commit: "fix: address review on #78 — flag detect..." | Re-trigger Greptile
There was a problem hiding this comment.
Pull request overview
This PR realigns public documentation and the sdmx-explorer skill guidance with the current, shipped behavior of opensdmx search (keyword vs semantic via Ollama), and introduces a small CLI behavior change to avoid silently ignoring incompatible flags under --semantic.
Changes:
- Update docs/README/skill references to accurately describe current keyword scoring, semantic search prerequisites (Ollama + per-provider on-demand embeddings), and measured retrieval results.
- Change CLI behavior so
search --semanticrejects keyword-only flags (--category,--page,--all) instead of accepting/ignoring them silently; add tests for these combinations. - Remove/ignore outdated evaluation documents from the public repo surface (delete files, extend
.gitignore).
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_cli.py | Adds regression tests ensuring keyword-only flags are rejected under --semantic, while --n/--grep remain allowed. |
| src/opensdmx/cli.py | Enforces incompatible-flag rejection in the semantic path; improves missing-embeddings messaging. |
| skills/sdmx-explorer/SKILL.md | Updates the agent’s routing guidance and semantic-search expectations (latency, top-N, compatible flags). |
| skills/sdmx-explorer/references/thematic-tree.md | Updates routing table and adds clarified semantic-search prerequisites and behavior. |
| skills/sdmx-explorer/references/istat-flow.md | Corrects search matching description (titles/IDs/categories, not “descriptions”). |
| README.md | Updates public CLI docs and semantic-search section to match actual behavior and defaults. |
| LOG.md | Adds a dated entry documenting the doc realignment and the --semantic flag-behavior change. |
| docs/search.md | Adds an upfront “Current state” section and amends the self-contained-backend plan status. |
| docs/PRD.md | Corrects dataset discovery and semantic-search statements to match the actual implementation. |
| docs/future-ideas.md | Clarifies the historical fastembed attempts and corrects the “approved plan” framing. |
| docs/evaluation.md | Removed (superseded/outdated evaluation doc). |
| docs/evaluation-v0.2.6.md | Removed (superseded evaluation doc). |
| docs/evaluation-v0.14.0.md | Removed (superseded evaluation doc). |
| .gitignore | Ensures docs/evaluation.md stays untracked going forward. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…rstated claims - `--page 1` was accepted under `--semantic`: the value equals the default, so a value comparison cannot tell "not given" from "given as 1". Read the parameter source instead. Uses `ParameterSource.name` rather than importing the enum, since `click` is not a declared dependency. - The "no index" message named the provider only when `--provider` was passed on the command line, so a provider selected via OPENSDMX_PROVIDER or by default produced a hint the user could not act on. Use the resolved cache key, which is what actually determines the index path. - README claimed a rebuild "reproduces the same index". Only the *model* is fixed within a release: `build_embeddings` re-reads the catalog, the category tree and the harvested descriptions as they are at that moment, so a rebuild after the caches refresh embeds different text. - SKILL.md told the agent to probe for an index with `search --semantic`, which fails on the Ollama check first — so a stopped server was indistinguishable from a missing index. Name both error messages and, either way, fall back to the tree-first path instead of stopping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TekhaUTgxCgKVyiNEBCN36
|
Thanks for the thorough writeup — the All four fixes look well-reasoned. Nothing further from me on this PR. |
Semantic search shipped in February 2026 and the documentation has drifted ever since. This PR makes the public docs describe what the code does, and marks the plans that are no longer executable as such.
Why the docs drifted
July and August improved the corpus — category context (v0.17.0),
df_keywords(v0.19.0), harvested ISTAT prose (v0.20.0),df_notes— while the engine stayed on Ollama. Each document written along the way described a different intended future in the present tense, and none was amended when that future changed. The result was five public documents describing five different states.The state, measured
search --semanticworks. Backend: Ollama withnomic-embed-text-v2-moe, a hardcoded constant inembed.py. No flag, no env var, no fallback backend.opensdmx embedinto<cache>/<provider>/embeddings.parquet. Nothing is precomputed and nothing ships in the package.df_prosecomes from adata/descriptions/<provider>.parquetinside the wheel and onlyistat.parquetexists, so elsewhere the embedded document is id + title + category context. The eval numbers are an ISTAT ceiling, not a per-provider promise.Corrections to public claims
README.mdcli.py)README.mdREADME.md--nto changeREADME.mdREADME.mddocs/PRD.md,references/istat-flow.mdThe "descriptions" wording is the same error
LOG.mdrecords fixing on 2026-07-16; dataflows expose an SDMXName, not aDescription.docs/search.mdgains a Current state section up front, and its "already-approved plan" claim is amended (below).The self-contained-backend plan is a direction, not an approved plan
Its premise is falsified and nothing said so:
google/embeddinggemma-300mwithmultilingual-e5-smallas fallback. fastembed 0.8.0 exposes neither.nomic-embed-text-v1.5-Q), for poor quality on Italian queries.So the repo carried both "fastembed reverted" (
LOG.md) and "fastembed validated" (docs/future-ideas.md) with nothing to reconcile them. Each entry now names its model. No candidate has been scored against the targetsdocs/search.mdsets — MRR 0.135 to beat BM25, ~0.327 to replace nomic — which is tracked as #77.Behaviour change:
--semanticno longer drops flags in silence--category,--pageand--allwere accepted and ignored — the semantic branch returns before the code that reads them. A user who restricted a search to a category read results drawn from the whole catalogue, with no signal that the filter had been dropped. They now fail with an explicit error;--nand--grepare unaffected.The "cache not found" message now says the index is per-provider and suggests the right
--provider. Ollama-unreachable was already reported distinctly —_check_ollama()runs before the parquet read — verified withOLLAMA_HOSTpointed at a dead port.The skill was routing agents to the weaker arm
SKILL.mdtold the agent to offer--semanticonly after keyword returned fewer than 3 results, and priced it at "10–30 s". Measured on ISTAT (eval/results/2026-08-10/retrieval.md):On English queries against Italian metadata the keyword path scores 0.012 and semantic 0.310.
Routing is now conditional: where an index exists and the question is in natural language, semantic goes first; otherwise the
tree-first default stands. This follows the measurement without promising what the install does not provide.Removed from the repository
docs/evaluation.md,docs/evaluation-v0.14.0.md,docs/evaluation-v0.2.6.md— dated audits of superseded versions, kept public by an accident of.gitignoreordering (docs/evaluation-v*.mdwas added after two of them were already committed).evaluation.mdstill told readers thatsearch --semanticrequires a Google API key, removed on 2026-04-03.Untracked, not deleted;
.gitignoreextended so they stay out. No public document links to them.Issues
Closed #53 (ISTAT descriptions from METADATA_API — shipped in v0.20.0, refreshed monthly, last run 2026-08-01 green).
Opened #75 (
[semantic]extra), #76 (BM25 onsearch_dataset, +85% MRR, no new dependency), #77 (static retriever measured against the targets).Verification
ruff clean · mypy strict clean on 15 files · 384 tests green, 5 new covering the rejected flag combinations.
Not addressed, and worth stating: the
doc-firstcounterweight block in the gold set, and measuring the extended-text gain on providers other than ISTAT. Both are prerequisites for reading the eval numbers as anything other than an ISTAT ceiling.🤖 Generated with Claude Code
https://claude.ai/code/session_01TekhaUTgxCgKVyiNEBCN36