Skip to content

feat(search): tiered BM25 search (briefs/summaries/sources) + taxonomy accessors - #259

Open
sebastianbraun25 wants to merge 3 commits into
VectifyAI:mainfrom
sebastianbraun25:feat/issue-233-tiered-search
Open

feat(search): tiered BM25 search (briefs/summaries/sources) + taxonomy accessors#259
sebastianbraun25 wants to merge 3 commits into
VectifyAI:mainfrom
sebastianbraun25:feat/issue-233-tiered-search

Conversation

@sebastianbraun25

Copy link
Copy Markdown

Note

This PR was created in collaboration between a human and AI: implementation, tests, and
PR text were created by an AI assistant under the guidance and review of the human author.

Problem

search_wiki (#233/#234) is a single BM25 index over concepts/, entities/, and
summaries/ combined. Concepts/entities are meant to be found by semantic browsing (an
LLM scanning one-line briefs and picking a slug by meaning), not keyword search — but they
share one BM25 corpus with summaries today, so a keyword-dense concept page can outrank the
summary a caller actually needed. Separately, one combined BM25 tier conflates two very
different recall needs (a generic term like "case" wants precision over which document is
about that topic; a specific fact like an exact field name wants recall into full
document bodies), and long PageIndex documents (sources/*.json) aren't searchable at all
today, even though details like authorship/creation dates only ever live there.

Solution / Changes

  • openkb/fulltext_index.py:
    • Extracted _BM25Scorer from WikiFullTextIndex (pure refactor, same BM25 math, no
      behavior change — WikiFullTextIndex/search() keep their exact existing signature
      and output for backward compatibility with the current search_wiki tool).
    • New Locator (kind: "line"|"page", value: int) on SearchHit, computed at query
      time (first-match line) or fixed at indexing time (PageIndex page number).
    • New TieredWikiSearch with three independent BM25 tiers, deliberately excluding
      concepts/entities (see list_taxonomy_items below):
      1. briefs — one-line description/legacy brief frontmatter per summaries/*.md.
      2. summaries — full body of summaries/*.md (frontmatter stripped).
      3. sourcessources/*.md (whole file, line locator) + sources/*.json PageIndex
        docs, indexed per page (never per document), each hit carrying
        Locator(kind="page", value=N) directly usable with
        get_page_content(doc_name, pages=str(N)). Malformed/foreign JSON under sources/
        is skipped, not raised.
    • search(query, scope=None, top_k=5) returns {tier: [SearchHit, ...]}; scope
      restricts to a subset of TIERED_SCOPES = ("briefs", "summaries", "sources").
  • openkb/frontmatter.py: new resolve_description(fm) / body_only(text) shared
    helpers. Kept as a separate copy from agent.compiler._resolve_description (same logic)
    rather than importing from compiler.py, which has several other open, unrelated PRs
    actively touching it.
  • openkb/agent/tools.py: new list_taxonomy_items(wiki_root, kind=None) /
    get_taxonomy_item(slug, wiki_root, kind=None) for semantic browsing of persisted
    concepts//entities/ pages. Only ever surfaces committed .md pages —
    PendingTopicsStore's not-yet-paged candidates are a structurally separate buffer and
    are never returned.
  • No CLI, MCP, or query/chat-agent wiring in this PR — pure, additive core logic. Follow-up
    PRs (tracked separately) wire this into openkb list-taxonomy/openkb search, the
    query/chat agent's tool set, and a new MCP server.

Testing

  • tests/test_fulltext_index.py: 8 existing tests unchanged/still passing (backward
    compatibility of WikiFullTextIndex); +12 new tests for TieredWikiSearch (briefs vs.
    full-body matching, frontmatter exclusion from body search, line/page locators,
    malformed-JSON tolerance, scope validation, concepts/entities never searched).
  • tests/test_agent_tools.py: +13 new tests for list_taxonomy_items/get_taxonomy_item
    (kind filtering, legacy brief: fallback, missing directories, path-traversal
    rejection, disambiguation by kind).
  • ruff check, ruff format --check, mypy openkb, pytest all green — full suite: 1267
    passed (18 pre-existing, environment-specific failures on Windows unrelated to this
    change, e.g. POSIX-only symlink/chmod-mode tests, are unaffected and unchanged in count).

Dependencies

Issues

Resolves #258

Sebastian Braun added 3 commits August 28, 2026 16:25
Adds a dependency-free BM25 full-text index (openkb/fulltext_index.py) over concepts/entities/summaries pages, exposed as a new search_wiki tool alongside index.md-driven navigation in build_query_agent. Additive hybrid retrieval: surfaces pages whose one-line index summary omits a buried detail, without replacing existing navigation. Resolves VectifyAI#233.
…axonomy accessors

- fulltext_index.py: extract shared _BM25Scorer from WikiFullTextIndex
  (no behavior change), add Locator (line/page) on SearchHit, add
  TieredWikiSearch with three independent tiers over summaries/ (briefs +
  full body) and sources/ (whole-file .md + per-page PageIndex .json,
  never the whole long doc as one BM25 unit).
- frontmatter.py: add resolve_description()/body_only() shared helpers
  (kept separate from agent.compiler._resolve_description, which is under
  active unrelated development).
- agent/tools.py: add list_taxonomy_items()/get_taxonomy_item() for
  semantic browsing of persisted concepts/entities (pending candidates
  in PendingTopicsStore are structurally excluded).
- No wiring into CLI/MCP/query-agent yet (follow-up PRs); WikiFullTextIndex
  and agent.tools.search_wiki keep their existing signature/behavior.
sebastianbraun25 pushed a commit to sebastianbraun25/OpenKB that referenced this pull request Sep 11, 2026
…o query/chat agent

- cli.py: new 'openkb list-taxonomy [--kind concept|entity] [--json]' and
  'openkb search <query> [--scope briefs,summaries,sources] [--top-k N]
  [--json]' commands.
- agent/tools.py: search_wiki now searches the new tiered
  briefs/summaries/sources index instead of the old combined
  concepts+entities+summaries index (concepts/entities move to the new
  list_taxonomy tool - semantic browsing, not keyword search); new
  list_taxonomy() text-formatting wrapper over list_taxonomy_items().
- agent/query.py (+ chat.py via tool inheritance): wires list_taxonomy and
  the retiered search_wiki in as agent tools; search strategy instructions
  updated to browse taxonomy first, then use scope-restricted search_wiki
  as a keyword fallback.
- README.md: updated hybrid-retrieval paragraph and command table.
- Intentional behavior change to agent.tools.search_wiki (scope param,
  concepts/entities out of scope, output grouped by tier) - safe since
  VectifyAI#234/VectifyAI#259, which introduced it, are not yet merged upstream; existing
  tests updated to the new contract.
sebastianbraun25 pushed a commit to sebastianbraun25/OpenKB that referenced this pull request Sep 11, 2026
- skills/openkb/SKILL.md: 'See what's available' now leads with
  list_taxonomy (MCP) / 'openkb list-taxonomy' (CLI) before falling back
  to reading the full index.md.
- 'Read content' table adds search_wiki (MCP) / 'openkb search' (CLI)
  rows ahead of the existing grep fallback, with a note on why BM25
  ranking beats raw grep occurrence count.
- 'When the KB doesn't have the answer' and the openkb-query guidance
  updated to reference the new search options alongside grep.
- Documentation-only change; no behavior change to the underlying
  tools/CLI/MCP server (VectifyAI#259/VectifyAI#261/VectifyAI#263).
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.

feat(search): tiered BM25 search (briefs/summaries/sources) + taxonomy accessors

1 participant