Inspiration🟦 Inspiration
Every buildathon drowns in the same repeated questions — "When's the deadline?", "Which tracks are still open?", "How do I submit?" — and the single most dangerous failure for an event support bot is a confident WRONG answer. A hallucinated deadline can make someone miss their submission entirely.
So I deliberately built the opposite of a chatty bot. Instead of "an AI that always answers," I built an assistant that is grounded to the official AABW knowledge base and will say "I don't have that information" rather than guess. It miniaturizes two principles from my main platform (Kaori): answer only from trusted, citable sources, and always disclose when an answer is AI-generated. The goal was trust, not chattiness — a support tool a builder can actually rely on at 2 AM the night before a deadline.
🟩 What it does
Trợ lý AABW is a grounded, bilingual (Vietnamese / English) support assistant for Agentic AI Build Week — built for the thousands of builders (and the organizing team) who need fast, correct answers about tracks, deadlines, workshops, and how to submit.
What makes it more than a chat box: • Grounding gate — every answer is validated against the official knowledge base through a structured-output contract {covered, citations, answer}. If a question isn't covered by the data, the bot DECLINES and redirects the user to the right channel instead of fabricating an answer. • Citations on every reply — it surfaces the exact facts it used, so answers are auditable, not opaque. • Machine-readable AI disclosure — every response carries a "Generated by AI" disclosure object + a visible badge, so users always know they're talking to AI. • Spam & abuse guards — it detects repeated/near-duplicate questions and off-topic floods and short-circuits them before spending model tokens, plus per-IP rate limiting — so it stays cheap and on-topic at event scale. • Bilingual by design — answers in VN or EN based on the user's question.
In short: a support bot that is trustworthy because it knows the limits of what it knows.
🟨 How I built it
Stack: Next.js 16 (App Router) + TypeScript + Tailwind for a single bilingual chat UI (with citations and a disclosure badge); Claude (claude-haiku-4-5) via the official @anthropic-ai/sdk; Vitest for tests; Docker for deployment.
The core idea is enforced grounding, not prompted grounding: • Knowledge base (lib/knowledge.ts) — one curated, self-contained markdown file of official AABW facts, each tagged with an Fxx ID so answers can cite them. Loaded once and cached in-process. • Grounding gate (lib/grounding.ts + lib/gate.ts) — I call Claude with STRUCTURED OUTPUTS, forcing the model to return a typed JSON object {covered, citations, answer} that conforms to a JSON schema. The "covered" flag is what decides whether the bot answers or declines — so grounding is a hard contract validated at the output layer, not a polite request inside a prompt. • Request pipeline (app/api/chat/route.ts) — each request flows rate-limit → spam guard → grounding gate → JSON response. Cheap guards run BEFORE any model call. • Safety layers — per-IP rate limiter (lib/ratelimit.ts) and a Jaccard-similarity spam/repeat guard (lib/spam.ts) that blocks duplicate/off-topic floods. • Tests — Vitest unit tests for the gate, spam guard, rate limiter, and KB loader (mock-based, no API key needed). • Deployment — a multi-stage Docker build using Next.js standalone output, deployed on a VPS and running side-by-side with an existing production app, published on its own port without disrupting it.
Self-contained data only: the knowledge base is my own curated content, satisfying the track's data rule.
🟧 Challenges I ran into
• Grounding calibration — my first version was TOO cautious and declined valid, in-scope questions. I had to tune the system prompt and the coverage gate so it confidently answers what it genuinely knows, and only declines beyond the knowledge base. Getting that boundary right was the hardest part. • Runtime file in a standalone build — the knowledge base is read from the working directory at runtime, which Next.js standalone output does NOT bundle automatically. The deployed container couldn't find it until I explicitly copied the content into the image. • Deploy auth bug — a 401 "invalid x-api-key" in production turned out to be the container caching a stale environment; I learned that env_file changes require a forced container recreate, not just a restart or a browser refresh. • Staying cheap at scale — for thousands of builders, I couldn't afford a model call per spammy message, so the spam/repeat guard and rate limiter had to run before spending any tokens.
🟪 Accomplishments that I'm proud of
• A verifiable "decline instead of guess" behavior — the bot demonstrably refuses to hallucinate, which is exactly what a support bot handling real deadlines needs. • Transparency by default — every answer ships with citations and a machine-readable AI disclosure, with no extra user action. • Not a wrapper — grounding is enforced through structured outputs and a coverage gate, with real spam/abuse defenses, not a prompt glued onto a chat UI. • Actually shipped — fully tested and deployed live on a VPS, running alongside another production app without disrupting it.
🟫 What I learned
• Structured outputs turn "please stay grounded" from a hope into an enforced contract — the model must return a schema-valid object, and the app decides answer-vs-decline from it. • Honest declining is a feature, not a weakness. Users trust a bot more when it admits the limits of what it knows than when it bluffs. • Cheap-by-design beats optimize-later: putting the cheapest guards (rate limit, spam detection) before the expensive model call makes the whole thing affordable at event scale.
🟦 What's next for Trợ lý AABW
• Auto-sync the knowledge base to live AABW announcements so it stays current without manual edits. • A Discord integration (alongside the community's "Clawbie" bot) so builders can ask it directly in-channel. • Log declined/uncovered questions to reveal real knowledge gaps and grow the KB where builders actually need it. • HTTPS + a custom domain via the existing reverse proxy for a polished public URL.
Built With
- anthropic-claude
- anthropic-sdk
- claude-haiku
- docker
- json-schema
- next.js
- node.js
- react
- structured-outputs
- tailwind-css
- typescript
- vitest
Log in or sign up for Devpost to join the conversation.