--- title: 'A retrieval-backed support agent' description: 'A support agent with two tools: one searches a knowledge base with RAG, the other looks up an order in the agent database.' position: 3 --- # A retrieval-backed support agent > This support agent answers from two places: a knowledge base it searches with > [RAG](/docs/sdk/data/rag), and its own [database](/docs/sdk/data/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](https://orm.drizzle.team/) and throws. ```ts // 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`: ```text 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](/docs/sdk/data/rag#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 - **[A plain greeter](/docs/examples/agents/plain-greeter)**: the same file shape with no tools at all. - **[A config-driven agent](/docs/examples/agents/config-driven)**: let an operator change the agent's tone without a redeploy. - **[What is an agent](/docs/sdk/agents/overview)**: the model behind this example. - **[Workflow + Agents examples](/docs/examples/workflow-agents/delegate-to-agent)**: drive an agent like this one from a durable workflow.