Build a Research Assistant
Learn how to build a deep-research assistant with subagents, planning, memory and a web UI.
This recipe starts from the research template. It picks up where Getting Started leaves off. If you haven't built the hello agent yet, start there.
What will we build?Copy link to section: What will we build?
We'll build a research assistant at /research. It plans sub-questions, sends a researcher subagent after each one, searches a local corpus and writes a cited report. A browser client, the B4.run Workbench, sits in front of it.
The tests and evals use recorded fixtures and run offline. Live runs need a real model and an OpenAI API key.
Create the appCopy link to section: Create the app
First, scaffold the app and install its dependencies:
npm create b4-app@latest my-research -- --template research
cd my-research
npm installThe --template research flag picks the research template instead of the default. You need Node.js 24 or later and npm 11.
Look at the agentsCopy link to section: Look at the agents
The app is an npm workspace with two packages. server/ is the B4.run app, and web/ is the Workbench. The root npm install installs both.
The research route lives in server/src/app/research/. Let's look at the coordinator, its subagent and the app config:
import { agent } from "@b4run/sdk"
export default agent({
model: "gpt-5-mini",
// Deep research fans out: plan → dispatch a researcher per sub-question → many
// corpus tool calls → synthesize. That legitimately exceeds LangGraph's default
// 25 super-steps, so raise the ceiling for this coordinator.
recursionLimit: 100,
description:
"A deep-research assistant: plans sub-questions, dispatches researchers, and writes a cited report.",
systemPrompt: `You are a deep-research coordinator. Given a question:
1. Start by checking durable context with \`recall({ query: "<the user's topic and preferences>" })\`.
2. Plan the sub-questions to investigate and record them in your todos.
3. For each sub-question, dispatch a specialist with \`task({ subagent: "researcher", input: "<sub-question>" })\`.
4. You may also \`searchCorpus({ query })\` and \`readDoc({ path })\` directly for quick lookups.
5. When the corpus lacks coverage, you may run \`runBash({ command: "node scripts/fetch-source.mjs <topic>" })\` — the human must approve it.
6. Synthesize the findings into a cited report and save it with \`writeFile({ path: "reports/<slug>.md", content: "<report>" })\`.
7. When the user gives a durable preference or you verify a reusable finding, call \`remember({ data, content })\` so it can be reviewed and recalled later.
Cite every claim with its source path in square brackets, e.g. [corpus/agent-architectures.md]. Keep the final answer concise.`,
})The coordinator plans the work and hands each sub-question to the researcher subagent. Both agents can call the shared tools in server/src/tools/. The config sets the shell permissions, spills large tool output to files and makes new memories wait for review. Threads persist to SQLite by default, so they survive a restart.
A few more files shape the agent:
server/src/app/research/
plan.md # seeds each thread's todo list
memory.md # route-specific prompt guidance
memory.ts # typed cross-session memory
subagents/researcher/ # the specialist subagent
skills/cite-sources/ # loaded on demand: citation rules
skills/synthesize-findings/ # loaded on demand: report structure
evals/research-quality.eval.ts
server/src/tools/
searchCorpus.ts # keyword search over the corpus
readDoc.ts # reads one document in full
server/workspace/
AGENTS.md # prompt guidance injected every turn
corpus/ # the documents the agent searchesplan.mdturns on planning. Its checklist seeds each thread's todos.workspace/AGENTS.mdand the route'smemory.mdadd persistent prompt guidance.memory.tsdefines typed memory records that the agent reads withrecalland writes withremember.- The agent loads skills under
skills/by name when it needs them.
Verify and testCopy link to section: Verify and test
Run these from the workspace root. Each script delegates to the package that owns it:
npm run typegen
npm run check
npm run typechecktypegen writes server/.b4/b4.generated.d.ts. check validates routes, tools and configuration without writing files:
B4.run app is valid: 2 routes discovered.
- /research (agent)
- /research/subagents/researcher (agent)Next, run both packages' tests:
npm testThe server tests replay recorded model responses. They cover corpus search and citations, memory recall and approval, subagent dispatch, tool-output offloading and the permission gate. The web tests cover the Workbench. Neither needs an API key.
Then run the quality eval:
npm run evalThe eval asks research questions against the corpus. Its scorers require a corpus search, source citations and a passing quality grade from a model judge. Recorded fixtures cover the agent and the judge, so the eval runs without an API key.
Run it liveCopy link to section: Run it live
These fixture-backed runs prove the app works offline. To answer a new question, we need a real model.
Copy the server's environment example, add your API key, run the preflight and start the dev server:
cp server/.env.example server/.env
# Add your OPENAI_API_KEY to server/.env
npm run verify
npm run dev:serververify checks the app, its types, its dependencies, Node and the provider environment. The dev server listens on http://127.0.0.1:3002 and serves Agent Protocol and AG-UI.
In a second terminal, send the agent a question:
cd server
echo '{"messages":[{"role":"user","content":"What are common agent architectures?"}]}' | npx b4 run /research --url http://127.0.0.1:3002The report lands in server/workspace/reports/. Thread state is saved to server/.b4/checkpoints.sqlite, so threads survive dev-server restarts.
See it in a UICopy link to section: See it in a UI
The Workbench is a chat UI over the same agent. Leave the server running and start it in another terminal:
npm run dev:webOpen http://localhost:3010 and ask a research question. You'll see the plan and researcher cards update as the agent works, tool cards, permission prompts and a panel for reviewing memory. The Workbench talks to B4.run over AG-UI and holds no model credentials. Those stay in server/.env.
To build your own client, follow Research Assistant Web UI.
The scaffold also installs the Inspector. Open it in a third terminal to review the records the agent saves with remember:
npx b4 inspect --cwd serverWhen you're ready to ship, compare Deployment Options and follow Node and Docker for the default self-hosted path.