pleach
Cookbook

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):

  1. Invoke retriever(message, { topK }) once.
  2. 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.
  3. 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.
  • score is 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

On this page