Skip to main content
Glama
SodaShikenn

doc-search

Official
by SodaShikenn

doc-search — Hybrid Search + RAG Chat for Document Repositories

A hybrid search engine combining keyword search (BM25) × vector search (semantic search) for an internal document repository (glossary, review criteria, design documents), plus a RAG chat (Claude API, model selection, streaming output) built on top of it.

The UI design follows SodaShikenn/LLM-RAG_KBQA (left sidebar: model selection / knowledge settings / conversation history; right: chat + Send / Cancel).

Four ways to use it:

  1. RAG chat (/) — Pick a model and ask a question. Search → stream answers with citations

  2. Search explorer (/search.html) — Incremental search, KW/VEC/RRF score display

  3. CLI — docsearch search "..."

  4. MCP server — Registered as a tool for Claude Code (Agentic RAG)

Setup (lightweight: no ML dependencies, ~30MB)

cd doc-search
brew install uv        # 未導入の場合
uv venv --python 3.12 .venv
uv pip install -p .venv/bin/python -r requirements.txt
cp .env.example .env   # ANTHROPIC_API_KEY を記入(チャット用)

Only when using a local embedding model (e5 / bge-m3), add the heavy ML stack:

uv pip install -p .venv/bin/python -r requirements-local.txt

Related MCP server: LLMDoc

Usage

# 1) インデックス構築
.venv/bin/python -m docsearch index sample_docs                    # 自動選択
.venv/bin/python -m docsearch index /path/to/docs --embedder voyage  # クラウド埋め込み

# 2) サーバー起動 → http://127.0.0.1:8765
.venv/bin/python -m docsearch serve --port 8765

# 3) CLI検索
.venv/bin/python -m docsearch search "解約率" --mode vector

Even without an API key, you can verify the chat UI behavior with the "Demo (offline)" model.

Choosing an embedding model (--embedder)

name

Location

Weight

Characteristics

voyage

Cloud

Zero local dependencies

voyage-3.5. Anthropic's recommended embedding partner. Top-tier quality. Requires VOYAGE_API_KEY. Note that document text is sent externally, so approval is needed

e5

Local

~470MB + torch

multilingual-e5-small. Fully local, a safe default for Japanese/English support

e5-large

Local

~2.2GB + torch

Higher-accuracy version of e5

bge-m3

Local

~2.3GB + torch

The strongest local-class multilingual model. However, it is the opposite of "lightweight" and CPU inference is also slow

hash

Local

Zero dependencies

Character-hash (no semantic search, degraded mode)

How to choose: If you want both quality and lightweight setup, go with voyage (when cloud is permitted). If it must be fully local, use e5; for higher accuracy, bge-m3 (if you can tolerate the weight). BM25 (lexical matching) always runs locally, so the embedding's role is only to absorb "paraphrases" — model differences only matter there, and bge-m3's multi-vector/sparse features are unnecessary in this setup.

How chat (RAG) works

質問 → 検索の深さ(effort)を解決(auto は確信度シグナルで自動判断)
     → 検索実行(hard は選択モデルがクエリを言い換え → 全変種を検索して RRF 融合)
     → system プロンプトに参照資料として注入([n] path:line 付き)
     → Claude API へストリーミング要求(output_config.effort も連動)
     → data: {status|sources|delta|done|error} を SSE 配信
     → UI が逐次描画 + 「なぜこの検索をしたか」の説明 + 引用チップ。会話は localStorage

Search depth (effort) — hiding hybrid/keyword/vector

Users don't choose IR terminology. They only choose "how thoroughly to search," and what was actually done is shown in Japanese below the answer (e.g., "Auto → Thorough — no keyword matches… generated paraphrases and searched deeply").

effort

Behavior

When to use

Auto (auto)

Probes once, then auto-selects easy/medium/hard based on confidence

Default. Use this when unsure

Easy (easy)

One hybrid search, top 4 results. Model effort is also low

Direct term lookup. Fastest and cheapest

Medium (medium)

Standard hybrid search, 6 results

Previous default behavior

Thorough (hard)

The selected model generates 3 paraphrases → searches with all queries and fuses via RRF, 10 results. Model effort is high

Questions where the wording differs from the documents (e.g., "pay for overtime" → overtime premium)

Auto decision signals: presence of keyword matches, strength of vector similarity, and top matches from both searches. If paraphrase generation is unavailable (Demo model / no key set), hard automatically degrades to "expanded result count." The raw search modes (keyword/vector/hybrid) remain available to engineers in /search.html and the CLI.

  • Models: Claude Opus 5 (default) / Sonnet 5 / Haiku 4.5 / Demo (offline)

  • Opus 5 enables server-side refusal fallback (when the model declines to answer for safety, it automatically falls back to an alternative model within the same request)

  • The generation API uses the official Anthropic SDK. The key is ANTHROPIC_API_KEY in .env

Integration with Claude Code (MCP / Agentic RAG)

.mcp.json (in the target repository or home directory):

{
  "mcpServers": {
    "docsearch": {
      "command": "/ABSOLUTE/PATH/doc-search/.venv/bin/python",
      "args": ["-m", "docsearch.mcp_server"],
      "env": { "DOCSEARCH_INDEX": "/ABSOLUTE/PATH/doc-search/index" }
    }
  }
}

