A retrieval-backed support agent

This support agent answers from two places: a knowledge base it searches with RAG, and its own database of orders. The code does not route between them. The model chooses from each tool's description, so write both descriptions as plain instructions.

The agent is a single deep-agents/support/index.ts file, sitting next to the schema.ts its tools import. Both tools read from the ambient stackbone client, and the two surfaces report failure differently. stackbone.rag returns a { data, error } envelope you check before touching .data, while stackbone.database is native Drizzle and throws.

deep-agents/support/index.ts
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { defineDeepAgent } from '@stackbone/sdk/deep';
import { stackbone } from '@stackbone/sdk';
import { eq } from '@stackbone/sdk/db';
import { orders } from './schema';

const searchKb = tool(
  async ({ query }: { query: string }) => {
    const result = await stackbone.rag.retrieve({
      text: query,
      model: 'openai/text-embedding-3-small',
      topK: 5,
      namespace: 'help-center',
    });
    if (result.error) throw new Error(result.error.code);
    return JSON.stringify(result.data.map((hit) => ({ score: hit.score, content: hit.content })));
  },
  {
    name: 'search_kb',
    description: 'Search the help-center knowledge base before answering.',
    schema: z.object({ query: z.string() }),
  },
);

const lookupOrder = tool(
  async ({ orderId }: { orderId: string }) => {
    const [order] = await stackbone.database
      .select()
      .from(orders)
      .where(eq(orders.id, orderId))
      .limit(1);
    return order ? JSON.stringify(order) : 'not_found';
  },
  {
    name: 'lookup_order',
    description: 'Look up an order by its id.',
    schema: z.object({ orderId: z.string() }),
  },
);

export default defineDeepAgent({
  name: 'support',
  model: 'openai/gpt-4o-mini',
  tools: [searchKb, lookupOrder],
});

Its prompt, keyed support:

You are Acme's support agent.
Search the knowledge base with search_kb before answering policy questions.
Use lookup_order when the user gives an order number.
Answer in two sentences or fewer, and say plainly when you don't know.

search_kb only finds what you already put in the help-center collection. Fill it from your own code with stackbone.rag.ingest, from the CLI, or from Studio. See Ingestion. Retrieve with the same model you embedded the chunks with, or nothing matches.

Send an x-stackbone-session header on every call to keep one running conversation per user. A follow-up question then lands in the same context without you re-sending the history.

What's next

BUILT WITH ❤️ FROM CANADA AND SPAIN