0
# Agent Framework
1
2
ReAct agents and task execution system for complex reasoning and multi-step operations with tool usage in LlamaIndex.TS.
3
4
## Import
5
6
```typescript
7
import { ReActAgent } from "llamaindex/agent";
8
// Or main package import
9
import { ReActAgent } from "llamaindex";
10
```
11
12
## Overview
13
14
The agent framework in LlamaIndex.TS implements ReAct (Reasoning + Acting) agents that can break down complex problems, use tools, and execute multi-step workflows. Agents can access various tools including query engines, calculators, APIs, and custom functions.
15
16
## Task-Based Agent Architecture
17
18
LlamaIndex.TS implements a sophisticated task-based agent system built around the ReAct (Reasoning + Acting) pattern with support for streaming, parallel execution, and complex multi-step workflows.
19
20
### AgentRunner
21
22
The core agent execution engine that manages task scheduling and execution.
23
24
```typescript { .api }
25
class AgentRunner<AI = LLM, Store = ChatMemoryBuffer, AdditionalMessageOptions = object, AdditionalChatOptions = object> {
26
constructor(params: AgentParamsBase<AI, AdditionalMessageOptions, AdditionalChatOptions>);
27
28
chat(message: string, options?: AdditionalChatOptions & { stream?: false }): Promise<AgentChatResponse>;
29
chat(message: string, options: AdditionalChatOptions & { stream: true }): AsyncIterable<AgentTaskStepOutput>;
30
31
createTask(input: string, options?: AdditionalChatOptions): Task<AdditionalMessageOptions>;
32
33
finalize(): void;
34
}
35
```
36
37
### ReActAgent
38
39
The main ReAct agent implementation that uses structured reasoning and tool execution.
40
41
```typescript { .api }
42
class ReActAgent extends AgentRunner {
43
constructor(params: ReACTAgentParams);
44
45
// Inherited from AgentRunner
46
chat(message: string, options?: AgentChatOptions): Promise<AgentChatResponse>;
47
achat(message: string, options?: AgentChatOptions): AsyncIterable<AgentTaskStepOutput>;
48
createTask(input: string, options?: AgentChatOptions): Task;
49
50
// Agent properties
51
readonly worker: ReACTAgentWorker;
52
readonly tools: BaseToolWithCall[];
53
readonly llm: LLM;
54
readonly memory: ChatMemoryBuffer;
55
readonly verbose: boolean;
56
}
57
```
58
59
### Task System
60
61
The task system enables complex multi-step agent workflows with step-by-step execution and monitoring.
62
63
```typescript { .api }
64
class Task<AdditionalMessageOptions = object> {
65
taskId: string;
66
input: string;
67
memory: ChatMemoryBuffer;
68
extraState: Record<string, any>;
69
70
step(): Promise<TaskStepOutput>;
71
stepAsync(): AsyncIterable<TaskStepOutput>;
72
run(): Promise<AgentChatResponse>;
73
runAsync(): AsyncIterable<TaskStepOutput>;
74
75
isFinished(): boolean;
76
getResult(): TaskStepOutput | undefined;
77
}
78
79
interface TaskStepOutput {
80
output: AgentChatResponse;
81
taskStep: TaskStep;
82
nextSteps: TaskStep[];
83
isLast: boolean;
84
}
85
86
interface TaskStep<Model = LLM, Store = ChatMemoryBuffer, AdditionalMessageOptions = object, AdditionalChatOptions = object> {
87
taskId: string;
88
stepId: string;
89
input: string;
90
stepState: Record<string, any>;
91
92
// Context for step execution
93
context: AgentTaskContext<Model, Store, AdditionalMessageOptions, AdditionalChatOptions>;
94
}
95
```
96
97
## Agent Parameters and Types
98
99
### ReACTAgentParams
100
101
Configuration parameters for ReAct agents with support for static tools or dynamic tool retrieval.
102
103
```typescript { .api }
104
type ReACTAgentParams = AgentParamsBase<LLM, object, object> & {
105
maxIterations?: number;
106
reactSystemHeaderStr?: string;
107
reactSystemPrompt?: string;
108
};
109
110
type AgentParamsBase<AI, AdditionalMessageOptions, AdditionalChatOptions> =
111
| {
112
llm?: AI;
113
chatHistory?: ChatMessage<AdditionalMessageOptions>[];
114
systemPrompt?: MessageContent;
115
verbose?: boolean;
116
tools: BaseToolWithCall[];
117
additionalChatOptions?: AdditionalChatOptions;
118
}
119
| {
120
llm?: AI;
121
chatHistory?: ChatMessage<AdditionalMessageOptions>[];
122
systemPrompt?: MessageContent;
123
verbose?: boolean;
124
toolRetriever: ObjectRetriever<BaseToolWithCall>;
125
additionalChatOptions?: AdditionalChatOptions;
126
};
127
```
128
129
### Agent Response Types
130
131
```typescript { .api }
132
interface AgentChatResponse {
133
response: string;
134
sources: ToolOutput[];
135
sourceNodes?: NodeWithScore[];
136
isStreamingFinished?: boolean;
137
}
138
139
interface AgentChatOptions {
140
chatHistory?: ChatMessage[];
141
toolChoice?: ToolChoice | "auto" | "none";
142
stream?: boolean;
143
}
144
145
interface AgentTaskStepOutput {
146
output: AgentChatResponse;
147
taskStep: TaskStep;
148
nextSteps: TaskStep[];
149
isLast: boolean;
150
}
151
```
152
153
### Tool System
154
155
The tool system supports both static tool sets and dynamic tool retrieval with sophisticated parameter validation.
156
157
```typescript { .api }
158
interface BaseToolWithCall {
159
metadata: ToolMetadata;
160
call(input: string): Promise<ToolOutput>;
161
}
162
163
interface ToolMetadata {
164
name: string;
165
description: string;
166
parameters?: ToolMetadataParameters;
167
fnSchema?: any;
168
}
169
170
interface ToolMetadataParameters {
171
type: "object";
172
properties: Record<string, ToolParameterProperty>;
173
required?: string[];
174
}
175
176
interface ToolParameterProperty {
177
type: string;
178
description?: string;
179
enum?: string[];
180
items?: ToolParameterProperty;
181
}
182
183
interface ToolOutput {
184
content: string;
185
tool: BaseToolWithCall;
186
rawInput: string;
187
rawOutput?: any;
188
isError?: boolean;
189
}
190
```
191
192
### Function Tool Creation
193
194
```typescript { .api }
195
class FunctionTool<T = any, R = any> implements BaseToolWithCall {
196
static from<T, R>(
197
fn: (params: T) => R | Promise<R>,
198
metadata: ToolMetadata
199
): FunctionTool<T, R>;
200
201
constructor(
202
fn: (params: T) => R | Promise<R>,
203
metadata: ToolMetadata
204
);
205
206
metadata: ToolMetadata;
207
call(input: string): Promise<ToolOutput>;
208
}
209
```
210
211
### Agent Worker System
212
213
The worker system enables custom agent implementations and task scheduling strategies.
214
215
```typescript { .api }
216
abstract class AgentWorker<
217
AI = LLM,
218
Store = ChatMemoryBuffer,
219
AdditionalMessageOptions = object,
220
AdditionalChatOptions = object
221
> {
222
abstract initializeStep(task: Task<AdditionalMessageOptions>): TaskStep<AI, Store, AdditionalMessageOptions, AdditionalChatOptions>;
223
224
abstract runStep(
225
step: TaskStep<AI, Store, AdditionalMessageOptions, AdditionalChatOptions>,
226
task: Task<AdditionalMessageOptions>
227
): Promise<TaskStepOutput>;
228
229
abstract streamStep(
230
step: TaskStep<AI, Store, AdditionalMessageOptions, AdditionalChatOptions>,
231
task: Task<AdditionalMessageOptions>
232
): AsyncIterable<TaskStepOutput>;
233
234
abstract finalizeTask(task: Task<AdditionalMessageOptions>): void;
235
}
236
237
class ReACTAgentWorker extends AgentWorker {
238
constructor(
239
tools: BaseToolWithCall[] | ObjectRetriever<BaseToolWithCall>,
240
llm: LLM,
241
options?: {
242
maxIterations?: number;
243
reactSystemHeaderStr?: string;
244
reactSystemPrompt?: string;
245
verbose?: boolean;
246
}
247
);
248
}
249
```
250
251
## Basic Usage
252
253
### Simple Agent with Query Tool
254
255
```typescript
256
import { ReActAgent, VectorStoreIndex, Document, QueryEngineTool } from "llamaindex";
257
258
// Create knowledge base
259
const documents = [
260
new Document({ text: "The capital of France is Paris." }),
261
new Document({ text: "Python is a programming language." }),
262
new Document({ text: "Machine learning is a subset of AI." }),
263
];
264
265
const index = await VectorStoreIndex.fromDocuments(documents);
266
267
// Create query engine tool
268
const queryTool = new QueryEngineTool({
269
queryEngine: index.asQueryEngine(),
270
metadata: {
271
name: "knowledge_search",
272
description: "Search the knowledge base for factual information",
273
},
274
});
275
276
// Create agent
277
const agent = new ReActAgent({
278
tools: [queryTool],
279
llm: /* your LLM instance */,
280
verbose: true, // Enable verbose logging
281
});
282
283
// Use the agent
284
const response = await agent.chat("What is the capital of France and how is it related to programming?");
285
console.log("Agent response:", response.response);
286
287
// View sources used
288
console.log("Tools used:", response.sources.map(source => source.tool.metadata.name));
289
```
290
291
### Agent with Multiple Tools
292
293
```typescript
294
import { ReActAgent, FunctionTool, QueryEngineTool } from "llamaindex";
295
296
// Create calculator tool
297
const calculatorTool = FunctionTool.from(
298
({ a, b, operation }: { a: number; b: number; operation: string }) => {
299
switch (operation) {
300
case "add": return a + b;
301
case "subtract": return a - b;
302
case "multiply": return a * b;
303
case "divide": return a / b;
304
default: throw new Error("Unknown operation");
305
}
306
},
307
{
308
name: "calculator",
309
description: "Perform basic arithmetic operations",
310
parameters: {
311
type: "object",
312
properties: {
313
a: { type: "number", description: "First number" },
314
b: { type: "number", description: "Second number" },
315
operation: {
316
type: "string",
317
description: "Operation to perform",
318
enum: ["add", "subtract", "multiply", "divide"]
319
},
320
},
321
required: ["a", "b", "operation"],
322
},
323
}
324
);
325
326
// Create web search tool (example)
327
const webSearchTool = FunctionTool.from(
328
async ({ query }: { query: string }) => {
329
// Implement web search logic
330
return `Search results for: ${query}`;
331
},
332
{
333
name: "web_search",
334
description: "Search the web for current information",
335
parameters: {
336
type: "object",
337
properties: {
338
query: { type: "string", description: "Search query" },
339
},
340
required: ["query"],
341
},
342
}
343
);
344
345
// Create multi-tool agent
346
const multiToolAgent = new ReActAgent({
347
tools: [queryTool, calculatorTool, webSearchTool],
348
llm: /* your LLM */,
349
maxIterations: 10,
350
});
351
352
// Complex query requiring multiple tools
353
const response = await multiToolAgent.chat(
354
"Search my knowledge base for information about Python, then calculate 15 * 23, and search the web for the latest Python version."
355
);
356
```
357
358
## Advanced Usage
359
360
### Custom System Prompt
361
362
```typescript
363
const customAgent = new ReActAgent({
364
tools: [queryTool, calculatorTool],
365
llm: /* your LLM */,
366
systemPrompt: `
367
You are a helpful research assistant with access to tools.
368
369
Guidelines:
370
- Always think step by step
371
- Use tools when you need specific information or calculations
372
- Provide clear explanations of your reasoning
373
- If you can't find information, say so clearly
374
- Cite sources when using tool results
375
376
Available tools: {tool_desc}
377
`,
378
});
379
```
380
381
### Task-Based Agent Usage
382
383
```typescript
384
// Create a task for complex multi-step processing
385
const task = agent.createTask(
386
"Analyze the performance metrics in my knowledge base and calculate the year-over-year growth rate."
387
);
388
389
// Execute task step by step
390
while (!task.isFinished()) {
391
const step = await task.step();
392
console.log(`Step ${step.stepId}:`, step.output);
393
}
394
395
const result = task.getResult();
396
console.log("Final result:", result);
397
```
398
399
### Streaming Agent Responses
400
401
```typescript
402
// Stream agent responses for real-time interaction
403
for await (const chunk of agent.achat("Explain machine learning and calculate 2 + 2")) {
404
console.log("Chunk:", chunk.response);
405
406
if (chunk.sources && chunk.sources.length > 0) {
407
console.log("Tool used:", chunk.sources[0].tool.metadata.name);
408
}
409
}
410
```
411
412
## Custom Tools
413
414
### Function Tool Creation
415
416
```typescript
417
import { FunctionTool } from "llamaindex";
418
419
// Simple function tool
420
const timestampTool = FunctionTool.from(
421
() => new Date().toISOString(),
422
{
423
name: "get_timestamp",
424
description: "Get the current timestamp",
425
}
426
);
427
428
// Complex function tool with validation
429
const dataAnalysisTool = FunctionTool.from(
430
async ({ data, analysis_type }: { data: number[]; analysis_type: string }) => {
431
if (!data || data.length === 0) {
432
throw new Error("Data array cannot be empty");
433
}
434
435
switch (analysis_type) {
436
case "mean":
437
return data.reduce((a, b) => a + b, 0) / data.length;
438
case "max":
439
return Math.max(...data);
440
case "min":
441
return Math.min(...data);
442
case "sum":
443
return data.reduce((a, b) => a + b, 0);
444
default:
445
throw new Error(`Unknown analysis type: ${analysis_type}`);
446
}
447
},
448
{
449
name: "data_analysis",
450
description: "Perform statistical analysis on numerical data",
451
parameters: {
452
type: "object",
453
properties: {
454
data: {
455
type: "array",
456
items: { type: "number" },
457
description: "Array of numbers to analyze",
458
},
459
analysis_type: {
460
type: "string",
461
enum: ["mean", "max", "min", "sum"],
462
description: "Type of analysis to perform",
463
},
464
},
465
required: ["data", "analysis_type"],
466
},
467
}
468
);
469
```
470
471
### Custom Tool Class
472
473
```typescript
474
import { BaseTool, ToolMetadata, ToolOutput } from "llamaindex";
475
476
class DatabaseTool implements BaseTool {
477
metadata: ToolMetadata = {
478
name: "database_query",
479
description: "Execute SQL queries against the database",
480
parameters: {
481
type: "object",
482
properties: {
483
query: { type: "string", description: "SQL query to execute" },
484
table: { type: "string", description: "Table to query" },
485
},
486
required: ["query"],
487
},
488
};
489
490
async call(input: string): Promise<ToolOutput> {
491
try {
492
const params = JSON.parse(input);
493
494
// Implement database query logic
495
const result = await this.executeQuery(params.query, params.table);
496
497
return {
498
content: JSON.stringify(result),
499
tool: this,
500
rawInput: input,
501
rawOutput: result,
502
};
503
} catch (error) {
504
return {
505
content: `Database error: ${error.message}`,
506
tool: this,
507
rawInput: input,
508
isError: true,
509
};
510
}
511
}
512
513
private async executeQuery(query: string, table?: string): Promise<any> {
514
// Implement your database query logic here
515
return { rows: [], count: 0 };
516
}
517
}
518
519
// Use custom tool
520
const dbTool = new DatabaseTool();
521
const agent = new ReActAgent({
522
tools: [dbTool],
523
llm: /* your LLM */,
524
});
525
```
526
527
## Agent Memory and State
528
529
### Persistent Memory
530
531
```typescript
532
import { ChatMemoryBuffer } from "llamaindex";
533
534
// Create agent with persistent memory
535
const persistentMemory = new ChatMemoryBuffer({
536
tokenLimit: 4000,
537
chatHistory: [], // Start with empty history
538
});
539
540
const agent = new ReActAgent({
541
tools: [queryTool],
542
llm: /* your LLM */,
543
memory: persistentMemory,
544
});
545
546
// The agent will remember conversation context across sessions
547
await agent.chat("My name is John.");
548
await agent.chat("What's my name?"); // Agent remembers "John"
549
```
550
551
### Memory Management
552
553
```typescript
554
// Save agent memory state
555
const saveAgentState = (agent: ReActAgent, filename: string) => {
556
const state = {
557
chatHistory: agent.memory.getAll(),
558
timestamp: new Date().toISOString(),
559
};
560
561
// Save to file or database
562
// fs.writeFileSync(filename, JSON.stringify(state, null, 2));
563
};
564
565
// Load agent memory state
566
const loadAgentState = (agent: ReActAgent, stateData: any) => {
567
agent.memory.set(stateData.chatHistory);
568
};
569
```
570
571
## Error Handling and Robustness
572
573
### Tool Error Handling
574
575
```typescript
576
// Create robust tools with error handling
577
const robustCalculatorTool = FunctionTool.from(
578
({ expression }: { expression: string }) => {
579
try {
580
// Safely evaluate mathematical expressions
581
const result = eval(expression.replace(/[^0-9+\-*/().\s]/g, ''));
582
583
if (typeof result !== 'number' || !isFinite(result)) {
584
throw new Error("Invalid mathematical expression");
585
}
586
587
return result;
588
} catch (error) {
589
throw new Error(`Calculation error: ${error.message}`);
590
}
591
},
592
{
593
name: "safe_calculator",
594
description: "Safely evaluate mathematical expressions",
595
parameters: {
596
type: "object",
597
properties: {
598
expression: {
599
type: "string",
600
description: "Mathematical expression to evaluate (numbers and +, -, *, /, parentheses only)",
601
},
602
},
603
required: ["expression"],
604
},
605
}
606
);
607
```
608
609
### Agent Error Recovery
610
611
```typescript
612
const robustAgentChat = async (agent: ReActAgent, message: string): Promise<AgentChatResponse | null> => {
613
try {
614
const response = await agent.chat(message);
615
616
// Validate response
617
if (!response.response || response.response.trim().length === 0) {
618
console.warn("Empty response from agent");
619
return null;
620
}
621
622
return response;
623
} catch (error) {
624
console.error("Agent error:", error);
625
626
// Handle specific error types
627
if (error.message.includes("max_iterations")) {
628
console.error("Agent exceeded maximum iterations");
629
// Possibly retry with different parameters
630
} else if (error.message.includes("tool_error")) {
631
console.error("Tool execution failed");
632
// Possibly retry without problematic tools
633
}
634
635
return null;
636
}
637
};
638
```
639
640
## Performance Optimization
641
642
### Tool Selection Optimization
643
644
```typescript
645
import { ObjectRetriever } from "llamaindex";
646
647
// Create tool retriever for large tool sets
648
const toolRetriever = new ObjectRetriever({
649
objects: [queryTool, calculatorTool, webSearchTool, /* many more tools */],
650
retrieveTopK: 3, // Only consider top 3 most relevant tools
651
});
652
653
const optimizedAgent = new ReActAgent({
654
tools: [], // Empty - tools will be retrieved dynamically
655
toolRetriever,
656
llm: /* your LLM */,
657
});
658
```
659
660
### Parallel Tool Execution
661
662
```typescript
663
// Custom agent with parallel tool execution capability
664
class ParallelReActAgent extends ReActAgent {
665
async executeToolsInParallel(toolCalls: Array<{tool: BaseTool, input: string}>) {
666
const results = await Promise.all(
667
toolCalls.map(async ({ tool, input }) => {
668
try {
669
return await tool.call(input);
670
} catch (error) {
671
return {
672
content: `Error: ${error.message}`,
673
tool,
674
rawInput: input,
675
isError: true,
676
};
677
}
678
})
679
);
680
681
return results;
682
}
683
}
684
```
685
686
## Integration Patterns
687
688
### Agent Workflows
689
690
```typescript
691
// Create a workflow with multiple specialized agents
692
class AgentWorkflow {
693
private researchAgent: ReActAgent;
694
private analysisAgent: ReActAgent;
695
private reportAgent: ReActAgent;
696
697
constructor() {
698
this.researchAgent = new ReActAgent({
699
tools: [webSearchTool, queryTool],
700
llm: /* your LLM */,
701
systemPrompt: "You are a research specialist. Gather comprehensive information.",
702
});
703
704
this.analysisAgent = new ReActAgent({
705
tools: [calculatorTool, dataAnalysisTool],
706
llm: /* your LLM */,
707
systemPrompt: "You are a data analyst. Analyze and interpret information.",
708
});
709
710
this.reportAgent = new ReActAgent({
711
tools: [], // No tools, just synthesis
712
llm: /* your LLM */,
713
systemPrompt: "You are a report writer. Synthesize information into clear reports.",
714
});
715
}
716
717
async executeWorkflow(query: string): Promise<string> {
718
// Step 1: Research
719
const researchResponse = await this.researchAgent.chat(`Research: ${query}`);
720
721
// Step 2: Analysis
722
const analysisResponse = await this.analysisAgent.chat(
723
`Analyze this research: ${researchResponse.response}`
724
);
725
726
// Step 3: Report
727
const reportResponse = await this.reportAgent.chat(
728
`Create a report based on this research and analysis:
729
Research: ${researchResponse.response}
730
Analysis: ${analysisResponse.response}`
731
);
732
733
return reportResponse.response;
734
}
735
}
736
```
737
738
## Best Practices
739
740
### Agent Configuration
741
742
```typescript
743
// Configure agents for different use cases
744
const createAgent = (useCase: string, tools: BaseTool[]) => {
745
const baseConfig = {
746
tools,
747
llm: /* your LLM */,
748
verbose: process.env.NODE_ENV === "development",
749
};
750
751
switch (useCase) {
752
case "research":
753
return new ReActAgent({
754
...baseConfig,
755
maxIterations: 15, // Allow more iterations for complex research
756
systemPrompt: "You are a thorough researcher. Use tools to gather comprehensive information.",
757
});
758
759
case "analysis":
760
return new ReActAgent({
761
...baseConfig,
762
maxIterations: 8, // Focused analysis
763
systemPrompt: "You are a data analyst. Use tools to analyze information and provide insights.",
764
});
765
766
case "support":
767
return new ReActAgent({
768
...baseConfig,
769
maxIterations: 5, // Quick support responses
770
systemPrompt: "You are a helpful support agent. Provide clear, actionable assistance.",
771
});
772
773
default:
774
return new ReActAgent(baseConfig);
775
}
776
};
777
```
778
779
### Monitoring and Debugging
780
781
```typescript
782
// Add comprehensive logging
783
const monitoredAgent = new ReActAgent({
784
tools: [queryTool, calculatorTool],
785
llm: /* your LLM */,
786
verbose: true,
787
});
788
789
// Wrap agent calls with monitoring
790
const monitoredChat = async (agent: ReActAgent, message: string) => {
791
const startTime = Date.now();
792
793
try {
794
const response = await agent.chat(message);
795
const duration = Date.now() - startTime;
796
797
console.log({
798
timestamp: new Date().toISOString(),
799
message: message.substring(0, 100),
800
responseLength: response.response.length,
801
toolsUsed: response.sources.map(s => s.tool.metadata.name),
802
duration: `${duration}ms`,
803
memorySize: agent.memory.getAll().length,
804
});
805
806
return response;
807
} catch (error) {
808
console.error({
809
timestamp: new Date().toISOString(),
810
message: message.substring(0, 100),
811
error: error.message,
812
duration: `${Date.now() - startTime}ms`,
813
});
814
throw error;
815
}
816
};
817
```