Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Operational LLMWiki

Turn evolving manuals, SOPs, and operational documents into persistent wiki pages with citations and revision history. New documents can extend an existing page; every addition keeps its source evidence, and every changed page gets a reviewable revision.

Documents → deduplicate → chunk → extract claims → match pages → append → diff.

Runs locally with FastAPI + SQLite, no API key. The default demo extractor copies sentences; a conservative local matcher chooses a page, creates one when no topic terms overlap, or asks for a choice when the result is unclear. An optional Anthropic adapter requests atomic claims with verified chunk quotes. All pages remain drafts. Canonical pages are the goal; lexical matching does not guarantee one page per real-world subject.

Why not just RAG?

Query-time RAG retrieves context and synthesizes an answer when someone asks a question. LLMWiki maintains a page that can be inspected, incrementally edited, and compared across revisions before a question is asked.

RAG and GraphRAG can also persist indexes, update their corpora, and provide citations. They can complement this project: retrieval could use the maintained pages as context. The distinction is the durable page and its update history, not a claim that one architecture is universally better.

Try the complete workflow

Use Python 3.11 or newer (the commands below use 3.12):

python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
python scripts/demo.py

The demo uses the real API routes through FastAPI's in-process client. No running server, API key, or external service is needed. It explicitly selects demo mode and uses a fresh temporary database, so every run starts at revision 1 and leaves your existing database untouched.

  1. Import the maintenance manual, chunk it, extract three demo claims, and create revision 1.
  2. Create separate emergency-exit and tool-storage pages.
  3. Import the supplement; automatically match the existing maintenance page without a target page ID.
  4. Append two claims to that same page, producing revision 2; display the stored diff.
  5. Trace a new claim through its chunk to the source and verbatim evidence.
  6. Re-upload the supplement: reuse source/chunk/claim IDs and keep revision 2.
  7. Verify both unrelated pages and their histories are unchanged; show the saved decisions.

See the example corpus, actual demo transcript, and

To keep your own wiki and explore the API:

python -m uvicorn app.main:app --reload

Open interactive API docs. The server stores data in data/wiki.db. Set LLMWIKI_DB_PATH before starting the process for another SQLite file. The demo intentionally ignores that setting and uses its own temporary file.

Architecture

flowchart TD
    D[Operational documents] --> S[Exact-content source deduplication]
    S --> C[Chunks]
    C --> E[ClaimExtractor port]
    E --> DEMO[Default local demo adapter]
    E --> LLM[Optional Anthropic adapter]
    DEMO --> V[Claims with chunk ID and evidence quote]
    LLM --> V
    V --> M[Local page matcher]
    M --> DEC{Matching decision}
    DEC -->|Clear match or new topic| P[Create or append to wiki draft]
    DEC -->|Ambiguous| R[Return candidates for explicit choice]
    R --> P
    P --> H[Revision history and version diffs]
    H --> TRACE[Page to claim to chunk to source]
    DB[(SQLite)] --- S
    DB --- V
    DB --- P
    DB --- H
    M --> AUDIT[Saved candidates, scores, reasons]
    AUDIT --> DB
Loading

extractors.py defines the extraction port; llm.py contains Anthropic HTTP code. services.py coordinates extraction; matching.py is a pure local matcher; storage.py owns SQLite transactions. Routes call these application interfaces without selecting or constructing an Anthropic client.

Extraction finishes before page matching starts. Matching, page changes, revision snapshots, and the decision record share one SQLite write transaction. An error while recording the decision rolls back the page change. This small application uses no worker queue, vector database, or agent framework.

Automatic matching and incremental updates

  1. POST /sources with title and content registers a source and its chunks.
  2. POST /sources/{id}/process with no body extracts claims if needed, then decides whether to create a page, append to one page, return noop, or request review.
  3. GET /sources/{id}/decisions retrieves saved decisions, oldest first.

The processing response has decision and page fields. A decision records its method (local-v1 or manual), action, reason, candidate page IDs/titles/scores, claim coverage, shared terms, selected page, input/added claim IDs, before/after revision numbers, and timestamp. These are stored snapshots, so later page edits do not rewrite the audit trail. Each processing attempt records a decision; a noop records no new page revision.

The local matcher compares English topic words in claims and existing page titles and claims. It removes common instruction words and applies simple plural normalization. Automatic matching requires every incoming claim to have at least two shared topic terms and a score of at least 0.5, with a 0.15 lead over the runner-up. An existing claim ID also counts as a direct match. If all incoming IDs already belong to exactly one page, that page is reused without a revision. Scores are heuristics, not confidence probabilities.

No shared topic terms means a new draft, titled from the source. Weak, competing, mixed-topic, or unsupported-language matches return needs_review with no page change. A title collision also requires review. These rules are tested on small English examples; paraphrases and synonyms can still produce separate pages.

To resolve an ambiguous decision, repeat processing with one explicit choice:

{"page_id": "existing-page-id"}

or:

{"new_page_title": "A distinct topic title"}

A conflicting existing title returns 409; choose its ID explicitly. A batch is routed to at most one page. Mixed-topic sources need review and can use the manual claim-selection endpoints below. This is append-only updating: old claims and revisions are preserved, and unrelated pages are not rewritten. Automatic fact reconciliation and contradiction detection are not implemented.

Extraction modes

Default: LLMWIKI_EXTRACTOR=demo. DemoClaimExtractor calls extract_demo_claims(), which copies sentences into candidate claims. These are not semantically verified atomic facts. A key in the environment alone never activates a model call. Both the demo and acceptance workflows stay in demo mode.

Optional: LLMWIKI_EXTRACTOR=anthropic. For a server that uses real extraction:

