-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: updating DOCs, fix merge with main
- Loading branch information
1 parent
226aaf5
commit a934b19
Showing
14 changed files
with
2,504 additions
and
1,324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,3 +137,4 @@ dist | |
.idea | ||
/experiments | ||
.npmrc | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,232 @@ | ||
# Agents | ||
|
||
AI agents built on large language models control the path to solving a complex problem. They can typically act on feedback to refine their plan of action, a capability that can improve performance and help them accomplish more sophisticated tasks. | ||
|
||
We recommend reading the [following article](https://research.ibm.com/blog/what-are-ai-agents-llm) to learn more. | ||
|
||
## Implementation in Bee Agent Framework | ||
|
||
An agent can be thought of as a program powered by LLM. The LLM generates structured output that is then processed by your program. | ||
|
||
Your program then decides what to do next based on the retrieved content. It may leverage a tool, reflect, or produce a final answer. | ||
Before the agent determines the final answer, it performs a series of `steps`. A step might be calling an LLM, parsing the LLM output, or calling a tool. | ||
|
||
Steps are grouped in a `iteration`, and every update (either complete or partial) is emitted to the user. | ||
|
||
### Bee Agent | ||
|
||
Our Bee Agent is based on the `ReAct` ([Reason and Act](https://arxiv.org/abs/2210.03629)) approach. | ||
|
||
Hence, the agent in each iteration produces one of the following outputs. | ||
|
||
For the sake of simplicity, imagine that the input prompt is "What is the current weather in Las Vegas?" | ||
|
||
First iteration: | ||
|
||
``` | ||
thought: I need to retrieve the current weather in Las Vegas. I can use the OpenMeteo function to get the current weather forecast for a location. | ||
tool_name: OpenMeteo | ||
tool_input: {"location": {"name": "Las Vegas"}, "start_date": "2024-10-17", "end_date": "2024-10-17", "temperature_unit": "celsius"} | ||
# Agent | ||
|
||
The `BaseAgent` class is the foundation of the Bee Framework, providing the core interface and functionality that all agent implementations must follow. It orchestrates the interaction between LLMs, tools, memory, and development utilities to create intelligent, automated workflows. | ||
|
||
## Overview | ||
|
||
`BaseAgent` acts as an abstract base class that defines the standard interface and basic functionality for all agents in the framework. It manages the lifecycle of agent operations, coordinates between different components, and provides a consistent interface for agent implementations. | ||
|
||
## Architecture | ||
|
||
```mermaid | ||
classDiagram | ||
class BaseAgent { | ||
+LLM llm | ||
+Memory memory | ||
+Tool[] tools | ||
+DevTools devTools | ||
+run(prompt: string, options?: ExecutionOptions) | ||
} | ||
BaseAgent *-- LLM | ||
BaseAgent *-- Memory | ||
BaseAgent *-- Tool | ||
BaseAgent *-- DevTools | ||
class LLM { | ||
+inference() | ||
+templates: TemplatePrompt | ||
} | ||
class Memory { | ||
+store() | ||
+retrieve() | ||
+cache: Cache | ||
} | ||
class Tool { | ||
+execute() | ||
+validate() | ||
} | ||
class DevTools { | ||
+emitter: Emitter | ||
+logger: Logger | ||
+adapter: Adapter | ||
+serializer: Serializer | ||
+errorHandler: ErrorHandler | ||
} | ||
``` | ||
|
||
> [!NOTE] | ||
> [!TIP] | ||
> | ||
> Agent emitted 3 complete updates in the following order (`thought`, `tool_name`, `tool_input`) and tons of partial updates in the same order. | ||
> Partial update means that new tokens are being added to the iteration. Updates are always in strict order: You first get many partial updates for thought, followed by a final update for thought (that means no final updates are coming for a given key). | ||
> Location within the framework `bee-agent-framework/agents`. | ||
Second iteration: | ||
## Core Properties | ||
|
||
``` | ||
thought: I have the current weather in Las Vegas in Celsius. | ||
final_answer: The current weather in Las Vegas is 20.5°C with an apparent temperature of 18.3°C. | ||
``` | ||
| Property | Type | Description | | ||
| ---------- | ---------- | -------------------------------------------- | | ||
| `llm` | `LLM` | Manages interactions with the language model | | ||
| `memory` | `Memory` | Handles state management and persistence | | ||
| `tools` | `Tool[]` | Array of available tools for the agent | | ||
| `devTools` | `DevTools` | Development and debugging utilities | | ||
|
||
For more complex tasks, the agent may do way more iterations. | ||
## Main Methods | ||
|
||
In the following example, we will transform the knowledge gained into code. | ||
### Public Methods | ||
|
||
```ts | ||
const response = await agent | ||
.run({ prompt: "What is the current weather in Las Vegas?" }) | ||
.observe((emitter) => { | ||
emitter.on("update", async ({ data, update, meta }) => { | ||
// to log only valid runs (no errors), check if meta.success === true | ||
console.log(`Agent Update (${update.key}) 🤖 : ${update.value}`); | ||
console.log("-> Iteration state", data); | ||
}); | ||
#### `run(prompt: string, options?: ExecutionOptions): Promise<AgentResponse>` | ||
|
||
emitter.on("partialUpdate", async ({ data, update, meta }) => { | ||
// to log only valid runs (no errors), check if meta.success === true | ||
console.log(`Agent Partial Update (${update.key}) 🤖 : ${update.value}`); | ||
console.log("-> Iteration state", data); | ||
}); | ||
Executes the agent with the given prompt and options. | ||
|
||
// you can observe other events such as "success" / "retry" / "error" / "toolStart" / "toolEnd", ... | ||
```typescript | ||
interface ExecutionOptions { | ||
signal?: AbortSignal; | ||
execution?: { | ||
maxRetriesPerStep?: number; | ||
totalMaxRetries?: number; | ||
maxIterations?: number; | ||
}; | ||
} | ||
|
||
// To see all events, uncomment the following code block | ||
// emitter.match("*.*", async (data: unknown, event) => { | ||
// const serializedData = JSON.stringify(data).substring(0, 128); // show only part of the event data | ||
// console.trace(`Received event "${event.path}"`, serializedData); | ||
// }); | ||
}); | ||
const response = await agent.run("What's the weather in Las Vegas?", { | ||
signal: AbortSignal.timeout(60000), | ||
execution: { | ||
maxIterations: 20, | ||
maxRetriesPerStep: 3, | ||
totalMaxRetries: 10, | ||
}, | ||
}); | ||
``` | ||
|
||
### Behaviour | ||
|
||
You can alter the agent's behavior in the following ways. | ||
#### `observe(callback: (emitter: Emitter) => void): void` | ||
|
||
#### Setting execution policy | ||
Subscribes to agent events for monitoring and debugging. | ||
|
||
```ts | ||
await agent.run( | ||
{ prompt: "What is the current weather in Las Vegas?" }, | ||
|
||
{ | ||
signal: AbortSignal.timeout(60 * 1000), // 1 minute timeout | ||
execution: { | ||
// How many times an agent may repeat the given step before it halts (tool call, llm call, ...) | ||
maxRetriesPerStep: 3, | ||
```typescript | ||
agent.observe((emitter) => { | ||
// Listen for complete updates | ||
emitter.on("update", ({ data, update, meta }) => { | ||
console.log(`Complete Update: ${update.key} = ${update.value}`); | ||
}); | ||
|
||
// How many retries can the agent occur before halting | ||
totalMaxRetries: 10, | ||
// Listen for partial updates (streaming) | ||
emitter.on("partialUpdate", ({ data, update, meta }) => { | ||
console.log(`Partial Update: ${update.key} = ${update.value}`); | ||
}); | ||
|
||
// Maximum number of iterations in which the agent must figure out the final answer | ||
maxIterations: 20, | ||
}, | ||
}, | ||
); | ||
// Listen for tool execution | ||
emitter.on("toolStart", ({ tool, input }) => { | ||
console.log(`Tool Started: ${tool.name}`); | ||
}); | ||
}); | ||
``` | ||
|
||
> [!NOTE] | ||
> | ||
> The default is zero retries and no timeout. | ||
##### Overriding prompt templates | ||
## Events | ||
|
||
The agent uses the following prompt templates. | ||
Agent emits various events through its DevTools.Emitter: | ||
|
||
1. **System Prompt** | ||
| Event | Description | Payload | | ||
| --------------- | -------------------------- | ------------------------- | | ||
| `update` | Complete update for a step | `{ data, update, meta }` | | ||
| `partialUpdate` | Streaming update | `{ data, update, meta }` | | ||
| `toolStart` | Tool execution started | `{ tool, input }` | | ||
| `toolEnd` | Tool execution completed | `{ tool, result }` | | ||
| `error` | Error occurred | `{ error, context }` | | ||
| `retry` | Retry attempt | `{ attempt, maxRetries }` | | ||
| `success` | Successful completion | `{ result }` | | ||
|
||
2. **User Prompt** (to reformat the user's prompt) | ||
## Implementation Example | ||
|
||
3. **User Empty Prompt** | ||
Here's an example of implementing an simple agent base in Bee Agent class: | ||
|
||
4. **Tool Error** | ||
```typescript | ||
import { BeeAgent } from "bee-agent-framework/agents/bee/agent"; | ||
import { TokenMemory } from "bee-agent-framework/memory/tokenMemory"; | ||
import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch"; | ||
import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat"; | ||
import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo"; | ||
|
||
5. **Tool Input Error** (validation error) | ||
const llm = new OllamaChatLLM(); | ||
const agent = new BeeAgent({ | ||
llm, | ||
memory: new TokenMemory({ llm }), | ||
tools: [new DuckDuckGoSearchTool(), new OpenMeteoTool()], | ||
}); | ||
|
||
6. **Tool No Result Error** | ||
|
||
7. **Tool Not Found Error** | ||
|
||
Please refer to the [following example](/examples/agents/bee_advanced.ts) to see how to modify them. | ||
|
||
## Creating your own agent | ||
const response = await agent | ||
.run({ prompt: "What's the current weather in Las Vegas?" }) | ||
.observe((emitter) => { | ||
emitter.on("update", async ({ data, update, meta }) => { | ||
console.log(`Agent (${update.key}) 🤖 : `, update.value); | ||
}); | ||
}); | ||
|
||
To create your own agent, you must implement the agent's base class (`BaseAgent`). | ||
console.log(`Agent 🤖 : `, response.result.text); | ||
``` | ||
|
||
The example can be found [here](/examples/agents/custom_agent.ts). | ||
## Best Practices | ||
|
||
1. **Error Handling** | ||
|
||
```typescript | ||
protected async executeIteration(iteration: number): Promise<IterationResult> { | ||
try { | ||
// ... iteration logic ... | ||
} catch (error) { | ||
this.devTools.emitter.emit('error', { error, context: { iteration } }); | ||
throw error; | ||
} | ||
} | ||
``` | ||
|
||
2. **Memory Management** | ||
|
||
```typescript | ||
protected async cleanup(): Promise<void> { | ||
await this.memory.store('lastCleanup', Date.now()); | ||
// Clear temporary data | ||
} | ||
``` | ||
|
||
3. **Event Emission** | ||
|
||
```typescript | ||
protected emitProgress(progress: number): void { | ||
this.devTools.emitter.emit('progress', { value: progress }); | ||
} | ||
``` | ||
|
||
4. **Tool Management** | ||
```typescript | ||
protected async validateTools(): Promise<void> { | ||
for (const tool of this.tools) { | ||
if (!await tool.validate()) { | ||
throw new Error(`Tool validation failed: ${tool.name}`); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Best Practices | ||
|
||
1. **Error Handling** | ||
|
||
- Use `AgentError` for agent-specific errors | ||
- Implement proper cleanup in `finally` blocks | ||
- Handle tool execution errors gracefully | ||
|
||
2. **State Management** | ||
|
||
- Use the `isRunning` flag to prevent concurrent executions | ||
- Implement proper state cleanup in the `destroy` method | ||
- Use snapshots for state persistence | ||
|
||
3. **Event Emission** | ||
|
||
- Configure appropriate event namespaces | ||
- Emit events for significant state changes | ||
- Include relevant metadata with events | ||
|
||
4. **Type Safety** | ||
- Leverage generic types for input/output typing | ||
- Define clear interfaces for options and metadata | ||
- Use type guards for runtime safety | ||
|
||
## See Also | ||
|
||
- [LLM Documentation](./llms.md) | ||
- [Memory System](./memory.md) | ||
- [Tools Guide](./tools.md) | ||
- [DevTools Reference](./dev-tools.md) | ||
- [Event System](./emiter.md) |
Oops, something went wrong.