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';
// 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.`
});
// Optional: Apply middleware to enhance the agent
// Note: Middleware wraps individual nodes, not the entire agent graph
// For production use, you can wrap specific nodes with middleware:
// import { production, withCache, withRateLimit } from '@agentforge/core';
// const enhancedNode = production(myNode, { nodeName: 'reasoning', enableMetrics: 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) {
// Access ReAct state fields for incremental updates
if (chunk.actions && chunk.actions.length > 0) {
const latestAction = chunk.actions[chunk.actions.length - 1];
console.log(`\n🔧 Using tool: ${latestAction.name}`);
}
if (chunk.response) {
console.log('\n✅ Response:', chunk.response);
}
}With State Persistence
typescript
import { createReActAgent } from '@agentforge/patterns';
import { MemorySaver } from '@langchain/langgraph';
const checkpointer = new MemorySaver();
const agent = createReActAgent({
model: new ChatOpenAI({ model: 'gpt-4' }),
tools: [calculator, currentDateTime],
checkpointer,
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
Middleware should be applied to individual nodes, not the agent itself:
typescript
import { createReActAgent } from '@agentforge/patterns';
import { withRetry, withTimeout } from '@agentforge/core';
import type { NodeFunction } from '@agentforge/core';
// Create the agent
const agent = createReActAgent({
model: new ChatOpenAI({ model: 'gpt-4' }),
tools: [calculator, currentDateTime],
maxIterations: 5
});
// To apply middleware, you would wrap individual nodes in the graph
// For example, if you have access to the graph nodes:
// const enhancedNode = withRetry(
// withTimeout(myNode, { timeout: 30000 }),
// { maxAttempts: 3, backoff: 'exponential' }
// );
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,
AgentTestRunner,
assertToolCalled,
assertIterationsWithinLimit
} 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 runner = new AgentTestRunner(agent, {
timeout: 5000,
captureSteps: true
});
it('should use tools correctly', async () => {
const result = await runner.run({
messages: [{ role: 'user', content: 'What is 2+2?' }]
});
expect(result.passed).toBe(true);
assertToolCalled(result.finalState.actions || [], 'calculator');
});
it('should complete within iterations', async () => {
const result = await runner.run({
messages: [{ role: 'user', content: 'Calculate 10 * 5' }]
});
expect(result.passed).toBe(true);
assertIterationsWithinLimit(result.finalState.iteration || 0, 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