ReAct Agent Example
A complete example of a ReAct (Reasoning and Acting) agent that can perform calculations, scrape web data, and answer questions.
Overview
The ReAct pattern alternates between reasoning and acting:
- Reason - Think about what to do next
- Act - Use a tool or provide an answer
- Observe - See the result
- Repeat - Continue until task is complete
Complete Example
typescript
import { createReActAgent } from '@agentforge/patterns';
import { ChatOpenAI } from '@langchain/openai';
import {
calculator,
webScraper,
currentDateTime
} from '@agentforge/tools';
import {
caching,
rateLimiting,
logging
} from '@agentforge/core/middleware';
// Create the agent
const agent = createReActAgent({
model: new ChatOpenAI({
model: 'gpt-4',
temperature: 0
}),
tools: [
calculator,
webScraper,
currentDateTime
],
maxIterations: 10,
systemPrompt: `You are a helpful research assistant.
Use the available tools to answer questions accurately.
Always show your reasoning process.
Cite sources when using web search.`,
middleware: [
// Cache results for 5 minutes
caching({
ttl: 300,
keyGenerator: (input) => {
const query = input.messages[0]?.content || '';
return `react:${query}`;
}
}),
// Rate limit to 20 requests per minute
rateLimiting({
maxRequests: 20,
windowMs: 60000
}),
// Log all interactions
logging({
level: 'info',
logInput: true,
logOutput: true
})
]
});
// Use the agent
async function main() {
const questions = [
'What is the current time?',
'Calculate the square root of 144',
'What are the latest developments in AI?',
'What is 15% of 250, and what time is it now?'
];
for (const question of questions) {
console.log(`\n❓ Question: ${question}`);
const result = await agent.invoke({
messages: [{
role: 'user',
content: question
}]
});
const answer = result.messages[result.messages.length - 1].content;
console.log(`✅ Answer: ${answer}`);
// Show reasoning steps
console.log('\n📝 Reasoning Steps:');
result.messages.forEach((msg, i) => {
if (msg.role === 'assistant') {
console.log(` ${i}. ${msg.content.substring(0, 100)}...`);
}
});
}
}
main().catch(console.error);Output Example
❓ Question: What is 15% of 250, and what time is it now?
✅ Answer: 15% of 250 is 37.5, and the current time is 14:30:00 UTC.
📝 Reasoning Steps:
1. I need to calculate 15% of 250 and get the current time...
2. Let me use the calculator tool first...
3. Now I'll get the current time...
4. I have both pieces of information. Let me provide the answer...With Streaming
typescript
import { createReActAgent } from '@agentforge/patterns';
const agent = createReActAgent({
model: new ChatOpenAI({ model: 'gpt-4' }),
tools: [calculator, currentDateTime],
maxIterations: 5
});
// Stream responses
const stream = await agent.stream({
messages: [{
role: 'user',
content: 'What is the weather in London and calculate 15% of 250?'
}]
});
for await (const chunk of stream) {
if (chunk.type === 'text') {
process.stdout.write(chunk.content);
} else if (chunk.type === 'tool_call') {
console.log(`\n🔧 Using tool: ${chunk.tool}`);
}
}With State Persistence
typescript
import { createReActAgent } from '@agentforge/patterns';
import { MemorySaver } from '@langchain/langgraph';
const checkpointSaver = new MemorySaver();
const agent = createReActAgent({
model: new ChatOpenAI({ model: 'gpt-4' }),
tools: [calculator, currentDateTime],
checkpointSaver,
maxIterations: 5
});
// First conversation
await agent.invoke({
messages: [{ role: 'user', content: 'My name is Alice' }]
}, {
configurable: { thread_id: 'user-123' }
});
// Continue conversation (remembers context)
await agent.invoke({
messages: [{ role: 'user', content: 'What is my name?' }]
}, {
configurable: { thread_id: 'user-123' }
});Error Handling
typescript
import { createReActAgent } from '@agentforge/patterns';
import { retry, timeout } from '@agentforge/core/middleware';
const agent = createReActAgent({
model: new ChatOpenAI({ model: 'gpt-4' }),
tools: [calculator, currentDateTime],
maxIterations: 5,
middleware: [
// Retry on failures
retry({
maxAttempts: 3,
delayMs: 1000,
backoff: 'exponential'
}),
// Timeout after 30 seconds
timeout({
timeoutMs: 30000
})
]
});
try {
const result = await agent.invoke({
messages: [{ role: 'user', content: 'Complex question...' }]
});
console.log('Success:', result);
} catch (error) {
console.error('Error:', error.message);
}Testing
typescript
import { describe, it, expect } from 'vitest';
import { MockLLM, AgentTestHarness } from '@agentforge/testing';
import { createReActAgent } from '@agentforge/patterns';
import { calculator } from '@agentforge/tools';
describe('ReAct Agent', () => {
const mockLLM = new MockLLM({
responses: [
'Let me calculate that...',
'The answer is 4'
]
});
const agent = createReActAgent({
model: mockLLM as any,
tools: [calculator],
maxIterations: 3
});
const harness = new AgentTestHarness(agent);
it('should use tools correctly', async () => {
await harness.invoke('What is 2+2?');
harness.assertToolCalled('calculator');
});
it('should complete within iterations', async () => {
const result = await harness.invoke('Calculate 10 * 5');
harness.assertWithinIterations(3);
});
});Key Features
- ✅ Reasoning Loop - Think before acting
- ✅ Tool Integration - Use multiple tools
- ✅ Streaming Support - Real-time responses
- ✅ State Persistence - Remember context
- ✅ Error Handling - Graceful failures
- ✅ Middleware Support - Caching, rate limiting, etc.
When to Use ReAct
Use ReAct when you need:
- Multi-step reasoning
- Tool usage based on context
- Flexible problem-solving
- Iterative refinement
Limitations
- Can be slower than simpler patterns
- May use more tokens
- Requires good tool descriptions
- Can get stuck in loops (use maxIterations)
Next Steps
- Plan-Execute Pattern - For structured tasks
- Reflection Pattern - For quality improvement
- Multi-Agent System - For complex workflows