{"slug": "rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back", "title": "Rebuilding the Cerebras Knowledge Base: the web UI and a look back", "summary": "The Cerebras knowledge base project concluded with a lightweight web UI built on FastAPI and vanilla JavaScript, serving two endpoints for LLM-free search and full retrieval-augmented generation. The final post in the series also reported evaluation results showing that adding a reranker improved MRR from 0.57 to 0.90 on a fixed 31-question benchmark.", "body_md": "[Post 6](//post-6-mcp.md) handed the retrieval tools to agents over MCP. This last post builds the other front end (a plain web page) for the humans who don't have an MCP client sitting in their editor. And since it is the end of the series, it is also where I add up the scoreboard.\n\nThe UI is deliberately small: one FastAPI app, two JSON endpoints, and a single static HTML file with no build step. The same no-frameworks rule that governed retrieval (no LangChain, no vector-store SDK) governs the front end too (no React, no bundler, no npm).\n\nThe whole back end is a thin wrapper over the pipeline built in posts 1–5:\n\n``` php\ndef create_app(search_fn, ask_fn) -> FastAPI:\n    @app.post(\"/api/search\")   # LLM-free retrieval  (posts 1–4)\n    @app.post(\"/api/ask\")      # full pipeline       (post 5)\n```\n\n`create_app`\n\ntakes the two functions as arguments rather than building them: the same seam the tests use to inject fakes, and the same seam `main()`\n\nuses to wire in the real `hybrid_search`\n\nand `run_ask`\n\n. The endpoints mirror the two things the series built:\n\n`/api/search`\n\n`/api/ask`\n\nThe front-end toggle makes that split literal:\n\n```\n<label><input type=\"radio\" name=\"mode\" value=\"search\" checked> search (LLM-free)</label>\n<label><input type=\"radio\" name=\"mode\" value=\"ask\"> ask (full pipeline)</label>\n```\n\nYou can see the architecture from the home page: one radio button for retrieval, one for retrieval plus reasoning.\n\nReal responses from the running app against the live corpus. `search`\n\nmode is instant and free:\n\n```\n// POST /api/search {\"query\": \"how do I add middleware\", \"limit\": 3}\n{\"results\": [\n  {\"source_id\": \"issue_3027\", \"score\": 0.0164,\n   \"snippet\": \"# How to add a header field to the request in a middleware?\",\n   \"url\": \"https://github.com/fastapi/fastapi/issues/3027\"},\n  {\"source_id\": \"issue_10180\", \"score\": 0.0162,\n   \"snippet\": \"# Mounting sub-applications under `APIRouter`\", \"url\": \"…/10180\"}\n]}\n```\n\n`ask`\n\nmode spends the LLM calls and hands back a written, cited answer:\n\n```\n// POST /api/ask {\"question\": \"How do I add a custom middleware in FastAPI?\"}\n{\n  \"tools\": [\"search\"],\n  \"answer\": \"You can add custom middleware in FastAPI in several ways.\\n\\n### 1. HTTP\n             middleware with `@app.middleware(\\\"http\\\")` ... async def\n             add_process_time_header(request, call_next): ...\",\n  \"evidence\": [{\"n\": 1, \"source_id\": \"issue_5071\",\n                \"url\": \"https://github.com/fastapi/fastapi/issues/5071\",\n                \"snippet\": \"# Update Middleware Documentation ...\"}]\n}\n```\n\nThe page renders the answer in one block and the evidence as a citation list under it: the `[n]`\n\nmarkers in the prose line up with the numbered sources, so every claim is one click from the thread it came from.\n\n`index.html`\n\nis ~125 lines: inline CSS with a `prefers-color-scheme`\n\ndark mode, a form, three result containers, and ~50 lines of vanilla `fetch`\n\nthat POST to the two endpoints and render the JSON. No framework, no state library, no build. It is served as a string straight from the package:\n\n```\nindex_html = (resources.files(\"knowbase\") / \"static\" / \"index.html\").read_text()\n```\n\nThat is the entire deployment story: `kb-web`\n\nstarts uvicorn on `127.0.0.1:8000`\n\n, and the one file it needs travels inside the package. For a knowledge base that a handful of people query, a single static page is not a compromise; it is the right size.\n\nSeven posts, one rule: every post had to fix a failure the previous one demonstrated. Here is the whole arc on the fixed 31-question eval (MRR, the metric that tracked the story best):\n\n| Post | Change | MRR† | What it taught |\n|---|---|---|---|\n| 1 | naive vector | 0.77* | the baseline is stronger than you expect |\n| 2 | + hybrid (RRF) | 0.67 | fusion is confidence-blind; it regressed vs vector (0.77) |\n| 3 | + distill & burst | 0.57 | corpus rebuild hurt precision (vector fell to 0.63 too) but lifted recall@10 to 0.94 |\n| 4 | + rerank |\n0.90 |\nthe reranker converts recall into precision (the win) |\n| 5 | planner + synthesis | n/a | retrieval becomes cited answers; \"misses\" get answered |\n| 6 | MCP server | n/a | the tools become an agent's, not just ours |\n| 7 | web UI | n/a | …and a human's |\n\n†The MRR column traces the hybrid pipeline (posts 2–4); post 1 is its vector-only predecessor, the thing hybrid replaced. *Posts 1–2 measured on the ~3,700-doc pre-distillation corpus; 3–4 on the 16,315-doc distilled/burst corpus. Same 31 questions throughout.\n\nThe shape of that table is the honest lesson of the series. The naive baseline was good. The two changes that looked like obvious wins (hybrid search, LLM distillation) each lost in isolation, and stayed in only because they were scaffolding: hybrid supplied a recall pool and distillation/bursting raised its ceiling to 0.94, and then one LLM reranker turned that pool into MRR 0.90. If I had shipped hybrid on faith in post 2 and never measured, I would have quietly made the system worse and called it progress.\n\nThe series ends honest about its edges:\n\n`search_code`\n\nis only as good as the pattern it is given`def X`\n\nlands the definition. It should rank indexed code above docs.`jsonable_encoder`\n\nand background-tasks code-location lookups), even though `ask`\n\nanswers both from issue evidence. Closing that gap means better code retrieval, not a better ranker.The system that started as one embeddings table and a cosine query ended as a planner routing across three retrievers, an LLM reranker, grounded synthesis with citations, an MCP server, and a web page; and the single most valuable component was the one I almost didn't measure carefully enough to keep. Build naive first, make the eval honest before you make the system clever, and let every addition prove it earned its place. That is the whole method; the FastAPI knowledge base was just where I ran it.\n\n*Thanks for reading all seven. The retrieval numbers live in the Results Appendix; the web front end is knowbase.web plus one static index.html. *", "url": "https://wpnews.pro/news/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back", "canonical_source": "https://dev.to/faridgnank02/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back-4k2m", "published_at": "2026-08-16 12:20:45+00:00", "updated_at": "2026-08-16 12:42:28.537086+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "machine-learning", "natural-language-processing"], "entities": ["Cerebras", "FastAPI", "LangChain"], "alternates": {"html": "https://wpnews.pro/news/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back", "markdown": "https://wpnews.pro/news/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back.md", "text": "https://wpnews.pro/news/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back.txt", "jsonld": "https://wpnews.pro/news/rebuilding-the-cerebras-knowledge-base-the-web-ui-and-a-look-back.jsonld"}}