You asked your financial agent for NVIDIA's current P/E ratio. It answered: 40.2.
The actual number was 45.65.
You asked it to summarize the key risks from a company's latest 10-K. It cited concerns that were quietly removed two annual reports ago.
You asked for Apple's most recent quarterly revenue. Off by $3 billion.
This is not a hallucination problem in the sense you might think. The LLM isn't randomly generating numbers. It's retrieving the most statistically likely answer from its training data, and doing it confidently. The problem is that financial data has a shelf life measured in hours, sometimes minutes and LLM training data has a shelf life measured in years or months.
This is a data access problem, not an intelligence problem. And it has a clean fix.
Why the training cutoff ruins financial agents
GPT-5.2's training data cuts off is August 31, 2025. Claude 4.6 Sonnet's is August 2025.
Stock prices move by the second. Earnings drop quarterly. The Fed makes a rate decision and markets reprice overnight. A company files an 8-K about a material event and that changes everything. LLMs have none of this.
What makes it worse is that the model doesn't know it's wrong. When you ask for Microsoft's current P/E ratio, it has an answer. That answer was accurate at some point during training. It delivers it with the same confidence as if it just pulled the number off a live exchange. No hedging, no "as of my knowledge cutoff" qualifier, unless you've explicitly prompted for it, and even then it often still gives you a number.
The result: An agent that sounds authoritative while being factually wrong on every time-sensitive financial data point.
For general Q&A this is acceptable. For anything financial, it's a liability.
The two approaches that don't actually work
Approach 1: Prompt the model harder
const result = await generateText({
model: openai('gpt-5.2'),
prompt: `You are a financial expert. Always provide accurate,
up-to-date financial data. Today's date is ${new Date().toISOString()}.
What is Apple's current stock price?`,
});
This does nothing useful. Telling the model today's date doesn't give it access to today's data. It still answers from training data. Worse, the explicit date sometimes triggers more confident wrong answers because the model pattern-matches "I know this domain" and generates a plausible-sounding number.
Approach 2: RAG with financial documents
Some teams build a RAG pipeline: scrape financial reports, chunk them, embed them, retrieve on query. This is better than nothing but it creates a new set of problems:
- You're now responsible for keeping the document store current
- Scraped financial documents lose structure (tables, footnotes, cross-references)
- Your retrieval quality determines your answer quality
- SEC filings alone average 40,000 words. Chunking strategies matter enormously
- You're essentially rebuilding a financial data API, badly
The root problem isn’t your retrieval strategy. It’s that you’re trying to solve a live data problem with a static data architecture.
Let’s be honest. It’s 2026. Why are you still building and maintaining your own RAG pipeline from scratch?
Vector DB tuning. Chunking debates. Re-indexing jobs. Infra bills creeping up every month. Edge cases multiplying. It gets expensive fast. And the maintenance burden compounds even faster.
The actual fix: live data as tools
The correct mental model is this: An LLM should reason over financial data, not store it.
The LLM is good at understanding context, synthesizing information, drawing conclusions, and communicating clearly. It's bad at being a database. Stop asking it to be one.
The fix is to give your agent tools that query live financial data at inference time. When the agent needs a stock price, it calls a tool and gets the current price. When it needs SEC filings, it searches them in real time. The LLM never touches a stale number.
Here's what this looks like with the Vercel AI SDK and TypeScript:
Basic setup
pnpm add @valyu/ai-sdk ai @ai-sdk/openai
import { generateText, stepCountIs } from "ai";
import { financeSearch } from "@valyu/ai-sdk";
import { openai } from "@ai-sdk/openai";
const { text } = await generateText({
model: openai('gpt-5.2'),
prompt: 'What is the current P/E ratio for NVIDIA and how does it compare to AMD?',
tools: {
financeSearch: financeSearch(),
},
stopWhen: stepCountIs(5),
});
console.log(text);
When the agent runs this, it doesn't guess. It calls financeSearch with a query, gets back current market data, and reasons over it. The number it tells you is the number that was retrieved from a live source at the moment you asked.
Building a financial research agent
Here's a more complete example, a streaming financial agent you can drop into a Next.js API route:
// app/api/finance/route.ts
import { openai } from '@ai-sdk/openai';
import { financeSearch } from '@valyu/ai-sdk';
import { streamText } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-5.2'),
messages,
system: `You are a financial analyst with access to real-time market data and SEC filings. When asked about any financial metric, stock price, earnings figure, or company filing, always use your search tool to retrieve current data. Never rely on prior knowledge for financial figures. Cite your sources.`,
tools: {
searchFinance: financeSearch(),
},
});
return result.toDataStreamResponse();
}
The critical part is the system prompt instruction: "Always use your search tool to retrieve current data. Never rely on prior knowledge for financial figures."
This forces the agent to go live on every financial query instead of falling back to training data.
Going deeper: SEC filings and Earnings data
Stock prices are the obvious case. But the same problem applies to everything structural: balance sheets, income statements, risk factors, insider transactions, earnings guidance.
Here's how to search SEC filings specifically:
import { Valyu } from "valyu-js";
const valyu = new Valyu();
// Search for specific disclosure language across recent 10-K filings
const response = await valyu.search(
"material risk factors related to AI compute supply chain 2024",
{
searchType: "proprietary",
includedSources: ["valyu/valyu-sec-filings"],
maxNumResults: 10,
responseLength: "large",
}
);
response.results.forEach((result) => {
console.log(`Filing: ${result.title}`);
console.log(`Source: ${result.url}`);
console.log(`Excerpt: ${result.content.slice(0, 400)}`);
});
This returns actual filing content. Not summaries, not news articles about filings, the actual text from 10-Ks.
Natural language queries work. You don't need ticker symbols or accession numbers.
For combining market data with fundamentals in one agent:
import { generateText, stepCountIs } from "ai";
import { financeSearch } from "@valyu/ai-sdk";
import { openai } from "@ai-sdk/openai";
const { text } = await generateText({
model: openai('gpt-5.2'),
prompt: `Analyze Microsoft's financial position: current valuation
multiples, most recent quarterly earnings vs expectations, and any
material risk disclosures from their latest 10-K.`,
tools: {
finance: financeSearch(),
},
stopWhen: stepCountIs(10),
});
The agent will make multiple tool calls. One for market data, one for earnings, one for SEC filings and synthesize the results into a coherent analysis. All from live sources.
Combining multiple financial data sources
The most useful financial agents cross-reference data types. An earnings miss is more meaningful when you can see it alongside the stock reaction, the analyst revision history, and what management said in the 8-K. Here's a multi-source pattern:
import { Valyu } from "valyu-js";
const valyu = new Valyu();
async function analyzeCompany(ticker: string) {
// Parallel queries across data types
const [marketData, fundamentals, filings] = await Promise.all([
valyu.search(`${ticker} stock price market cap volume`, {
searchType: "proprietary",
includedSources: ["valyu/valyu-stocks"],
maxNumResults: 3,
}),
valyu.search(`${ticker} earnings revenue EPS most recent quarter`, {
searchType: "proprietary",
includedSources: [
"valyu/valyu-earnings-US",
"valyu/valyu-income-statement-US",
],
maxNumResults: 5,
}),
valyu.search(`${ticker} 10-K risk factors material disclosures`, {
searchType: "proprietary",
includedSources: ["valyu/valyu-sec-filings"],
maxNumResults: 5,
responseLength: "large",
}),
]);
return {
market: marketData.results,
fundamentals: fundamentals.results,
filings: filings.results,
};
}
This is the pattern that makes financial agents actually useful. You're not asking the LLM to recall financial data, you're giving it three live data streams to reason over.
Handling the response in a streaming UI
If you want this in a chat interface, the Vercel AI SDK handles the streaming side cleanly:
// app/page.tsx
'use client';
import { useChat } from 'ai/react';
export default function FinanceAgent() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/finance',
});
return (
<div className="flex flex-col h-screen max-w-3xl mx-auto p-4">
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}
>
<div
className={`rounded-lg px-4 py-2 max-w-2xl ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-900'
}`}
>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="text-gray-400 text-sm">Searching live data...</div>
)}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask about any company or market..."
className="flex-1 px-4 py-2 border rounded-lg"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading}
className="px-6 py-2 bg-blue-500 text-white rounded-lg disabled:bg-gray-300"
>
{isLoading ? 'Searching...' : 'Ask'}
</button>
</form>
</div>
);
}
What changes once you do this
The quantitative difference is real. On a benchmark of 120 finance-specific questions, agents using live proprietary data access score 73% accuracy. Agents using GPT with no tool access score significantly lower, and every miss is a confident, plausible-sounding wrong answer.
A more practical scenario: When a user asks your agent about a company's debt-to-equity ratio, they're probably making a decision. The cost of a confidently wrong answer is not a user complaint, it's a user making a bad decision because your tool told them something false with authority.
The architecture shift here is small. You're adding a tool definition and changing a system prompt instruction. The result is an agent that reasons accurately over live financial data instead of pattern-matching from stale training.
FAQ
Q: Does this work with Claude and other models, not just OpenAI?
Yes. The Vercel AI SDK supports any model through their provider system. Swap openai('gpt-5.2') for anthropic('claude-sonnet-4-6') and the tool calling works identically. Financial data tools are model-agnostic.
Q: What financial data sources does this actually cover?
The financeSearch tool routes across stocks (200K+), crypto (200+ coins), forex (180+ pairs), ETFs (25K+), plus company fundamentals, earnings, balance sheets, income statements, cash flows, dividends, insider transactions. And SEC filings (3M+ documents: 10-K, 10-Q, 8-K, Form 4s). All queryable via natural language.
Q: What about FRED economic data and macro indicators?
You can include valyu/valyu-fred and valyu/valyu-bls as sources in the search. Same pattern. Just pass them in includedSources and query in natural language. Useful for macro context alongside company-level data.
Q: How do I avoid the agent making too many tool calls and running up costs?
stopWhen: stepCountIs(N) from the Vercel AI SDK controls the maximum number of tool call steps. For simple queries, stepCountIs(3) is usually enough. For deep research queries, stepCountIs(10) gives more room. You can also set maxPrice on the search tool itself to cap per-result retrieval costs.
Q: Should I still use RAG for financial data?
RAG makes sense for private documents you own. Internal research reports, your own financial models, proprietary analysis. For public financial data (market prices, SEC filings, earnings) that changes continuously, live API access is more appropriate. Don't use static RAG for data that has a freshness requirement.
Q: Does this work for real-time stock prices or just historical data?
Both. Market data has a 1-5 minute delay on real-time prices. Historical data is available going back years. For most analytical use cases, the 1-5 minute delay is irrelevant. If you're building a trading system that requires millisecond precision, you need a dedicated market data feed. For financial research agents, this is more than sufficient.
The code in this article is runnable as-is with a VALYU_API_KEY and OPENAI_API_KEY in your environment.
Full working examples are in the Valyu cookbook on GitHub.
Here's a real-world Finance AI agent - https://finance.valyu.ai. The repo is open-source
If you're building something with this, drop it in the comments. I'm curious what use cases people are working on.
';" />
';" />