A minimal agentic loop in one TypeScript file: you give it a research question, it
searches the web (Tavily) in a loop until it's confident, then returns a synthesized
answer with sources. Built to be read, not shipped — no framework, just the
Anthropic SDK and fetch.
The whole agent is src/agent.ts:
- The question goes into a
historyarray; the full history is resent every turn. - The model is called with one custom tool,
web_search(query). - If the response contains a
tool_useblock, our code calls Tavily, appends atool_resultto history, and loops. - If the response has no tool call, that's the final answer.
- Hard cap of 15 iterations — on the last one the tool is withheld and the model is told to synthesize from what it has.
The system prompt tells the model to break the question into sub-searches, cross-check claims across at least 2 sources, and answer in a fixed format (direct answer → detail → sources).
Requires Node 18+ (uses built-in fetch).
npm install
cp .env.example .env # then fill in both keysYou need:
ANTHROPIC_API_KEY— https://platform.claude.com/TAVILY_API_KEY— https://tavily.com/ (free tier is fine)
npm run research -- "What caused the 2024 CrowdStrike outage and what changed since?"Each step is logged to the console — which query it searched, how many results came back, and when it decided to stop — so you can watch the loop work.
- Model:
claude-sonnet-5(adaptive thinking is on by default; the loop appends the fullresponse.contentback to history each turn, which keeps thinking andtool_useblocks intact as the API requires). - The Anthropic SDK retries rate limits and 5xx errors itself; Tavily calls get a small exponential-backoff wrapper.
- A failed search is fed back to the model as an
is_errortool result so it can adapt (rephrase the query) instead of crashing the run.