Tools: search_docs(query, mode, k) / docs_repo_info(). Since Claude Code itself handles query planning → re-search → file reading → cited answers, Agentic RAG works inside the editor, separate from the chat UI.

Swapping in real data (on a machine with access permissions)

This repository only contains placeholders (sample_docs). The link to real data and the internal repository is swapped on the machine that has access, without any code changes. Priority order:

  1. Environment variables (Docker uses these): in .env, set DOCSEARCH_DOCS_HOST=/path/to/real-docs (the mount source into the container) and DOCSEARCH_GITHUB_BASE=https://github.example.com/org/repo/blob/main

  2. Config file (local execution): cp datasource.example.json datasource.json then edit docs_dir / github_base / embedder → docsearch index (no arguments). datasource.json is gitignored, so pointers to the internal repository are never pushed

  3. Placeholder: if nothing is configured, sample_docs/ is indexed

The resolution logic is consolidated into a single function, get_datasource(), in docsearch/datasource.py.

Search results, citation chips, and [path:line] in answers become deep links to the corresponding lines on GitHub in the document repository (in the blob/<SHA-at-index-time>/path#L<line> format, so line anchors don't break even as the repository advances).

  • Auto-detected at index time from the docs repository's git remote (GHE also works)

  • If auto-detection fails (e.g., when docs are mounted in Docker), set DOCSEARCH_GITHUB_BASE=https://github.com/o/r/blob/main/docs in .env (or --github-base in the CLI)

Search engine design points

  • Japanese keyword search: CJK strings are expanded into bigrams and indexed in SQLite FTS5. On the query side, bigram phrase search handles adjacent matching (works without a morphological analyzer)

  • RRF fusion: BM25 scores and cosine similarity are scale-incompatible, so they are fused by rank

  • Breadcrumbs on chunks: The heading hierarchy is prepended to each chunk (in the glossary, headings = terms)

Interesting queries to try

Query

Expected result

消費税区分

Keyword search hits the glossary directly

解約率

Vector search discovers "churn rate" (paraphrase)

仕訳の二重登録を防ぐ仕組みは? (chat)

Answers citing idempotency / Idempotency-Key

テナント 漏えい

Tenant isolation from security review criteria

Deployment

Local resident (macOS / LaunchAgent)

bash deploy/install-launchd.sh    # ログイン時自動起動・クラッシュ時自動再起動
  • Logs: logs/docsearch.log / logs/docsearch.err.log

  • Stop and remove: launchctl bootout gui/$(id -u)/com.sodashikenn.docsearch && rm ~/Library/LaunchAgents/com.sodashikenn.docsearch.plist

  • macOS TCC note: If the repository is under a protected folder such as ~/Desktop, the python launched by launchd may be denied file access and enter a startup loop. In that case, grant python access permissions in "System Settings > Privacy & Security," or move the repository outside the protected area (e.g., to ~/dev/)

Docker (fastest way to share with another machine)

git clone https://github.com/SodaShikenn/doc-search.git && cd doc-search
cp .env.example .env               # ANTHROPIC_API_KEY を記入
docker compose up --build -d       # → http://127.0.0.1:8765
  • For a Mac without Docker (when not using Docker Desktop):

    brew install colima docker docker-compose && colima start
    mkdir -p ~/.docker/cli-plugins && ln -sfn $(brew --prefix)/opt/docker-compose/bin/docker-compose ~/.docker/cli-plugins/docker-compose
  • For fully local vector search (recommended on M-series Macs with spare memory): write WITH_LOCAL_ML=1 and DOCSEARCH_EMBEDDER=e5 in .env, then run docker compose up --build -d (image ~2-3GB, first run includes model download. Embedding model changes are detected at startup and automatically re-indexed)

  • Default slim image (~300MB): vector search uses the cloud if VOYAGE_API_KEY is set, otherwise degrades to hash (keyword search always works fully)

  • For real documents, replace ./sample_docs:/docs:ro in docker-compose.yml; to re-index after content updates, run DOCSEARCH_REINDEX=1 docker compose up -d

  • Keys are injected from the host's .env (not baked into the image)

  • There is no authentication. For public exposure, keep the local bind and use a reverse proxy (with auth) or VPN

Third-party

webui/vendor/ contains self-hosted third-party libraries, each under its own license: marked v13.0.2 (MIT) and DOMPurify 3.1.6 (Apache-2.0 OR MPL-2.0). Everything else is MIT (see LICENSE).

Limitations and future work

  • Indexing is full rebuild only (incremental updates not implemented)

  • Conversation history is stored in browser localStorage (no server-side persistence)

  • Evaluation: it would be good to compare hybrid vs. individual modes and embedding models using recall@k on question → correct-file pairs

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP server for semantic search across llms.txt documentation sources, with hybrid two-stage retrieval and automatic background refresh.
    5
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for documentation search that automatically indexes web documentation sites and provides semantic, full-text, or hybrid search capabilities.
    4 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for local RAG over personal notes, PDFs, and documents, enabling plain-English querying and hybrid search with multi-hop context expansion.
    MIT