export LLMWIKI_EXTRACTOR=anthropic
export ANTHROPIC_API_KEY="your-api-key"
# Optional model override; otherwise claude-haiku-4-5-20251001 is used.
export ANTHROPIC_MODEL="claude-haiku-4-5-20251001"
python -m uvicorn app.main:app --reload

AnthropicClaimExtractor uses the Messages API with structured JSON output. The prompt requests one supported assertion per claim, preserving conditions and negations. The adapter validates the schema, rejects blank text and extra fields, and requires every evidence quote to occur literally in its originating chunk. All chunks must validate before any claims are saved. A malformed or incomplete response rejects that extraction; it does not silently substitute demo output. Quote presence does not prove semantic faithfulness or atomicity. Live-model quality and costs have not been evaluated; tests use mocked responses only.

Configuration is read when fresh extraction is requested. Missing/invalid configuration or denied credentials return 503; provider/output failures return 502; timeouts return 504. The server can still start and serve stored data without a key. Successfully saved extraction is reused, including an empty list, even if the configured mode changes later. Re-extraction with another model is not currently an API operation. Simultaneous first requests can perform redundant model calls, although only one saved claim set wins. Real calls send chunk text to Anthropic and may incur charges. Matching stays local in both modes.

Manual controls and inspection

The existing manual routes remain available:

  • POST /sources/{id}/claims: run the configured extractor or return cached claims.
  • GET /sources/{id} and /sources/{id}/claims: retrieve saved input and claims.
  • GET /claims/{id} and /chunks/{id}: follow provenance to the source.
  • POST /pages: create a draft from title and a non-empty claim_ids list.
  • POST /pages/{id}/claims: append selected IDs, keeping existing claims and order.
  • PATCH /pages/{id}: change title or supply the complete ordered claim_ids selection. Omitted fields stay unchanged; omitting an old ID from the supplied list removes it from the current page, preserving its claim and past snapshots.
  • GET /pages and /pages/{id}: list drafts or read a full page with claim evidence.
  • GET /pages/{id}/revisions and /pages/{id}/revisions/{number}: inspect history.
  • GET /pages/{id}/diff?from_revision=1&to_revision=2: compare saved snapshots.
  • GET /pages/search?q=maintenance: search current titles and Markdown bodies.
  • GET /sources/{id}/pages and /claims/{id}/pages: inspect current reverse references.

Search returns IDs, titles, revision numbers, and a Markdown preview of at most 200 characters. It uses literal substrings (including % and _), handles Chinese text, and ignores ASCII case. Title matches rank first. Search, reverse references, and decision history accept limit (1–100, default 20) and offset (default 0). Reverse references group all matching claim IDs per page; history-only citations are excluded. Missing IDs return 404; invalid inputs return 422. Reading unextracted claims returns 409; reading a source's references before extraction returns an empty list without triggering extraction.

Diffs include added/removed/modified claims, title changes, relative claim order, and a unified Markdown diff. Identity is based on claim IDs. Same-version and reverse comparisons are supported. Manual page creation always creates a draft; use processing or an update route to reuse a page.

Persistence and invariants

  • Sources deduplicate by SHA-256 of exact UTF-8 content. Titles do not affect the hash; whitespace, case, and line-ending changes are different content.
  • SQLite stores sources, chunks, claims, ordered page associations, full revision snapshots, and matching decisions. IDs and evidence survive process restarts.
  • Repeating saved extraction or adding existing claim IDs reuses them. Equal wording from a different source keeps separate claim IDs and evidence; semantic deduplication and multi-source citations on one canonical claim are not implemented.
  • A real page change writes one revision. No-op updates write none. Concurrent additions serialize, preserving both updates; concurrent first processing checks the current page set inside the same write transaction before creating a page.
  • Startup creates missing tables without deleting existing data. Legacy pages get one baseline revision whose timestamp is the snapshot time, not an invented original creation time. Historical duplicate sources and their original citations remain readable; re-uploads reuse the earliest content owner.
  • The source of truth is your SQLite file. data/, environment files, and caches are ignored by Git; there is no hosted backup service.

Tests and acceptance

PYTHONPATH=src python -m pytest -q
python scripts/acceptance.py

The suite covers persistence across processes, transaction rollback, concurrency, source deduplication, revisions/diffs, search, reverse references, extractor validation and failures, automatic matching, ambiguous decisions, manual resolution, and the operational example corpus. Test defaults force demo mode and block real HTTP transports. Anthropic tests use fake responses or an in-memory mock transport; no test is intended to make paid API calls.

Acceptance runs both the original manual v0.1 scenario and the new automatic corpus workflow in temporary databases. It returns a nonzero exit code on failure. The original manual API behavior remains covered.

Scope and next work

This is a local portfolio backend, not an operational authority. Known limits:

  • Fixed 120-word chunks can cut sentences and normalize whitespace. Evidence is checked against the stored chunk; raw-source character offsets are not tracked.
  • The matcher is English lexical matching. It is not an embedding/semantic model, and its thresholds have not been evaluated on a representative real corpus.
  • One processing request handles one source as one batch. Mixed-topic segmentation, conflict detection, supersession, and automatic fact reconciliation remain open.
  • Pages are composed claim bullets, not LLM-written summaries. No production review workflow, authentication, PDF parser, background queue, or chat UI is included.

The design documents in docs/0006 describe the broader research direction, including components not implemented here. The current matcher and transaction contract are documented in automatic processing. The project was inspired by prior operational-document GraphRAG work; no proprietary source documents are included.

License

MIT — see LICENSE.

About

A persistent LLM-maintained knowledge system for technical manuals, SOPs, and operational documentation.

Resources

Stars

153 stars

Watchers

18 watching

Forks

Releases

Packages

Contributors

Languages