RAG with citations
Retrieve top-k chunks with scores and inline cite refs.
'use client'import { useMemo, useState } from 'react'import { motion, AnimatePresence } from 'motion/react'import { chunkText } from '@agentskit/rag/chunker'const SEED_DOC = `## Q3 strategyGTM expansion: target enterprise verticals in North America and Europe. Land-and-expand motion focused on dev tools, fintech, and healthcare. Hire two AEs per region by end of quarter.Pricing rework: introduce tiered model with usage-based add-ons. Base tier $99/mo, pro tier $499/mo, enterprise quoted. Migration plan for legacy customers grandfathered for 12 months.Onboarding cuts: reduce time-to-first-value to under 5 minutes. New welcome flow, in-product tooltips, and seeded sample data. Expect 40% lift in W1 activation.Reliability: target 99.95% uptime. Add multi-region failover, expand on-call rotation to 6 engineers, publish public status page.Hiring: close two senior engineering roles, one DPE, one design lead. Compensation benchmark refreshed against current market.`function tokenize(s: string): Set<string> { return new Set(s.toLowerCase().match(/[a-z0-9]+/g) ?? [])}function jaccard(a: Set<string>, b: Set<string>): number { if (a.size === 0 || b.size === 0) return 0 let inter = 0 for (const t of a) if (b.has(t)) inter++ const union = a.size + b.size - inter return inter / union}export function RAGCiteExample() { const [doc, setDoc] = useState(SEED_DOC) const [chunkSize, setChunkSize] = useState(220) const [overlap, setOverlap] = useState(40) const [query, setQuery] = useState('how should we change pricing?') const [k, setK] = useState(3) const chunks = useMemo( () => chunkText(doc, { chunkSize, chunkOverlap: overlap, split: (text: string) => { const sentences = text .split(/(?<=[.!?\n])\s+/) .map(s => s.trim()) .filter(Boolean) const out: string[] = [] let buf = '' for (const s of sentences) { if (!buf) { buf = s } else if (buf.length + 1 + s.length <= chunkSize) { buf += ' ' + s } else { out.push(buf) buf = s } } if (buf) out.push(buf) return out }, }), [doc, chunkSize, overlap], ) const ranked = useMemo(() => { const q = tokenize(query) return chunks .map((c, i) => ({ id: i + 1, text: c, score: jaccard(q, tokenize(c)) })) .sort((a, b) => b.score - a.score) .slice(0, k) }, [chunks, query, k]) const answer = ranked.length === 0 || ranked[0].score === 0 ? 'No matching context found in the corpus.' : `Top match suggests: ${ranked[0].text.slice(0, 140).trim()}… ${ranked.map(r => `[§${r.id}]`).join(' ')}` return ( <div data-ak-example className="flex flex-col gap-3 rounded-lg border border-ak-border bg-ak-surface p-4"> <div className="flex items-center justify-between font-mono text-xs"> <span className="text-ak-graphite">@agentskit/rag · chunkText · in-memory retrieval</span> <span className="text-ak-graphite">{chunks.length} chunks · top-{k}</span> </div> <div className="grid grid-cols-1 gap-3 lg:grid-cols-2"> <div className="flex flex-col gap-2"> <div className="font-mono text-[10px] text-ak-graphite">document</div> <textarea value={doc} onChange={e => setDoc(e.target.value)} className="min-h-[180px] resize-none rounded-md border border-ak-border bg-ak-midnight p-2 font-mono text-[11px] text-ak-foam outline-none focus:border-ak-blue" /> <div className="grid grid-cols-2 gap-2"> <label className="font-mono text-[10px] text-ak-graphite"> chunkSize: {chunkSize} <input type="range" min={80} max={500} value={chunkSize} onChange={e => setChunkSize(Number(e.target.value))} className="w-full" /> </label> <label className="font-mono text-[10px] text-ak-graphite"> chunkOverlap: {overlap} <input type="range" min={0} max={120} value={overlap} onChange={e => setOverlap(Number(e.target.value))} className="w-full" /> </label> </div> </div> <div className="flex flex-col gap-2"> <div className="font-mono text-[10px] text-ak-graphite">query</div> <div className="flex gap-2"> <input value={query} onChange={e => setQuery(e.target.value)} className="flex-1 rounded-md border border-ak-border bg-ak-midnight px-2 py-1.5 font-mono text-xs text-ak-foam outline-none focus:border-ak-blue" /> <select value={k} onChange={e => setK(Number(e.target.value))} className="rounded-md border border-ak-border bg-ak-midnight px-2 py-1.5 font-mono text-xs text-ak-foam outline-none" > <option value={1}>k=1</option> <option value={2}>k=2</option> <option value={3}>k=3</option> <option value={5}>k=5</option> </select> </div> <div className="font-mono text-[10px] text-ak-graphite">retrieved chunks</div> <div className="flex flex-col gap-1.5"> <AnimatePresence initial={false}> {ranked.map(r => ( <motion.div key={`${r.id}-${query}`} initial={{ opacity: 0, x: 8 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0 }} transition={{ duration: 0.25 }} className="rounded-md border border-ak-border bg-ak-midnight p-2" > <div className="mb-1 flex items-center justify-between"> <span className="rounded bg-ak-blue/15 px-1.5 py-0.5 font-mono text-[10px] text-ak-blue"> §{r.id} </span> <span className="font-mono text-[10px] text-ak-green"> {r.score.toFixed(3)} </span> </div> <div className="font-mono text-[11px] text-ak-foam">{r.text}</div> </motion.div> ))} </AnimatePresence> </div> <div className="rounded-md border border-ak-green/30 bg-ak-green/5 p-2"> <div className="mb-1 font-mono text-[10px] text-ak-graphite">answer (with cites)</div> <div className="font-mono text-[11px] text-ak-foam">{answer}</div> </div> </div> </div> <div className="font-mono text-[10px] text-ak-graphite"> chunkText is real · embedder + vector store stubbed (jaccard) · swap with createRAG for production </div> </div> )}Basic chat
Streaming chat with a mock adapter. Zero config, runs in-browser.
streamingchatTool use
Tool-calling agent that browses a mocked product catalog.
toolschatRAG
Retrieval-augmented chat with inline source citations.
ragchatCode assistant
Code-aware chat with syntax-highlighted output.
codechatMarkdown chat
Rich Markdown rendering in assistant responses.
markdownchatSupport bot
Chat with escalation, memory, and confirmation gates.
supportmemorytoolsMulti-agent
Planner + worker + reviewer topology.
multi-agentMulti-model
Switch providers on the fly in a single conversation.
multi-modelchatAgent actions
Streaming UI with live tool-call visualization.
toolsstreamingshadcn/ui chat
AgentsKit styled with shadcn/ui tokens.
chatdesign-systemMaterial UI chat
AgentsKit styled with MUI components.
chatdesign-systemSandbox runner
Agent-emitted code runs isolated in E2B / WebContainer.
sandboxtoolscodeObservability trace
Live span tree with tokens, latency, cost. Exports to LangSmith / OTEL.
observabilityproductionPersistent memory
Cross-session recall with sqlite, redis, or lancedb backends.
memorypersistenceEval suite
Run regression tests, track accuracy / latency / cost in CI.
evalproductionciSlack integration
Agent posts to Slack via @agentskit/tools.
integrationstoolsInk terminal
Same controller, rendered in your terminal via Ink.
inkcliterminalSkill swap
Hot-swap personas mid-conversation: researcher, critic, planner.
skillspromptsRuntime ReAct
Standalone agent runtime — no UI required. ReAct loop with tools + memory.
runtimemulti-agentProvider fanout
Same prompt across openai, anthropic, gemini, ollama. Compare quality + cost.
adaptersmulti-model