ragChatbot
Chatbot plus a retrieval stage. You supply the Retriever — the recipe is storage-agnostic on purpose. Per turn, top-K chunks are spliced into the prompt as numbered context before delegating to simpleChatbot.
ragChatbot is simpleChatbot with a retrieval preamble. It
takes a consumer-supplied retriever, calls it once per ask(),
prepends the returned chunks to the user message as
Context:\n[1] ...\n[2] ..., then delegates to the underlying
simpleChatbot. When the retriever returns no chunks or throws,
the message passes through unmodified — the bot stays useful if
retrieval is down.
Best fit: a knowledge-base assistant or docs assistant. Reach for this when answers must be grounded in your documents and you already have a vector store or full-text index.
Quickstart
import { ragChatbot } from "@pleach/recipes/rag";
const bot = ragChatbot({
retriever: async (query, { topK = 4 } = {}) => {
return await myVectorDb.search(query, { topK });
},
});
console.log(await bot.ask("what's our refund policy?"));The recipe does not bind a vector store. Bring your own —
pgvector, Pinecone, Weaviate, a Postgres FTS index, an
S3-backed JSON snapshot, anything that satisfies the
Retriever signature.
What it does
On each ask(message):
- Invoke
retriever(message, { topK })once. - If the result is a non-empty array, build a context
preamble of the form
Context:\n[1] (id) content\n[2] (id) content\n...and prepend it to the user message. - Delegate to the underlying
simpleChatbot.ask()with the augmented message.
If the retriever throws or returns [], step 2 is skipped and
the raw user message is forwarded. The retriever runs exactly
once per turn — no automatic re-query, no streaming retrieval.
Config reference
interface RetrievedChunk {
/** Stable identifier for citation rendering. */
id: string;
/** Raw text content surfaced to the LLM. */
content: string;
/** Optional similarity score (recipe is score-agnostic). */
score?: number;
/** Free-form metadata — source URL, page number, section. */
metadata?: Record<string, unknown>;
}
type Retriever = (
query: string,
opts?: { topK?: number },
) => Promise<RetrievedChunk[]>;
interface RagChatbotConfig extends SimpleChatbotConfig {
retriever: Retriever;
/** Default topK passed to the retriever. Defaults to 4. */
topK?: number;
}RagChatbotConfig extends SimpleChatbotConfig, so
systemPrompt, orchestratorConfig, and all
CreatePleachRuntimeConfig fields work identically.
Common gotchas
- The retriever is consumer-owned. The recipe does not rate-limit, cache, deduplicate, or retry. If your vector store has its own back-pressure or budget rules, enforce them inside the retriever.
scoreis not used for ordering. Chunks are inserted in the order the retriever returns them. If you want a different ranking, sort inside the retriever before returning.- No automatic citation rendering. The numbered
[N]preamble lets the LLM cite chunks by index in its reply, but the recipe does not parse[N]references out of the assistant text. Build that surface in your UI layer. - Retriever errors are swallowed. A thrown error falls through to the plain-delegation path. If you need observability on retrieval failures, log inside the retriever or wrap it.
See also
@pleach/recipesoverview — every recipe in one page.simpleChatbot— the base this recipe extends.Internal knowledge agent— full-system pattern for RAG with audit and lineage.
simpleChatbot
One-line factory over @pleach/core for the minimal conversational agent. No sibling peers — supply your own provider adapter and the recipe handles session lifecycle, conversational state, and stream collection.
observableChatbot
Chatbot whose ask() calls inherit a @pleach/observe sub-agent attribution scope. Every recordCall(...) downstream of the runtime turn carries [serviceName, ...] without threading a recorder argument through call sites.