There are many ways to build apps and systems. In today’s AI-native world, the possibilities are endless.
Now imagine you’re tasked with building a multi-agent research system, with one key requirement: don’t over-engineer it.
KISS — Keep It Simple, Stupid.
This is the scenario given to you:
"A friend asked me to pull together everything on Eli Lilly's Q4 results, any ongoing GLP-1 trials they've filed recently, and how the financial press was covering it. Build a multi-agent research system that handles that so you can go accomplish 10 other things while the system gets you the result."
Now, let’s break this down:
- Pull together everything on Eli Lilly’s Q4 results
- Identify any GLP-1 trials they’ve filed recently
- See how the financial press has been covering it
When broken down like this, it’s simply three queries across three completely different domains: SEC filings, clinical trials, and live news.
Three different tools, three different contexts and then stitching it all together manually at the end. It’s similar to the "15-tab" problem.
And it gets worse when you’re building an AI app. You often end up maintaining a research pipeline that’s just a collection of disconnected scripts, held together by copy-paste.
The Architecture
We have three different contexts, so three domains. We'll use the Vercel AI SDK to handle all three domains in parallel.
One query, three specialist agents running simultaneously, and a single synthesized response. With the @valyu/ai-sdk package, plugging in domain-specific data sources takes just one import—no manual tool definitions required.
This is the architecture: the Orchestrator–Worker pattern.
One orchestrator agent understands the query, dispatches the right specialists in parallel, and synthesizes the results. Each specialist has its own tool suite and domain expertise
The tools come from @valyu/ai-sdk, a package that provides ready-made Vercel AI SDK tools backed by Valyu's search API. No manual tool() definitions, no Zod schemas for parameters, no custom execute functions.
Import the tool, drop it into your tools object, done.
Why Parallel Dispatch Matters
Sequential agents are the wrong default for research workloads. Each specialist takes 4-6 seconds.
Three in sequence is 15+ seconds. Three specialists running simultaneously gets you results in the time it takes the slowest one to finish.
Data Flow: From Query to Report
For a query like "What's Eli Lilly's financial position and do their GLP-1 trials support the revenue projections?":
Setup
Install Nextjs and then add the following packages...
pnpm add ai @ai-sdk/anthropic @valyu/ai-sdk @ai-sdk/react zod valyu-js
Create .env.local:
ANTHROPIC_API_KEY=your_key_here
VALYU_API_KEY=your_key_here
Grab your Anthropic and Valyu API keys.
Both keys are read from environment automatically. @valyu/ai-sdk picks up VALYU_API_KEY without any explicit configuration.
Project structure:
research-nexus/
├── src/
│ ├── agents/
│ │ ├── financial-analyst.ts # Financial analyst
│ │ ├── scientist.ts # Scientist
│ │ ├── journalist.ts # Journalist
│ │ └── orchestrator.ts # Query router + synthesizer
│ └── app/
│ └── api/
│ └── chat/
│ └── route.ts # Next.js API route
└── package.json
No lib/ or tools/ directories. The tools come pre-built.
The Specialist Agents
Each specialist is a ToolLoopAgent with domain-specific tools from @valyu/ai-sdk. The agent loop lets the model chain multiple tool calls before returning. Searching SEC filings, then cross-referencing earnings, then adding macro context, all within a single agent invocation.
Financial Analyst
import { ToolLoopAgent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { secSearch, financeSearch, economicsSearch } from "@valyu/ai-sdk";
export const financialAnalystAgent = new ToolLoopAgent({
model: anthropic("claude-haiku-4-5-20251001"),
instructions: `You are a senior financial analyst specializing in SEC filings, market data, and economic research.
Your capabilities:
- Search and analyze SEC filings (10-K, 10-Q, 8-K, proxy statements)
- Look up financial data including stock prices, earnings, income statements
- Research economic indicators and macro data
When responding:
- Always cite the specific filing type and date
- Present financial figures clearly with proper formatting
- Highlight key risks, trends, and material changes
- Compare metrics across periods when relevant
- Be precise about numbers — never approximate when exact data is available`,
tools: {
secSearch: secSearch({ maxNumResults: 3, responseLength: "short" }),
financeSearch: financeSearch({ maxNumResults: 3, responseLength: "short" }),
economicsSearch: economicsSearch({ maxNumResults: 3, responseLength: "short" }),
},
});
financial-analyst.ts
Scientist
import { ToolLoopAgent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { bioSearch, paperSearch } from "@valyu/ai-sdk";
export const medicalResearcherAgent = new ToolLoopAgent({
model: anthropic("claude-haiku-4-5-20251001"),
instructions: `You are a medical and life sciences research specialist with expertise in clinical trials, drug discovery, and biomedical literature.
Your capabilities:
- Search clinical trial databases for trial status, results, and endpoints
- Look up FDA drug labels, approvals, and safety information
- Research biomedical literature from PubMed, bioRxiv, and medRxiv
- Analyze academic papers on drugs, therapies, and medical devices
When responding:
- Always cite trial IDs (NCT numbers), DOIs, or publication references
- Clearly distinguish between preliminary and peer-reviewed findings
- Note the phase of clinical trials and their primary endpoints
- Flag any safety concerns or adverse events mentioned in the data
- Use proper medical terminology but explain it when needed`,
tools: {
bioSearch: bioSearch({ maxNumResults: 3, responseLength: "short" }),
paperSearch: paperSearch({ maxNumResults: 3, responseLength: "short" }),
},
});
scientist.ts
Journalist
import { ToolLoopAgent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { webSearch } from "@valyu/ai-sdk";
export const journalistAgent = new ToolLoopAgent({
model: anthropic("claude-haiku-4-5-20251001"),
instructions: `You are an investigative journalist and news analyst with access to real-time web sources.
Your capabilities:
- Search the web for breaking news and current events
- Find and cross-reference multiple news sources on a topic
- Track developing stories and provide timeline context
- Research background on people, organizations, and events
When responding:
- Always attribute information to specific sources
- Present multiple perspectives when covering controversial topics
- Distinguish between confirmed facts and unverified reports
- Provide publication dates so readers know how current the information is
- Summarize key points clearly, then provide supporting details`,
tools: {
webSearch: webSearch({ maxNumResults: 5, responseLength: "short" }),
},
});
journalist.ts
Three agents, three imports from @valyu/ai-sdk, zero custom tool definitions.
The Orchestrator
The orchestrator handles query classification, parallel dispatch, and synthesis.
import { ToolLoopAgent, tool, stepCountIs } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { financialAnalystAgent } from "./financial-analyst";
import { scientistAgent } from "./scientist";
import { journalistAgent } from "./journalist";
const financialAnalystTool = tool({
description:
"Delegate to the Financial Analyst agent for SEC filings, stock data, earnings reports, economic indicators, and financial analysis.",
inputSchema: z.object({
task: z.string().describe("The financial research task to complete"),
}),
execute: async ({ task }, { abortSignal }) => {
const result = await financialAnalystAgent.generate({
prompt: task,
abortSignal,
});
return result.text;
},
});
const scientistTool = tool({
description:
"Delegate to the Scientist agent for clinical trials, drug information, FDA data, biomedical papers, and life sciences research.",
inputSchema: z.object({
task: z.string().describe("The medical/life sciences research task to complete"),
}),
execute: async ({ task }, { abortSignal }) => {
const result = await scientistAgent.generate({
prompt: task,
abortSignal,
});
return result.text;
},
});
const journalistTool = tool({
description:
"Delegate to the Journalist agent for real-time news, current events, breaking stories, and web-based research on any topic.",
inputSchema: z.object({
task: z.string().describe("The news/research task to complete"),
}),
execute: async ({ task }, { abortSignal }) => {
const result = await journalistAgent.generate({
prompt: task,
abortSignal,
});
return result.text;
},
});
export const orchestratorAgent = new ToolLoopAgent({
model: anthropic("claude-haiku-4-5-20251001"),
instructions: `You are a research orchestrator that routes queries to specialized agents.
You have three specialist agents available:
1. **Financial Analyst** — SEC filings, stock data, earnings, financial statements, economic data
2. **Scientist** — Clinical trials, drug discovery, FDA data, biomedical papers
3. **Journalist** — Real-time news, current events, web research
Your job:
- Analyze the user's query and delegate to the right specialist(s)
- For questions that span multiple domains, call multiple agents
- Synthesize results from multiple agents into a coherent response
- If a query doesn't fit any specialist, answer it yourself
- Always be clear about which sources informed your response`,
tools: {
financialAnalyst: financialAnalystTool,
scientist: scientistTool,
journalist: journalistTool,
},
stopWhen: stepCountIs(10),
});
The orchestrator agent acts as a smart router. It receives the user's query, analyzes what domains it touches, and delegates work to the right specialist(s).
It doesn't do the research itself. Instead, it calls one or more sub-agents as tools:
- Financial Analyst for SEC/market data,
- Scientist for clinical trials and biomedical literature,
- Journalist for real-time news.
For cross-domain questions like "How does Eli Lilly's GLP-1 pipeline affect their stock outlook?", it calls multiple specialists in sequence.
Sub-Agents as Tools. Each specialist is a ToolLoopAgent wrapped in a tool() call, which makes it callable by the orchestrator just like any other function.
When invoked, the sub-agent runs its own independent loop, calling Valyu search tools (like secSearch or bioSearch), reading the results, and synthesizing a response.
The sub-agent's final text is returned to the orchestrator as the tool's output.
Loop Control. The stopWhen: stepCountIs(10) on the orchestrator caps it at 10 loop iterations to prevent runaway execution. The sub-agents use the default limit of 20 steps. Within those bounds, each agent is free to make multiple tool calls. For example, the Financial Analyst might search SEC filings first, then cross-reference with earnings data, all within a single invocation before returning its findings.
The API Route
// app/api/chat/route.ts
import { createAgentUIStreamResponse } from "ai";
import { orchestratorAgent } from "@/agents/orchestrator";
export async function POST(request: Request) {
const { messages } = await request.json();
return createAgentUIStreamResponse({
agent: orchestratorAgent,
uiMessages: messages,
});
}
For the sake of keeping this post short, here’s the repo: multi-agent-research-sys.
You’ll find all the UI pages there. Clone the project and run it locally to explore the full setup.
Running it
pnpm dev
Output:
What @valyu/ai-sdk Provides
The package ships ten tools that cover the major research verticals:
| Tool | Data Sources |
|---|---|
secSearch() |
SEC 10-K, 10-Q, 8-K filings, EDGAR full-text |
financeSearch() |
Stocks, earnings, balance sheets, insider trades, dividends |
economicsSearch() |
FRED, BLS, World Bank, US government spending |
bioSearch() |
ClinicalTrials.gov, DrugBank, ChEMBL, FDA labels, Open Targets |
paperSearch() |
PubMed, arXiv, bioRxiv, medRxiv, academic publishers |
webSearch() |
Real-time web, news, general content |
patentSearch() |
USPTO, global patent databases |
companyResearch() |
Comprehensive company intelligence |
datasources() |
List available data sources |
datasourcesCategories() |
List available categories |
Each tool accepts optional configuration: maxNumResults, relevanceThreshold, includedSources for source-level filtering. All read VALYU_API_KEY from the environment by default.
The difference from web only search matters most for the financial and biomedical agents.
A web search for "Eli Lilly 10-K 2024 risk factors" returns SEO articles about the filing. secSearch() returns the actual filing text.
A web search for "tirzepatide Phase 3 results" returns health news coverage. bioSearch() returns the ClinicalTrials.gov entries and DrugBank compound data.
That's primary source access vs secondary commentary. A meaningful difference for research quality.
Extending the System
Adding a fourth specialist (patents, legal, government) requires:
- One agent file importing the relevant
@valyu/ai-sdktools - Adding the new agent to the orchestrator
Ten specialists in parallel takes the same wall-clock time as three.
The full code is on GitHub. Clone, run and explore!
Get a Valyu API key at platform.valyu.ai. $10 free credit, no credit card required.






Top comments (0)