diff --git a/content/en/docs/eino/Cookbook.md b/content/en/docs/eino/Cookbook.md index 0485c508545..65d0db60987 100644 --- a/content/en/docs/eino/Cookbook.md +++ b/content/en/docs/eino/Cookbook.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-16" +date: "2026-05-19" lastmod: "" tags: [] title: Cookbook @@ -63,6 +63,27 @@ This document serves as an example index for the eino-examples project, helping
| Directory | Name | Description |
| adk/agent/ralph-loop | Ralph Loop | Autonomous iteration mode: an outer forloop with Runner.Runimplements single-turn iteration. The Agent perceives prior work through the file system; a verification gate checks for BUG markers before accepting completion claims |
| Directory | Name | Description |
| adk/cancel/graceful-exit | Graceful Exit | Demonstrates Agent Cancel + Resume: captures terminal signals, then cancels nested Agents using CancelAfterChatModel+ WithRecursivemode, waits for a safe point to save Checkpoint, then resumes execution |
| Directory | Name | Description |
| adk/middlewares/skill | Skill Middleware | Loads Agent skills from the file system (e.g., log_analyzer), demonstrating skill middleware usage |
| quickstart/chat | Chat QuickStart | The most basic LLM conversation example, including template, generation, streaming output |
| quickstart/eino_assistant | Eino Assistant | Complete RAG application example, including knowledge indexing, Agent service, Web interface |
| quickstart/todoagent | Todo Agent | Simple Todo management Agent example |
| quickstart/chatwitheino | Chat with Eino (Tutorial) | 9-chapter progressive tutorial, from ChatModel → Runner → Session → Tool → Middleware → Callback → Interrupt → GraphTool → Skill, building a complete Agent step by step |
| Problem | Mechanism | Configuration Location |
| Invalid argument JSON | ToolArgumentsHandler | ToolsNodeConfig/ ToolsConfig |
| Calling non-existent tool | UnknownToolsHandler | ToolsNodeConfig/ ToolsConfig |
| Tool name/parameter name changes | ToolAliases | ToolsNodeConfig/ ToolsConfig |
| Tool execution error needs auto-correction | Middleware error conversion | ADK Handlersor ToolCallMiddlewares |
| markdown | README_zh.md | README.md |
| recursive | README_zh.md | README.md |
| semantic | README_zh.md | README.md |
@@ -46,19 +51,19 @@ Open `eino-examples/devops/debug/main.go` and run `main.go`. The plugin launches
-3) Enter 127.0.0.1:52538
+3. Enter 127.0.0.1:52538
|
-4) Confirm to enter the debug view, then select the Graph to debug
+4. Click confirm to enter the debug interface and select the Graph to debug
|
-1) Click “Test Run” to start from START
+1. Click "Test Run" to start execution from the start node
|
-2) Enter "hello eino" and confirm
+2. Enter "hello eino" and click confirm
|
| 3) Inspect per-node inputs/outputs | 4) Switch Input/Output views | -
![]() |
-![]() |
-
+
+
-### Orchestration Visualization
+### Orchestration Topology Visualization
-Supports Graph and Chain topology visualization.
+Supports visualization of Graph and Chain orchestration topologies.
-### Start from Any Node
+### Debug from Any Node
-### Inspect Node Results
+### View Node Execution Results
-Each node’s input, output, and execution time are shown in order.
+Each node's execution results are displayed in execution order in the debug area, including: input, output, and execution time.
@@ -110,7 +116,7 @@ Each node’s input, output, and execution time are shown in order.
### Orchestrate with Eino
-The plugin supports debugging Graph and Chain artifacts. Example registration:
+The plugin supports debugging Graph and Chain orchestration artifacts. Assume you already have orchestration code as follows:
```go
func RegisterSimpleGraph(ctx context.Context) {
@@ -140,24 +146,32 @@ func RegisterSimpleGraph(ctx context.Context) {
### Install Dependencies
+Run the following commands sequentially in the project directory:
+
```bash
+# 1. Pull latest devops repository
go get github.com/cloudwego/eino-ext/devops@latest
+
+# 2. Cleans and updates go.mod and go.sum
go mod tidy
```
-### Initialize Debugging
+### Call the Debug Initialization Function
-Because debugging starts an HTTP service in your main process to interact with the local plugin, you must call `Init()` from `github.com/cloudwego/eino-ext/devops` to start the debug service.
+Because debugging requires starting an HTTP service in the user's main process for interaction with the local debug plugin, you need to call `Init()` from _github.com/cloudwego/eino-ext/devops_ once to start the debug service.
> 💡
> Notes
>
-> 1. Ensure the target orchestration has run `Compile()` at least once.
-> 2. `devops.Init()` must run before calling `Compile()`.
-> 3. Make sure the main process stays alive after `devops.Init()`.
+> 1. Ensure the target orchestration artifact has executed `Compile()` at least once.
+> 2. `devops.Init()` must be executed before calling `Compile()`.
+> 3. You must ensure the main process does not exit after `devops.Init()` is executed.
+> 4. Starting from v0.1.9, the default listening address for the debug service changed from `0.0.0.0` to `127.0.0.1` (local connections only). For remote debugging, explicitly specify the listening IP via `WithDevServerIP`, e.g.: `devops.Init(ctx, devops.WithDevServerIP("0.0.0.0"))`.
+
+For example, add debug service startup code in the `main()` function:
```go
-// 1. Initialize debug service
+// 1. Call the debug service initialization function
err := devops.Init(ctx)
if err != nil {
logs.Errorf("[eino dev] init failed, err=%v", err)
@@ -170,9 +184,9 @@ RegisterSimpleGraph(ctx)
### Run Your Process
-Run your process locally or remotely, and ensure the main process does not exit.
+Run your process on your local machine or in a remote environment, and ensure the main process does not exit.
-In `github.com/cloudwego/eino-examples/devops/debug/main.go`, `main()` looks like:
+In github.com/cloudwego/eino-examples/devops/debug/main.go, the `main()` code is as follows:
```go
func main() {
@@ -199,102 +213,132 @@ func main() {
}
```
-### Configure Address
+### Configure Debug Address
+
+- **IP**: The IP address of the server where the user process is running.
+ - If the user process is running on your local machine, enter `127.0.0.1`;
+ - If the user process is running on a remote server, enter the remote server's IP address, supporting both IPv4 and IPv6.
+- **Port**: The port the debug service listens on, default is `52538`, configurable via the `WithDevServerPort` option method.
-- IP: `127.0.0.1` for local; remote server IP for remote (IPv4/IPv6).
-- Port: default `52538`, configurable via `WithDevServerPort`.
+> 💡
+> Notes
+>
+> - Local debugging: The system may show a network access warning — allow access.
+> - Remote server debugging: Ensure the port is accessible. Additionally, starting from v0.1.9, the default listening address is `127.0.0.1` only. For remote debugging, you must specify an IP accessible from the remote end (e.g., `0.0.0.0`) via `WithDevServerIP` when calling `devops.Init()`.
-Allow network prompts locally; ensure remote ports are reachable. Once connected, the status indicator turns green.
+After configuring the IP and Port, click confirm. The debug plugin will automatically connect to the target debug server. If the connection is successful, the connection status indicator will turn green.
-### Select an Artifact
+### Select Target Orchestration Artifact to Debug
-Ensure your target orchestration has been compiled at least once. Multiple `Compile()` runs register multiple artifacts; you’ll see them in the selection list.
+Ensure your target orchestration artifact has executed `Compile()` at least once. Since the debug system targets orchestration artifact instances, if `Compile()` is executed multiple times, multiple artifacts will be registered in the debug service, and you'll see multiple debug targets in the selection list.
### Start Debugging
-- From START: click “Test Run”, enter mock input (complex types are inferred), and confirm.
+Debugging supports starting from any node, including the START node and other intermediate nodes.
+
+- Debugging from the START node: Click "Test Run" directly, then enter mock input (if the input is a complex structure, the system will automatically infer the input structure), click confirm, and your graph will begin execution. Each node's results will be displayed below.
-- From a specific node: click the run button on that node.
+- Debugging from any operable node: For example, starting execution from the second node.
-## Advanced
+
+
+### View Execution Results
+
+When debugging from the START node, click Test Run and view the debug results below the plugin.
+
+
+
+When debugging from any operable node, view the debug results below the plugin.
+
+
+
+## Advanced Features
### Specify Implementation Type for Interface Fields
-Interface-typed fields render as `{}` by default. Type a space inside `{}` to select an implementation type. The plugin uses a special JSON structure:
+For interface-typed fields, they will be rendered as `{}` by default. Typing a space inside `{}` will bring up a list of interface implementation types. After selecting a type, the system will generate a special struct to express the interface information. The special struct is defined as follows:
-```json
+```go
{
- "_value": {}, // JSON value of the concrete type
- "_eino_go_type": "*model.MyConcreteType" // Go type name
+ "_value": {} // JSON value generated according to the concrete type
+ "_eino_go_type": "*model.MyConcreteType" // Go type name
}
```
> 💡
-> Common interface types like `string`, `schema.Message` are built-in. To register custom types, use `devops.AppendType` during `Init()`.
+> The system has built-in common interface types such as `string`, `schema.Message`, etc., which can be directly selected. If you need to register custom interface implementation types, use the `AppendType` method provided by `devops`.
-1) Suppose you have orchestration code where the graph input is `any`, and `node_1` takes `*NodeInfo`:
+1. Assume you already have orchestration code as follows, where the graph input is defined as `any` and `node_1`'s input is defined as `*NodeInfo`:
-```go
-type NodeInfo struct {
- Message string
-}
+ ```go
+ type NodeInfo struct {
+ Message string
+ }
-func RegisterGraphOfInterfaceType(ctx context.Context) {
- // Define a graph that input parameter is any.
- g := compose.NewGraph[any, string]()
+ func RegisterGraphOfInterfaceType(ctx context.Context) {
+ // Define a graph that input parameter is any.
+ g := compose.NewGraph[any, string]()
- _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input *NodeInfo) (output string, err error) {
- if input == nil {
- return "", nil
- }
- return input.Message + " process by node_1,", nil
- }))
+ _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input *NodeInfo) (output string, err error) {
+ if input == nil {
+ return "", nil
+ }
+ return input.Message + " process by node_1,", nil
+ }))
- _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
- return input + " process by node_2,", nil
- }))
+ _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_2,", nil
+ }))
- _ = g.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
- return input + " process by node_3,", nil
- }))
+ _ = g.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_3,", nil
+ }))
- _ = g.AddEdge(compose._START_, "node_1")
+ _ = g.AddEdge(compose._START_, "node_1")
- _ = g.AddEdge("node_1", "node_2")
+ _ = g.AddEdge("node_1", "node_2")
- _ = g.AddEdge("node_2", "node_3")
+ _ = g.AddEdge("node_2", "node_3")
- _ = g.AddEdge("node_3", compose._END_)
+ _ = g.AddEdge("node_3", compose._END_)
- r, err := g.Compile(ctx)
- if err != nil {
- logs.Errorf("compile graph failed, err=%v", err)
- return
- }
-}
-```
+ r, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("compile graph failed, err=%v", err)
+ return
+ }
+ }
+ ```
+2. Before debugging, register the custom `*NodeInfo` type via the `AppendType` method during `Init()`:
-2) Before debugging, register the custom `*NodeInfo` type with `AppendType` at `Init()`:
+ ```go
+ err := devops.Init(ctx, devops.AppendType(&graph.NodeInfo{}))
+ ```
+3. During debugging, in the Test Run JSON input box, interface-typed fields will appear as `{}` by default. You can type a space inside `{}` to view all built-in and custom-registered data types, and select the concrete implementation type for that interface.
-```go
-err := devops.Init(ctx, devops.AppendType(&graph.NodeInfo{}))
-```
+
+
+1. Fill in the debug node input in the `_value` field.
+
+
-3) During Test Run, interface fields show `{}` by default. Type a space inside `{}` to view all built-in and custom types, select the concrete implementation, then fill `_value`.
+1. Click confirm to view the debug results.
-### Debugging `map[string]any`
+
-If a node input is `map[string]any`:
+#### Debugging map[string]any
+
+Here we explain how to debug when the input type is map[string]any. If a node's input type is map[string]any, as shown below:
```go
func RegisterAnyInputGraph(ctx context.Context) {
@@ -341,17 +385,17 @@ func RegisterAnyInputGraph(ctx context.Context) {
}
```
-During debugging, in the Test Run JSON input box, use the following format to specify concrete types for values:
+During debugging, in the Test Run JSON input box, you need to enter content in the following format:
```json
{
- "name": {
- "_value": "alice",
- "_eino_go_type": "string"
- },
- "score": {
- "_value": "99",
- "_eino_go_type": "int"
- }
+ "name": {
+ "_value": "alice",
+ "_eino_go_type": "string"
+ },
+ "score": {
+ "_value": "99",
+ "_eino_go_type": "int"
+ }
}
```
diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md
index 7feea7b33e5..ea8ad812459 100644
--- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md
+++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md
@@ -1,28 +1,24 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
title: PatchToolCalls
-weight: 7
+weight: 8
---
adk/middlewares/patchtoolcalls
> 💡
-> The PatchToolCalls middleware is used to fix "dangling tool calls" issues in the message history. This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1).
+> The PatchToolCalls middleware is used to fix "dangling tool calls" issues in message history. Introduced in v0.8.0. Supports both `*schema.Message` and `*schema.AgenticMessage` message types.
## Overview
-In multi-turn conversation scenarios, there may be cases where an Assistant message contains ToolCalls, but the corresponding Tool message response is missing from the conversation history. Such "dangling tool calls" can cause some model APIs to throw errors or produce abnormal behavior.
-
-**Common scenarios:**
+In multi-turn conversation scenarios, there may be cases where an Assistant message contains ToolCalls, but the corresponding Tool response is missing from the conversation history. Such "dangling tool calls" can cause some model APIs to throw errors or produce abnormal behavior. **Common scenarios:**
- User sent a new message before tool execution completed, causing the tool call to be interrupted
- Some tool call results were lost when restoring a session
-- User canceled tool execution in a Human-in-the-loop scenario
-
-The PatchToolCalls middleware scans the message history before each model call and automatically inserts placeholder messages for tool calls that lack responses.
+- User canceled tool execution in a Human-in-the-loop scenario The PatchToolCalls middleware scans the message history before each model call (`BeforeModelRewriteState` hook) and automatically inserts placeholder messages for tool calls that lack responses.
## Quick Start
@@ -33,51 +29,67 @@ import (
"github.com/cloudwego/eino/adk/middlewares/patchtoolcalls"
)
-// Create middleware with default configuration
+// Use default configuration (cfg can be nil)
mw, err := patchtoolcalls.New(ctx, nil)
if err != nil {
// Handle error
}
-// Use with ChatModelAgent
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Model: yourChatModel,
Middlewares: []adk.ChatModelAgentMiddleware{mw},
})
```
-## Configuration Options
+## API Reference
+
+### Config
```go
type Config struct {
- // PatchedContentGenerator custom function to generate placeholder message content
- // Optional, uses default message if not set
PatchedContentGenerator func(ctx context.Context, toolName, toolCallID string) (string, error)
}
```
| Field | Type | Required | Description |
| PatchedContentGenerator | func(ctx, toolName, toolCallID string) (string, error) | No | Custom function to generate placeholder message content. Parameters include tool name and call ID, returns the content to fill in |
| PatchedContentGenerator | func(ctx context.Context, toolName, toolCallID string) (string, error) | No | Custom function to generate placeholder message content. Uses the built-in default message template when not set |
-
-**Processing Logic:**
-
-1. Executes in the `BeforeModelRewriteState` hook
-2. Iterates through all messages to find Assistant messages containing `ToolCalls`
-3. For each ToolCall, checks if a corresponding Tool message exists in subsequent messages (matched by `ToolCallID`)
-4. If no corresponding Tool message is found, inserts a placeholder message
-5. Returns the repaired message list
+> 💡
+> For `*schema.Message`, matching is done via `msg.Role == schema.Tool && msg.ToolCallID`; for `*schema.AgenticMessage`, matching is done via `ContentBlock.FunctionToolResult.CallID`.
-## Example Scenario
+### Example Scenario
-### Message History Before Repair
+**Before repair:**
```
-[User] "Help me check the weather"
-[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
-[Tool] "call_1: Sunny, 25°C"
-[User] "No need to check the location, just tell me Beijing's weather" <- User interrupts
+[User] "Help me check the weather"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: Sunny, 25°C"
+[User] "No need to check the location, just tell me Beijing's weather" <- User interrupts
```
-### Message History After Repair
+**After repair:**
```
-[User] "Help me check the weather"
-[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
-[Tool] "call_1: Sunny, 25°C"
-[Tool] "call_2: Tool call get_location (ID: call_2) was cancelled..." <- Automatically inserted
-[User] "No need to check the location, just tell me Beijing's weather"
+[User] "Help me check the weather"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: Sunny, 25°C"
+[Tool] "call_2: Tool call get_location (ID: call_2) was canceled..." <- Automatically inserted
+[User] "No need to check the location, just tell me Beijing's weather"
```
## Multi-language Support
-Placeholder messages support both Chinese and English, switch via `adk.SetLanguage()`:
+Placeholder messages support Chinese and English, switch via `adk.SetLanguage()`:
```go
import "github.com/cloudwego/eino/adk"
@@ -153,7 +172,8 @@ adk.SetLanguage(adk.LanguageEnglish) // English (default)
## Notes
> 💡
-> This middleware only modifies the history messages for the current run in the `BeforeModelRewriteState` hook, and does not affect the actual stored message history. The repair is temporary and only used for the current agent call.
+> The state returned by `BeforeModelRewriteState` is persisted to the agent's internal state by the framework (see the `ProcessState` call in `wrappers.go`). Therefore, placeholder messages inserted by PatchToolCalls **will be retained in subsequent iterations** and do not need to be re-patched every round.
-- It's recommended to place this middleware at the **front** of the middleware chain to ensure other middlewares process a complete message history
-- If your scenario requires persisting the repaired messages, implement the corresponding logic in `PatchedContentGenerator`
+- It is recommended to place this middleware at the **front** of the middleware chain to ensure other middlewares process a complete message history
+- The `cfg` parameter can be `nil`, equivalent to `&Config{}`
+- If the message list is empty (`len(state.Messages) == 0`), the middleware returns immediately without any processing
diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
index 740b3775891..e045e2a97d7 100644
--- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
+++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
@@ -1,33 +1,28 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
title: PlanTask
-weight: 4
+weight: 6
---
-# PlanTask Middleware
-
-adk/middlewares/plantask
-
> 💡
-> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1).
+> This middleware was introduced in v0.8.0. Package path: `github.com/cloudwego/eino/adk/middlewares/plantask`
## Overview
-`plantask` is a task management middleware that allows Agents to create and manage task lists. The middleware injects four tools through the `BeforeAgent` hook:
-
-- **TaskCreate**: Create a task
-- **TaskGet**: View task details
-- **TaskUpdate**: Update a task
-- **TaskList**: List all tasks
+`plantask` is a task management middleware that injects four tools into the Agent via the `BeforeAgent` hook, giving it structured task planning capabilities:
-Main purposes:
+| Tool | Function |
TaskCreate | Create a task |
TaskGet | Get details of a single task |
TaskUpdate | Update task status/fields, set dependencies, delete tasks |
TaskList | List summaries of all tasks |
| Status | Description |
pending | Pending (default) |
| Status Value | Description |
pending | Pending (default on creation) |
in_progress | In progress |
completed | Completed |
deleted | Deleted (will delete the file) |
deleted | Deleted (physically deletes the JSON file and removes from other tasks' dependency lists) |
| Parameter | Type | Required | Description |
subject | string | Yes | Title |
description | string | Yes | Description |
activeForm | string | No | Active form text, e.g., "Running tests" |
metadata | object | No | Custom data |
subject | string | Yes | Task title (imperative form) |
description | string | Yes | Detailed task description, including context and acceptance criteria |
activeForm | string | No | Active form text (e.g., "Running tests"), displayed to users when in_progress |
metadata | object | No | Custom key-value pairs |
| Parameter | Type | Required | Description |
taskId | string | Yes | Task ID |
taskId | string | Yes | Task ID (numeric string only) |
| Parameter | Type | Required | Description |
subject | string | No | New title |
description | string | No | New description |
activeForm | string | No | New active form text |
status | string | No | New status |
addBlocks | []string | No | Add blocked tasks |
addBlockedBy | []string | No | Add tasks blocking this one |
owner | string | No | Responsible agent |
metadata | object | No | Custom data (set to null to delete) |
status | string | No | New status, enum: pending/ in_progress/ completed/ deleted |
addBlocks | []string | No | Add task IDs that are blocked by the current task (bidirectional write) |
addBlockedBy | []string | No | Add task IDs that block the current task (bidirectional write) |
owner | string | No | Name of the responsible agent |
metadata | object | No | Merged into existing metadata; setting a key to null deletes that key |
| Mechanism | Description |
| ID Allocation | .highwatermarkfile stores the current maximum ID, incremented by 1 on creation |
| Concurrency Safety | All four tools share a single sync.Mutex, serializing execution within the same middleware instance |
| File Format | One {id}.json file per task, JSON serialized using sonic |
| Auto Cleanup | After TaskUpdate marks a task as completed, it checks — if all tasks are completed, batch deletes all files |
| ID Validation | Numeric-only regex ^\d+$ |
| Delete Cascade | When deleting a task, iterates all task files to remove references to that ID |
+
+1. **Discovery**: The Agent loads only the name and description of each available Skill, sufficient to determine when the Skill might be needed
+2. **Activation**: When a task matches a Skill, the Agent loads the full `SKILL.md` content into context
+3. **Execution**: The Agent follows the instructions to execute the task, loading additional files or running bundled code as needed
> 💡
> Ref: [https://agentskills.io/home](https://agentskills.io/home)
-# Interfaces
+# API Reference
## FrontMatter
-Skill metadata used for quick display during discovery, avoiding loading full content:
+The metadata structure of a Skill, parsed from the YAML frontmatter of SKILL.md. Used for quick Skill information display during the discovery phase:
```go
type FrontMatter struct {
@@ -48,11 +50,11 @@ type FrontMatter struct {
| Field | Type | Description |
Name | string | Unique identifier of a skill. The agent invokes the skill by name. Use short, meaningful names (e.g. pdf-processing, web-research). Corresponds to the namefield in SKILL.md frontmatter. |
Description | string | Description of what the skill does. This is the key basis for the agent to decide whether to use the skill, so it should clearly describe applicable scenarios and capabilities. Corresponds to the descriptionfield in SKILL.md frontmatter. |
Context | ContextMode | Context mode. Supported values: fork_with_context(copy history messages to a new agent for execution), fork(create a new agent with isolated context for execution). Empty means inline mode (return skill content directly). |
Agent | string | Agent name to use. Used with Context, resolved via AgentHub. Empty means using the default agent. |
Model | string | Model name to use. Resolved via ModelHub. In context mode, passed to the agent factory; in inline mode, it switches the model used by subsequent ChatModel calls. |
Name | string | Unique identifier of the Skill. Use short, meaningful names (e.g., pdf-processing, web-research) |
Description | string | Functional description of the Skill. Key basis for the Agent to decide whether to use this Skill; should clearly describe applicable scenarios and capabilities |
Context | ContextMode | Context mode. Possible values: fork(isolated context), fork_with_context(copy history messages). Empty means inline mode |
Agent | string | Specifies the Agent name to use, paired with Context, obtains the corresponding Agent via AgentHub. Empty uses the default Agent |
Model | string | Specifies the model name to use, obtains the corresponding model instance via ModelHub |
| Mode | Description |
| Inline (default) | Skill content is returned as the tool result and the current agent continues processing |
| ForkWithContext | Create a new agent, copy current conversation history, execute the skill independently, and return the result |
| Fork | Create a new agent with isolated context (only skill content), execute independently, and return the result |
| Inline (default) | Skill content is returned directly as a tool result, processed by the current Agent |
fork_with_context | Creates a new Agent, copies the current conversation history, independently executes the Skill task, then returns the result |
fork | Creates a new Agent with isolated context (only including Skill content), independently executes, then returns the result |
| Field | Type | Description |
FrontMatter | FrontMatter | Embedded metadata: Name, Description, Context, Agent, Model |
Content | string | The body of SKILL.md after frontmatter. Contains detailed instructions, workflows, examples, etc. The agent reads it after skill activation. |
BaseDirectory | string | Absolute path of the skill directory. The agent can use this path to access other resources in the skill directory (scripts, templates, references, etc.). |
FrontMatter | FrontMatter | Embedded metadata structure |
Content | string | The body content after the frontmatter in SKILL.md, including detailed instructions, workflows, examples, etc. |
BaseDirectory | string | Absolute path of the Skill directory, which the Agent can use to access other resource files in the directory |
| Method | Description |
List | List metadata of all available skills. Called when the agent starts to build the skill tool description, so the agent knows what skills exist. |
Get | Get full skill content by name. Called when the agent decides to use a skill, returning the full Skill structure including detailed instructions. |
List | Lists metadata of all available skills. Called when the Agent starts, used to build skill tool descriptions |
Get | Gets the complete skill content by name. Called when the Agent decides to use a specific skill |
| Field | Type | Required | Description |
| Backend | filesystem.Backend | Yes | Filesystem backend implementation used for file operations |
| BaseDir | string | Yes | Root directory for skills. It scans all first-level subdirectories and treats the ones containing SKILL.mdas skills. |
Backend | filesystem.Backend | Yes | Filesystem backend implementation for file operations |
BaseDir | string | Yes | Skills root directory path. Scans first-level subdirectories under this directory, looking for directories containing SKILL.mdfiles |
| Field | Type | Required | Default | Description |
Backend | Backend | Yes | Skill backend implementation responsible for storage and retrieval. You can use the built-in LocalBackendor provide your own. | |
SkillToolName | *string | No | "skill" | Name of the skill tool. Agents invoke skills via this tool name. If your agent already has a tool with the same name, set this to avoid conflicts. |
AgentHub | AgentHub | No | Provides agent factories. Required when a skill uses context: forkor context: isolate. | |
ModelHub | ModelHub | No | Provides model instances. Used when a skill specifies the modelfield. | |
CustomSystemPrompt | SystemPromptFunc | No | Built-in prompt | Custom system prompt function |
CustomToolDescription | ToolDescriptionFunc | No | Built-in description | Custom tool description function |
Backend | Backend | Yes | - | Skill backend implementation, responsible for skill storage and retrieval |
SkillToolName | *string | No | "skill" | Skill tool name. Can be customized to avoid conflicts if a tool with the same name already exists |
AgentHub | TypedAgentHub[M] | No | - | Provides Agent instances. Required when using context: forkor fork_with_context |
ModelHub | TypedModelHub[M] | No | - | Provides model instances. Passed to AgentHub in Context mode; in inline mode, switches the model for subsequent ChatModel calls via WrapModel |
CustomSystemPrompt | SystemPromptFunc | No | Built-in prompt | Custom system prompt. Signature: func(ctx, toolName) string |
CustomToolDescription | ToolDescriptionFunc | No | Built-in description | Custom tool description. Signature: func(ctx, skills []FrontMatter) string |
CustomToolParams | func | No | Only skillparameter | Custom tool parameter schema. Receives default parameters, returns custom parameters; always keeps skillas required |
BuildContent | func | No | Default formatting | Custom Skill content generation, can inject additional context into the content |
BuildForkMessages | func | No | See below | Custom initial messages passed to the sub-Agent in fork mode. Default: fork→ [UserMessage(content)], fork_with_context→ [history..., ToolMessage(content, callID)] |
FormatForkResult | func | No | Concatenate content | Custom sub-Agent result formatting. Default concatenates assistant message content and returns |
-
-> 💡
-> Skill middleware only provides the ability to load SKILL.md as shown above. If a skill requires the agent to read files, execute scripts, etc., users need to configure those capabilities for the agent separately.
diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
index 8895dc6f705..63fc73c77e3 100644
--- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
+++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
@@ -1,210 +1,343 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
title: Summarization
-weight: 3
+weight: 4
---
+> 💡
+> This middleware was introduced in v0.8.0. Package path: `github.com/cloudwego/eino/adk/middlewares/summarization`
+
## Overview
-The Summarization middleware automatically compresses conversation history when the token count exceeds a configured threshold. This helps maintain context continuity in long conversations while staying within the model's token limits.
+The Summarization middleware automatically calls a summarization model to compress conversation history when the conversation token count exceeds a threshold, keeping long conversations coherent within the model's context window. The middleware hooks into `BeforeModelRewriteState`, checking trigger conditions before each model call. When triggered, it executes: count → summary generation (with retry/failover) → post-processing → state replacement.
-> 💡
-> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1).
+## Generic System
-## Quick Start
+All core types and functions in this package provide both **Typed generic versions** (`M adk.MessageType`) and **non-generic aliases** (fixed to `*schema.Message`).
-```go
-import (
- "context"
- "github.com/cloudwego/eino/adk/middlewares/summarization"
-)
+| Generic Version | Non-generic Alias (= Typed\[*schema.Message\]) |
TypedConfig[M] | Config |
NewTyped[M](ctx, *TypedConfig[M]) | New(ctx, *Config) |
TypedTokenCounterFunc[M] | TokenCounterFunc |
TypedGenModelInputFunc[M] | GenModelInputFunc |
TypedGetFailoverModelFunc[M] | GetFailoverModelFunc |
TypedFinalizeFunc[M] | FinalizeFunc |
TypedCallbackFunc[M] | CallbackFunc |
TypedUserMessageFilterFunc[M] | UserMessageFilterFunc |
TypedPreserveUserMessages[M] | PreserveUserMessages |
TypedRetryConfig[M] | RetryConfig |
TypedFailoverConfig[M] | FailoverConfig |
TypedFailoverContext[M] | FailoverContext |
TypedFinalizerBuilder[M] | FinalizerBuilder |
| Field | Type | Required | Default | Description |
| Model | model.BaseChatModel | Yes | Chat model used for generating summaries | |
| ModelOptions | []model.Option | No | Options passed to the model when generating summaries | |
| TokenCounter | TokenCounterFunc | No | ~4 chars/token | Custom token counting function |
| Trigger | *TriggerCondition | No | 190,000 tokens | Condition to trigger summarization |
| Instruction | string | No | Built-in prompt | Custom summarization instruction |
| TranscriptFilePath | string | No | Full conversation transcript file path | |
| Prepare | PrepareFunc | No | Custom preprocessing function before summary generation | |
| Finalize | FinalizeFunc | No | Custom post-processing function for final messages | |
| Callback | CallbackFunc | No | Called after Finalize to observe state changes (read-only) | |
| EmitInternalEvents | bool | No | false | Whether to emit internal events |
| PreserveUserMessages | *PreserveUserMessages | No | Enabled: true | Whether to preserve original user messages in summary |
| Model | model.BaseModel[M] | Yes | — | Model used to generate summaries |
| ModelOptions | []model.Option | No | — | Options passed to the summarization model |
| TokenCounter | TypedTokenCounterFunc[M] | No | Uses the most recent assistant message's total\_tokens as baseline, with incremental messages estimated at ~4 chars/token | Custom token counting function |
| Trigger | *TriggerCondition | No | ContextTokens=160,000 | Conditions to trigger summarization |
| UserInstruction | string | No | Built-in prompt | Custom user-level summarization instruction, overrides the default instruction |
| TranscriptFilePath | string | No | — | Full conversation transcript file path, appended to the summary to remind the model of the original context location. Only takes effect when Finalize is not set |
| GenModelInput | TypedGenModelInputFunc[M] | No | sysInstruction → contextMsgs → userInstruction | Full control over building the summarization model input |
| Finalize | TypedFinalizeFunc[M] | No | Built-in post-processing | Custom summary post-processing. When set, the middleware no longer performs any default post-processing |
| Callback | TypedCallbackFunc[M] | No | — | Called after Finalize, with parameters before, after adk.TypedChatModelAgentState[M](value types), read-only |
| EmitInternalEvents | bool | No | false | Whether to emit internal events at key points |
| PreserveUserMessages | *TypedPreserveUserMessages[M] | No | Enabled: true | Preserve original user messages in the summary. Only takes effect when Finalize is not set |
| Retry | *TypedRetryConfig[M] | No | nil (no retry) | Retry strategy for primary model summary generation |
| Failover | *TypedFailoverConfig[M] | No | nil | Failover strategy after primary model failure |
+
+**Trigger Condition Check**: First checks `ContextMessages` (message count), then calculates token count via `TokenCounter` and compares against `ContextTokens`. Triggered if either condition is met.
+
+**Default Post-processing** (when Finalize is not set):
+
+1. Replace `| Event Type | Trigger Timing | Carried Data |
| ActionTypeBeforeSummary | Before generating summary | Original message list |
| ActionTypeAfterSummary | After completing summary | Final message list |
ActionTypeBeforeSummarize | After trigger condition is met, before calling the model | TypedBeforeSummarizeAction[M]{Messages}: original message list |
ActionTypeGenerateSummary | After each model generation attempt (including retries/failover) | TypedGenerateSummaryAction[M]{Attempt, Phase, ModelResponse, GetError()} |
ActionTypeAfterSummarize | After summarization completes, after Finalize | TypedAfterSummarizeAction[M]{Messages}: final message list |
| Field | Type | Description |
| Backend | Backend | Storage backend. Required when SkipTruncationis false; can be nil when only doing Clear without offload. |
| SkipTruncation | bool | Skip the truncation phase. |
| SkipClear | bool | Skip the clear phase. |
| ReadFileToolName | string | Tool name used to read offloaded content. Default "read_file". |
| RootDir | string | Root directory for saving content. Default "/tmp". Truncated content is stored at {RootDir}/trunc/{tool_call_id}, cleared content at {RootDir}/clear/{tool_call_id}. |
| GenTruncOffloadFilePath | func(ctx, *ToolDetail) (string, error) | Custom truncation file path generation. When set, RootDir does not apply to truncation. Useful when tool_call_id is not unique. |
| GenClearOffloadFilePath | func(ctx, *ToolDetail) (string, error) | Custom clear file path generation. When set, RootDir does not apply to clear. |
| MaxLengthForTrunc | int | Maximum character length to trigger truncation. Default 50000. |
| TruncExcludeTools | []string | List of tool names excluded from truncation. |
| TokenCounter | func(ctx, []M, []*schema.ToolInfo) (int64, error) | Token counting function. Default uses character count / 4 estimation. Recommended to replace with tiktoken-go/tokenizer. |
| MaxTokensForClear | int64 | Token threshold to trigger clear. Default 160000. |
| ClearRetentionSuffixLimit | int | Keep the most recent N rounds of assistant messages from being cleared. Default 1. |
| ClearAtLeastTokens | int64 | Minimum tokens that must be freed by clear. If not met, clear is not executed (to avoid needlessly breaking prompt cache). Default 0. |
| ClearExcludeTools | []string | List of tool names excluded from clear. |
| ClearMessageRewriter | func(ctx, M, []M) ([]M, error) | Message rewrite callback before clear. Parameters are toolCallMsg and the corresponding toolResponseMsgs. Can be used to rewrite write_file/edit_file calls into system-reminders. Returning nil removes that group of messages. |
| ClearPostProcess | func(ctx, *adk.TypedChatModelAgentState[M]) context.Context | Callback after clear completes, can save state or send notifications. Returns a potentially updated context. |
| ToolConfig | map[string]*ToolReductionConfig | Per-tool configuration, takes priority over global settings. |
| Parameter | Type | Required | Description |
regex_pattern | string | Yes | Regex pattern to match tool names |
query | string | Yes | Query string to find tools. Supports three modes: keyword search, select:direct selection, +keywordmust-match |
max_results | integer | No | Maximum number of results to return (default: 5). Only applies to keyword search mode; direct selection mode is not limited |
| Mode | Syntax | Description |
| Keyword search | "weather forecast" | Matches keywords in tool names and descriptions, sorted by relevance score. Supports camelCase and _/ __(MCP) separator splitting |
| Direct selection | "select:tool_a,tool_b" | Select one or more tools by exact name, comma-separated. Not limited by max_results |
| Must-match | "+slack send message" | Keywords prefixed with +are must-match items; tools without that keyword are filtered out. Other keywords are used for sorting |
| Matching Rule | Score |
| Tool name split part exactly matches keyword | 10 |
| Tool name split part contains keyword (substring) | 5 |
| Full tool name contains keyword | 3 |
| Tool description contains keyword | 2 |
+Default mode Removes all DynamicTools from state.ToolInfos, so the model initially can only see static tools and tool_search |
+Model native mode 1. Extract DynamicTools from state.ToolInfosinto state.DeferredToolInfos2. Remove tool_searchfrom state.ToolInfos(handled natively by the model) |
| Feature | AgentMiddleware (struct) | ChatModelAgentMiddleware (interface) |
| Extensibility | Closed, users cannot add new methods | Open, users can implement custom handlers |
| Context Propagation | Callbacks only return error | All methods return (context.Context, ..., error) |
| Configuration Management | Scattered in closures | Centralized in struct fields |
| AgentMiddleware field | ChatModelAgentMiddleware replacement |
AdditionalInstruction | Modify runCtx.Instructionin BeforeAgent |
AdditionalTools | Modify runCtx.Toolsin BeforeAgent |
BeforeChatModel | BeforeModelRewriteState |
AfterChatModel | AfterModelRewriteState |
WrapToolCall | WrapInvokableToolCall/ WrapStreamableToolCalletc. |
| Method | Input | Output | Scope |
BeforeAgent | Agent runtime env (*ChatModelAgentContext) | Modified Agent runtime env | Entire Run lifecycle, called only once |
BeforeModelRewriteState | Persistent state + Model runtime env | Modified persistent state | Persistent state across iterations (message list) |
WrapModel | ChatModel being wrapped + Model runtime env | Wrapped Model | Single Model request input, output and config |
AfterModelRewriteState | Persistent state (with response) + Model runtime env | Modified persistent state | Persistent state across iterations (message list) |
WrapInvokableToolCall | Tool being wrapped + Tool runtime env | Wrapped endpoint | Single Tool request input, output and config |
WrapStreamableToolCall | Tool being wrapped + Tool runtime env | Wrapped endpoint | Single Tool request input, output and config |
| Method | Function | Returns |
LsInfo | List files and directories under the specified path | []FileInfo |
Read | Read file content, supports line-based pagination (offset + limit) | *FileContent |
GrepRaw | Search for content matching a pattern within files | []GrepMatch |
GlobInfo | Find matching files by glob pattern | []FileInfo |
Write | Write or create a file | error |
Edit | Replace string content within a file | error |
| Type | Description | |
FileInfo | File/directory info: path, isDir, size, modified time | |
FileContent | File content with line number information | |
GrepMatch | Search match: content, path, line number | |
ReadRequest | Read request: path, offset (1-based line), limit (line count) | |
GrepRequest | Search request: pattern (regex), path, glob filter, file type filters, etc. | |
WriteRequest | Write request: path, content | |
EditRequest | Edit request: path, old string, new string, replace all | |
ExecuteRequest | Command request: command string, background flag | |
ExecuteResponse | Command result: stdout/stderr, exit code, truncated flag | |
| Type | Fields | Description |
LsInfoRequest | Path string | Directory path to list |
ReadRequest | FilePath string Offset int Limit int | File path; starting line number (1-based, <1 treated as 1); maximum number of lines to read (0=all) |
MultiModalReadRequest | Embeds ReadRequest Pages string | Inherits all ReadRequest fields; Pages specifies PDF page range (e.g. "1-5", "3") |
GrepRequest | Pattern string Path string Glob string FileType string CaseInsensitive bool EnableMultiline bool AfterLines int BeforeLines int | Regex search pattern (ripgrep syntax); search directory; glob file filter; file type filter (e.g. "go", "py"); case insensitive; enable multiline matching; show N lines after match; show N lines before match |
GlobInfoRequest | Pattern string Path string | Glob expression (supports *, **, ?, [abc]); starting directory for search |
WriteRequest | FilePath string Content string | Target file path; content to write |
EditRequest | FilePath string OldString string NewString string ReplaceAll bool | File path; exact string to replace (non-empty); replacement string; when false, requires OldString to appear exactly once in the file |
ExecuteRequest | Command string RunInBackendGround bool | Command string to execute; whether to run in background |
| Type | Fields | Description |
FileInfo | Path string IsDir bool Size int64 ModifiedAt string | File/directory path; whether it is a directory; file size (bytes); last modified time (ISO 8601 format) |
FileContent | Content string | Plain text content of the file |
MultiFileContent | *FileContent Parts []FileContentPart | Embeds FileContent; multi-modal output parts. Parts and FileContent are mutually exclusive: when Parts is non-empty, FileContent is ignored |
FileContentPart | Type FileContentPartType MIMEType string Data []byte | Content type ("image"or "pdf"); MIME type (e.g. "image/png"); raw binary data |
GrepMatch | Content string Path string Line int | Matched line content; file path; 1-based line number |
ExecuteResponse | Output string ExitCode *int Truncated bool | Command output; exit code (pointer, may be nil); whether output was truncated |
| Feature | InMemory | Local | Agentkit Sandbox |
| Execution model | In-memory | Local direct | Remote sandbox |
| Network dependency | No | No | Yes |
| Network dependency | None | None | Required |
| Configuration complexity | Zero config | Zero config | Credentials required |
| Persistence | No | Yes | Yes |
| Shell support | No | Yes (including streaming) | Yes |
| Use cases | Tests/temporary | Development/local | Multi-tenant/production |
| Shell support | No | Shell + StreamingShell | Shell |
| MultiModalReader | No | Implementation dependent | Implementation dependent |
| Use cases | Testing / temporary storage | Development / local environment | Multi-tenant / production |
| Feature | Agentkit | Local |
| Execution Model | Remote Sandbox | Local Direct |
| Network Dependency | Required | Not Required |
| Configuration Complexity | Requires Credentials | Zero Config |
| Security Model | Isolated Sandbox | OS Permissions |
| Use Cases | Multi-tenant/Production | Development/Local |
| Execution model | Remote sandbox | Local direct |
| Network dependency | Required | Not required |
| Configuration complexity | Credentials required | Zero config |
| Security model | Isolated sandbox | OS permissions |
| Use cases | Multi-tenant / production | Development / local environment |
| Method | Signature | Description |
LsInfo | (ctx, *LsInfoRequest) ([]FileInfo, error) | List directory contents |
Read | (ctx, *ReadRequest) (*FileContent, error) | Read file, supports line-level pagination (Offset 1-based, Limit 0=all) |
Write | (ctx, *WriteRequest) error | Write file; auto-creates parent directories; overwrites if file exists |
Edit | (ctx, *EditRequest) error | String replacement; supports ReplaceAll; errors if OldStringis not unique (non-ReplaceAll mode) |
GrepRaw | (ctx, *GrepRequest) ([]GrepMatch, error) | ripgrep-based search, supports full regex syntax; supports case-insensitive, multiline matching, context lines |
GlobInfo | (ctx, *GlobInfoRequest) ([]FileInfo, error) | Glob pattern file matching, supports */ **/ ?/ [abc] |
| Method | Signature | Description |
ExecuteStreaming | (ctx, *ExecuteRequest) (*StreamReader[*ExecuteResponse], error) | Streaming shell command execution with real-time output; supports background execution (RunInBackendGround) |
| Feature | Local | Agentkit |
| Execution model | Local direct | Remote sandbox |
| Network dependency | None | Required |
| Configuration complexity | Zero configuration | Requires credentials |
| Security model | OS permissions + ValidateCommand | Isolated sandbox |
| Streaming output | Supported (StreamingShell) | Not supported |
| Platform support | Unix/Linux/macOS | Any |
| Use case | Development/local environments | Multi-tenant/production environments |
| Method | Description |
| LsInfo | List directory contents |
| Read | Read file content (supports pagination, default 200 lines) |
| Write | Create new file (error if exists) |
| Edit | Replace file content |
| GrepRaw | Search file content (literal match) |
| GlobInfo | Find files by pattern |
| Execute | Execute shell commands |
| ExecuteStreaming | Execute commands with streaming output |
| Feature | Local | Agentkit |
| Execution Model | Local Direct | Remote Sandbox |
| Network Dependency | None | Required |
| Configuration Complexity | Zero Config | Requires Credentials |
| Security Model | OS Permissions | Isolated Sandbox |
| Streaming Output | Supported | Not Supported |
| Platform Support | Unix/Linux/macOS | Any |
| Use Cases | Development/Local | Multi-tenant/Production |
| Parameter | Type | Required | Default | Description |
Backend | Backend | Yes | - | File reading backend that performs the actual I/O |
AgentsMDFiles | []string | Yes | - | List of Agents.md file paths to load (at least one) |
AllAgentsMDMaxBytes | int | No | 0(unlimited) | Total byte limit for all files |
OnLoadWarning | func(string, error) | No | log.Printf | Callback for non-fatal errors |
Backend | Backend | Yes | — | File reading backend responsible for actual file I/O |
AgentsMDFiles | []string | Yes | — | List of Agents.md file paths to load (at least one), loaded and injected in order |
AllAgentsMDMaxBytes | int | No | 0(unlimited) | Total byte limit for all files; subsequent files are skipped when exceeded, but each file is always loaded in full |
OnLoadWarning | func(string, error) | No | log.Printf | Callback for non-fatal errors (missing files, circular @import, depth exceeded, etc.) |
| Rule | Description |
| Path resolution | Relative paths are resolved from the current file's directory; absolute paths are used directly |
| Maximum recursion depth | 5 levels (exceeding triggers OnLoadWarningand skips) |
| Circular reference detection | Paths already in the current ancestor chain are skipped (triggers OnLoadWarning) |
| Global deduplication | Each file path is only read and injected once during the entire load |
| Original text preserved | @import referenced files are appended as separate paragraphs; the @pathtext in the original is not removed |
| Byte budget | After cumulative bytes exceed AllAgentsMDMaxBytes, subsequent imports are skipped |
| Scenario | Behavior |
File not found (os.ErrNotExist) | Skip the file and trigger OnLoadWarning |
Cyclic @import | Skip the cyclic file and trigger OnLoadWarning |
@importdepth > 5 | Skip and trigger OnLoadWarning |
Total size exceeds AllAgentsMDMaxBytes | Skip remaining files and trigger OnLoadWarning(the first file is always loaded fully) |
| Permission denied / I/O error | Abort loading and return error |
| All file contents empty | Do not inject; pass through original messages |
File not found (os.ErrNotExist) | Skip the file, trigger OnLoadWarning |
| Circular @import | Skip the circular file, trigger OnLoadWarning |
| @import depth exceeds 5 levels | Skip, trigger OnLoadWarning |
Cumulative size exceeds AllAgentsMDMaxBytes | Skip subsequent files, trigger OnLoadWarning(first file is always loaded in full) |
| Permission denied / I/O error | Abort loading, return error |
| All file contents empty | No injection; pass through messages as-is |
| Function signature | Description |
New(ctx, *MiddlewareConfig) (ChatModelAgentMiddleware, error) | Recommended. Returns ChatModelAgentMiddleware, supports dynamically modifying Instruction and Tools via the BeforeAgenthook. |
NewTyped[M MessageType](ctx, *MiddlewareConfig) (TypedChatModelAgentMiddleware[M], error) | Generic version; type parameter Msupports *schema.Messageand *schema.AgenticMessage. Newis equivalent to NewTyped[*schema.Message]. |
| Field | Type | Description |
Backend | filesystem.Backend | Required. Provides filesystem operation capabilities, powering the ls, read\_file, write\_file, edit\_file, glob, grep tools (6 total). Interface defined in the github.com/cloudwego/eino/adk/filesystempackage. |
Shell | filesystem.Shell | Optional. Provides command execution capability; when set, registers the executetool. Mutually exclusive with StreamingShell. |
StreamingShell | filesystem.StreamingShell | Optional. Provides streaming command execution capability; when set, registers the streaming executetool. Mutually exclusive with Shell. |
UseMultiModalRead | bool | Optional, defaults to false. When enabled, the read_filetool becomes an EnhancedInvokableToolsupporting multi-modal content such as images/PDFs. Requires the Backend to also implement the filesystem.MultiModalReader interface. |
CustomSystemPrompt | *string | Optional. Overrides the system prompt appended to the Agent Instruction. If nil, no system prompt is appended. |
| Field | Corresponding tool |
LsToolConfig | ls |
ReadFileToolConfig | read\_file |
WriteFileToolConfig | write\_file |
EditFileToolConfig | edit\_file |
GlobToolConfig | glob |
GrepToolConfig | grep |
| Tool | Default name | Description | Condition |
| List directory | ls | List files and directories under the given path | Injected when Backend is not nil |
| Read file | read_file | Read file content, supports line-based pagination (offset + limit) | Injected when Backend is not nil |
| Write file | write_file | Create or overwrite a file | Injected when Backend is not nil |
| Edit file | edit_file | Replace strings in a file | Injected when Backend is not nil |
| Glob | glob | Find files by glob pattern | Injected when Backend is not nil |
| Search content | grep | Search file content by pattern, supports multiple output modes | Injected when Backend is not nil |
| Execute command | execute | Execute shell commands | Requires Shell or StreamingShell |
| Tool | Default name | Registration condition | Description |
| ls | ls | Backend ≠ nil | List files and subdirectories under a directory |
| read\_file | read_file | Backend ≠ nil | Read file content, supports offset/limit pagination. When UseMultiModalReadis enabled, can read images and PDFs |
| write\_file | write_file | Backend ≠ nil | Create or overwrite a file |
| edit\_file | edit_file | Backend ≠ nil | Exact string replacement editing, supports replace_all |
| glob | glob | Backend ≠ nil | Match file paths by glob pattern |
| grep | grep | Backend ≠ nil | Regex search of file content, supports multiple output modes and pagination |
| execute | execute | Shell ≠ nil or StreamingShell ≠ nil | Execute shell commands |
| Field | Description |
WithoutLargeToolResultOffloading bool | Set to trueto disable offloading; defaults to false(enabled) |
LargeToolResultOffloadingTokenLimit int | Token threshold, defaults to 20000 |
LargeToolResultOffloadingPathGen func(ctx, *compose.ToolInput) (string, error) | Offloading path generator function; defaults to /large_tool_result/{ToolCallID} |
| Call | Field values |
| Call method | Field values |
Agent.Run() | Inputis set, ResumeInfois nil |
Agent.Resume() | ResumeInfois set, Inputis nil |
| Agent type | GetType() return value |
| DeterministicTransfer Agent | "DeterministicTransfer" |
-Run1. Initialize callback context2. Handle input3. Call OnStart4. Execute agent logic5. Register OnEnd(when iterator is created) |
-Resume1. Build ResumeInfo2. Initialize callback context3. Call OnStart4. Resume agent execution5. Register OnEnd(when iterator is created) |
OnStart4. Execute Agent logic5. Register
OnEnd(when event stream is created)
OnStart4. Resume Agent execution5. Register
OnEnd(when event stream is created)
| Collaboration Method | Description |
| Transfer | Directly transfers the task to another Agent. The current Agent exits after execution completes, without concern for the task execution status of the transferred Agent |
| ToolCall (AgentAsTool) | Invokes the Agent as a ToolCall, waits for the Agent's response, and can obtain the output result of the called Agent for the next round of processing |
| Context Strategy | Description |
| Upstream Agent Full Conversation | Gets the complete conversation history of the current Agent's upstream Agent |
| Fresh Task Description | Ignores the complete conversation history of the upstream Agent and provides a completely new task summary as the AgentInput for the sub-Agent |
| Decision Autonomy | Description |
| Autonomous Decision | Within the Agent, based on its available downstream Agents, when assistance is needed, it autonomously selects a downstream Agent for assistance. Generally, the Agent makes decisions based on LLM internally, but even if selection is based on preset logic, it is still considered autonomous decision from the Agent's external perspective |
| Preset Decision | The next Agent after an Agent executes a task is predetermined. The execution order of Agents is predetermined and predictable |
-
-In Eino ADK, when building AgentInput for an Agent, the History it can see is "all AgentEvents produced before me".
-
-Worth mentioning is ParallelWorkflowAgent: two parallel sub-Agents (A, B) cannot see each other's AgentEvents during parallel execution because neither A nor B precedes the other.
-
-#### RunPath
-
-Each AgentEvent in History is "produced by a specific Agent in a specific execution sequence", meaning AgentEvent has its own RunPath. The purpose of RunPath is to convey this information; it doesn't carry other functions in the eino framework.
-
-The table below shows the specific RunPath when Agents execute under various orchestration modes:
-
-
-
-All three Agents are implemented using ChatModelAgent:
+- The parent Agent retains control and can continue reasoning based on sub-Agent results
+- The sub-Agent receives an independent task description and does not inherit the parent Agent's full conversation history
+- Multiple sub-Agents can be invoked in parallel
```go
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
"github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/components/tool"
)
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-type GetWeatherInput struct {
- City string `json:"city"`
-}
-
-func NewWeatherAgent() adk.Agent {
- weatherTool, err := utils.InferTool(
- "get_weather",
- "Gets the current weather for a specific city.",
- func(ctx context.Context, input *GetWeatherInput) (string, error) {
- return fmt.Sprintf(`the temperature in %s is 25°C`, input.City), nil
- },
- )
- if err != nil {
- log.Fatal(err)
- }
-
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "WeatherAgent",
- Description: "This agent can get the current weather for a given city.",
- Instruction: "Your sole purpose is to get the current weather for a given city by using the 'get_weather' tool. After calling the tool, report the result directly to the user.",
- Model: newChatModel(),
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{weatherTool},
- },
- },
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func NewChatAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ChatAgent",
- Description: "A general-purpose agent for handling conversational chat.",
- Instruction: "You are a friendly conversational assistant. Your role is to handle general chit-chat and answer questions that are not related to any specific tool-based tasks.",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func NewRouterAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "RouterAgent",
- Description: "A manual router that transfers tasks to other expert agents.",
- Instruction: `You are an intelligent task router. Your responsibility is to analyze the user's request and delegate it to the most appropriate expert agent.If no Agent can handle the task, simply inform the user it cannot be processed.`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-```
-
-Then use Eino ADK's Transfer capability to build a Multi-Agent and run it. ChatModelAgent implements the OnSubAgent interface. In the adk.SetSubAgents method, this interface is used to register parent/child Agents with ChatModelAgent, without requiring users to handle TransferAction generation:
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino/adk"
-)
-
-func main() {
- weatherAgent := NewWeatherAgent()
- chatAgent := NewChatAgent()
- routerAgent := NewRouterAgent()
-
- ctx := context.Background()
- a, err := adk.SetSubAgents(ctx, routerAgent, []adk.Agent{chatAgent, weatherAgent})
- if err != nil {
- log.Fatal(err)
- }
-
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: a,
- })
-
- // query weather
- println("\n\n>>>>>>>>>query weather<<<<<<<<<")
- iter := runner.Query(ctx, "What's the weather in Beijing?")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- if event.Action != nil {
- fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
- } else {
- fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
- }
- }
-
- // failed to route
- println("\n\n>>>>>>>>>failed to route<<<<<<<<<")
- iter = runner.Query(ctx, "Book me a flight from New York to London tomorrow.")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- if event.Action != nil {
- fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
- } else {
- fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
- }
- }
-}
-```
-
-Running result:
-
-```yaml
->>>>>>>>>query weather<<<<<<<<<
-Agent[RouterAgent]:
-assistant:
-tool_calls:
-{Index:| Option | Description |
WithFullChatHistoryAsInput() | Pass the parent Agent's full conversation history as the sub-Agent's input (by default only the model-generated request parameters are passed) |
WithAgentInputSchema(schema) | Custom input schema for the sub-Agent |
+### Event Stream Pass-through
-```go
-// github.com/cloudwego/eino/adk/prebuilt/supervisor.go
+When `ToolsConfig.EmitInternalEvents = true`, sub-Agent events are streamed through in real-time to the parent Agent's event stream, allowing end users to see the sub-Agent's intermediate process.
-type SupervisorConfig struct {
- Supervisor adk.Agent
- SubAgents []adk.Agent
-}
+> 💡
+> Pass-through events do not affect the parent Agent's state or checkpoint; they are only for user display. The sole exception is InterruptAction, which propagates across boundaries via CompositeInterrupt to support interrupt/resume.
-func NewSupervisor(ctx context.Context, conf *SupervisorConfig) (adk.Agent, error) {
- subAgents := make([]adk.Agent, 0, len(conf.SubAgents))
- supervisorName := conf.Supervisor.Name(ctx)
- for _, subAgent := range conf.SubAgents {
- subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{
- Agent: subAgent,
- ToAgentNames: []string{supervisorName},
- }))
- }
+### Pre-built Example: DeepAgents
- return adk.SetSubAgents(ctx, conf.Supervisor, subAgents)
-}
-```
+[DeepAgents](/docs/eino/core_modules/eino_adk/agent_implementation/deepagents) is a best-practice example of the AgentAsTool pattern: the main Agent delegates subtasks to sub-Agents via **TaskTool**, combined with **WriteTodos** for task planning and progress tracking.
## Workflow Agents
-WorkflowAgent supports running Agents according to workflows preset in code. Eino ADK provides three basic Workflow Agents: Sequential, Parallel, and Loop. They can be nested within each other to complete more complex tasks.
-
-By default, the input for each Agent in a Workflow is generated using the method described in the History section. You can customize the AgentInput generation method using WithHistoryRewriter.
-
-When an Agent produces an ExitAction Event, the Workflow Agent will immediately exit, regardless of whether there are other Agents that need to run afterward.
-
-For detailed explanations and use case references, see: [Eino ADK: Workflow Agents](/docs/eino/core_modules/eino_adk/agent_implementation/workflow)
-
-### SequentialAgent
-
-SequentialAgent executes a series of Agents in the order you provide:
-
-
-
-```go
-type SequentialAgentConfig struct {
- Name string
- Description string
- SubAgents []Agent
-}
-
-func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
-```
-
-### LoopAgent
-
-LoopAgent is implemented based on SequentialAgent. After SequentialAgent completes, it runs from the beginning again:
-
-
-
-```go
-type LoopAgentConfig struct {
- Name string
- Description string
- SubAgents []Agent
-
- MaxIterations int // Maximum number of loop iterations
-}
-
-func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
-```
-
-### ParallelAgent
-
-ParallelAgent runs multiple Agents concurrently:
+Deterministic orchestration for multi-step tasks with fixed flows:
-
+| Type | Description | Constructor |
| Sequential | Executes sub-Agents sequentially in array order | adk.NewSequentialAgent |
| Parallel | Executes all sub-Agents concurrently; completes when all finish | adk.NewParallelAgent |
| Loop | Loops through the sub-Agent sequence until BreakLoop or MaxIterations is exceeded | adk.NewLoopAgent |
-
-You can configure Tools for ChatModelAgent through ToolsConfig:
-
-```go
-// github.com/cloudwego/eino/adk/chatmodel.go
-
-type ToolsConfig struct {
- compose.ToolsNodeConfig
-
- // Names of the tools that will make agent return directly when the tool is called.
- // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
- ReturnDirectly map[string]bool
-
- // EmitInternalEvents indicates whether internal events from agentTool should be emitted
- // to the parent generator via a tool option injection at run-time.
- EmitInternalEvents bool
-}
-```
-
-ToolsConfig reuses Eino Graph ToolsNodeConfig, see [Eino: ToolsNode & Tool Usage Guide](/docs/eino/core_modules/components/tools_node_guide) for details. Additionally, it provides the ReturnDirectly configuration. ChatModelAgent will exit directly after calling a Tool configured in ReturnDirectly.
-
-## ChatModelAgent Configuration Fields
-
-> 💡
-> Note: GenModelInput by default renders the Instruction in F-String format using adk.GetSessionValues(). To disable this behavior, customize the GenModelInput method.
-
-```go
-type ChatModelAgentConfig struct {
- // Name of the agent. Better be unique across all agents.
- Name string
- // Description of the agent's capabilities.
- // Helps other agents determine whether to transfer tasks to this agent.
- Description string
- // Instruction used as the system prompt for this agent.
- // Optional. If empty, no system prompt will be used.
- // Supports f-string placeholders for session values in default GenModelInput, for example:
- // "You are a helpful assistant. The current time is {Time}. The current user is {User}."
- // These placeholders will be replaced with session values for "Time" and "User".
- Instruction string
-
- Model model.ToolCallingChatModel
-
- ToolsConfig ToolsConfig
-
- // GenModelInput transforms instructions and input messages into the model's input format.
- // Optional. Defaults to defaultGenModelInput which combines instruction and messages.
- GenModelInput GenModelInput
-
- // Exit defines the tool used to terminate the agent process.
- // Optional. If nil, no Exit Action will be generated.
- // You can use the provided 'ExitTool' implementation directly.
- Exit tool.BaseTool
-
- // OutputKey stores the agent's response in the session.
- // Optional. When set, stores output via AddSessionValue(ctx, outputKey, msg.Content).
- OutputKey string
-
- // MaxIterations defines the upper limit of ChatModel generation cycles.
- // The agent will terminate with an error if this limit is exceeded.
- // Optional. Defaults to 20.
- MaxIterations int
-
- // ModelRetryConfig configures retry behavior for the ChatModel.
- // When set, the agent will automatically retry failed ChatModel calls
- // based on the configured policy.
- // Optional. If nil, no retry will be performed.
- ModelRetryConfig *ModelRetryConfig
-}
-
-type ToolsConfig struct {
- compose.ToolsNodeConfig
-
- // Names of the tools that will make agent return directly when the tool is called.
- // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
- ReturnDirectly map[string]bool
-
- // EmitInternalEvents indicates whether internal events from agentTool should be emitted
- // to the parent generator via a tool option injection at run-time.
- EmitInternalEvents bool
-}
-
-type GenModelInput func(ctx context.Context, instruction string, input *AgentInput) ([]Message, error)
-```
-
-- `Name`: Agent name
-- `Description`: Agent description
-- `Instruction`: System Prompt when calling ChatModel, supports f-string rendering
-- `Model`: ChatModel used for running, must support tool calling
-- `ToolsConfig`: Tool configuration
- - ToolsConfig reuses Eino Graph ToolsNodeConfig, see [Eino: ToolsNode & Tool Usage Guide](/docs/eino/core_modules/components/tools_node_guide) for details.
- - ReturnDirectly: When ChatModelAgent calls a Tool configured in ReturnDirectly, it will immediately exit with the result, without returning to ChatModel per the react pattern. If multiple Tools are hit, only the first Tool is returned. Map key is the Tool name.
- - EmitInternalEvents: When using adk.AgentTool() to treat an Agent as a SubAgent through ToolCall, by default, this SubAgent will not send AgentEvents, only returning the final result as ToolResult.
-- `GenModelInput`: When the Agent is called, it uses this method to convert `Instruction` and `AgentInput` into Messages for calling ChatModel. The Agent provides a default GenModelInput method:
- 1. Add `Instruction` as `System Message` before `AgentInput.Messages`
- 2. Render `SessionValues` as variables into the message list from step 1
-
-> 💡
-> The default `GenModelInput` uses pyfmt rendering. Text in the message list is treated as a pyfmt template, meaning '{' and '}' in the text are treated as keywords. If you want to input these two characters directly, they need to be escaped as '{{' and '}}'.
-
-- `OutputKey`: When configured, the last Message produced by ChatModelAgent running will be set in `SessionValues` with `OutputKey` as the key
-- `MaxIterations`: Maximum number of ChatModel generations in react mode. Agent will exit with error when exceeded. Default value is 20
-- `Exit`: Exit is a special Tool. When the model calls this tool and executes it, ChatModelAgent will exit directly, with an effect similar to `ToolsConfig.ReturnDirectly`. ADK provides a default ExitTool implementation for users:
-
-```go
-type ExitTool struct{}
-
-func (et ExitTool) Info(_ context.Context) (*schema.ToolInfo, error) {
- return ToolInfoExit, nil
-}
-
-func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ ...tool.Option) (string, error) {
- type exitParams struct {
- FinalResult string `json:"final_result"`
- }
-
- params := &exitParams{}
- err := sonic.UnmarshalString(argumentsInJSON, params)
- if err != nil {
- return "", err
- }
-
- err = SendToolGenAction(ctx, "exit", NewExitAction())
- if err != nil {
- return "", err
- }
-
- return params.FinalResult, nil
-}
-```
-
-- `ModelRetryConfig`: When configured, various errors during ChatModel request (including direct errors and errors during streaming response) will be retried according to the configured policy. If an error occurs during streaming response, the streaming response will still be returned through AgentEvent immediately. If the error during streaming response will be retried according to the configured policy, consuming the message stream in AgentEvent will get `WillRetryError`. Users can handle this error for corresponding display processing. Example:
-
-```go
-iterator := agent.Run(ctx, input)
-for {
- event, ok := iterator.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- handleFinalError(event.Err)
- break
- }
-
- // Process streaming output
- if event.Output != nil && event.Output.MessageOutput.IsStreaming {
- stream := event.Output.MessageOutput.MessageStream
- for {
- msg, err := stream.Recv()
- if err == io.EOF {
- break // Stream completed successfully
- }
- if err != nil {
- // Check if this error will be retried (more streams coming)
- var willRetry *adk.WillRetryError
- if errors.As(err, &willRetry) {
- log.Printf("Attempt %d failed, retrying...", willRetry.RetryAttempt)
- break // Wait for next event with new stream
- }
- // Original error - won't retry, agent will stop and the next AgentEvent probably will be an error
- log.Printf("Final error (no retry): %v", err)
- break
- }
- // Display chunk to user
- displayChunk(msg)
- }
- }
-}
-```
-
-## ChatModelAgent Transfer
-
-`ChatModelAgent` supports converting other Agents' meta information into its own Tools, achieving dynamic Transfer through ChatModel judgment:
-
-- `ChatModelAgent` implements the `OnSubAgents` interface. After using `SetSubAgents` to set sub Agents for `ChatModelAgent`, `ChatModelAgent` will add a `Transfer Tool` and instruct ChatModel in the prompt to call this Tool when transfer is needed, using the transfer target AgentName as Tool input.
-
-```go
-const (
- TransferToAgentInstruction = `Available other agents: %s
-
-Decision rule:
-- If you're best suited for the question according to your description: ANSWER
-- If another agent is better according its description: CALL '%s' function with their agent name
-
-When transferring: OUTPUT ONLY THE FUNCTION CALL`
-)
-
-func genTransferToAgentInstruction(ctx context.Context, agents []Agent) string {
- var sb strings.Builder
- for _, agent := range agents {
- sb.WriteString(fmt.Sprintf("\n- Agent name: %s\n Agent description: %s",
- agent.Name(ctx), agent.Description(ctx)))
- }
-
- return fmt.Sprintf(TransferToAgentInstruction, sb.String(), TransferToAgentToolName)
-}
-```
-
-- `Transfer Tool` running sets a Transfer Event, specifying the jump to the target Agent, and ChatModelAgent exits after completion.
-- Agent Runner receives the Transfer Event and jumps to the target Agent for execution, completing the Transfer operation
-
-## ChatModelAgent AgentAsTool
-
-When the Agent being called doesn't need a complete running context but only clear and explicit input parameters to run correctly, the Agent can be converted to a Tool for `ChatModelAgent` to judge and call:
-
-- ADK provides utility methods to conveniently convert Eino ADK Agents to Tools for ChatModelAgent to call:
-
-```go
-// github.com/cloudwego/eino/adk/agent_tool.go
-
-func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
-```
-
-- Agents converted to Tools can be registered directly in ChatModelAgent through `ToolsConfig`
-
-```go
-bookRecommender := NewBookRecommendAgent()
-bookRecommendeTool := NewAgentTool(ctx, bookRecommender)
-
-a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- // ...
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{bookRecommendeTool},
- },
- },
-})
-```
-
-## ChatModelAgent Middleware
-
-`ChatModelAgentMiddleware` is an extension mechanism for `ChatModelAgent` that allows developers to inject custom logic at various stages of Agent execution:
-
-
-
-`ChatModelAgentMiddleware` is defined as an interface. Developers can implement this interface and configure it in `ChatModelAgentConfig` to make it effective in `ChatModelAgent`:
-
-```go
-type ChatModelAgentMiddleware interface {
- // ...
-}
-
-a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- // ...
- Handlers: []adk.ChatModelAgentMiddleware{
- &MyMiddleware{},
- },
-})
-```
-
-**Using BaseChatModelAgentMiddleware**
-
-`BaseChatModelAgentMiddleware` provides default empty implementations for all methods. By embedding it, you can override only the methods you need:
-
-```go
-type MyMiddleware struct {
- *adk.BaseChatModelAgentMiddleware
- // Custom fields
- logger *log.Logger
-}
-
-// Only override the methods you need
-func (m *MyMiddleware) BeforeModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- m.logger.Printf("Messages count: %d", len(state.Messages))
- return ctx, state, nil
-}
-```
-
-### BeforeAgent
-
-Called before each Agent run, can be used to modify instructions and tool configuration. ChatModelAgentContext defines the content that can be read and written in BeforeAgent:
-
-```go
-type ChatModelAgentContext struct {
- // Instruction is the current Agent's instruction
- Instruction string
- // Tools is the current configured original tool list
- Tools []tool.BaseTool
- // ReturnDirectly configures tool name sets that return directly after being called
- ReturnDirectly map[string]bool
-}
-
-type ChatModelAgentMiddleware interface {
- // ...
- BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error)
- // ...
-}
-```
-
-Example:
-
-```go
-func (m *MyMiddleware) BeforeAgent(
- ctx context.Context,
- runCtx *adk.ChatModelAgentContext,
-) (context.Context, *adk.ChatModelAgentContext, error) {
- // Copy runCtx to avoid modifying input
- nRunCtx := *runCtx
-
- // Modify instruction
- nRunCtx.Instruction += "\n\nPlease always reply in Chinese."
-
- // Add tool
- nRunCtx.Tools = append(runCtx.Tools, myCustomTool)
-
- // Set tool to return directly
- nRunCtx.ReturnDirectly["my_tool"] = true
-
- return ctx, &nRunCtx, nil
-}
-```
-
-### BeforeModelRewriteState / AfterModelRewriteState
-
-Called before/after each model call, can be used to inspect and modify message history. ModelContext defines read-only content, ChatModelAgentState defines read-write content:
-
-```go
-type ModelContext struct {
- // Tools contains the list of tools currently configured for the Agent
- // Populated at request time, contains tool info that will be sent to the model
- Tools []*schema.ToolInfo
-
- // ModelRetryConfig contains the retry configuration for the model
- // Populated from Agent's ModelRetryConfig
- ModelRetryConfig *ModelRetryConfig
-}
-
-type ChatModelAgentState struct {
- // Messages contains all messages in the current session
- Messages []Message
-}
-
-type ChatModelAgentMiddleware interface {
- BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
- AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
-}
-```
-
-Example:
-
-```go
-func (m *MyMiddleware) BeforeModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- // Copy state to avoid modifying input
- nState := *state
-
- // Check message history
- if len(state.Messages) > 50 {
- // Truncate old messages
- nState.Messages = state.Messages[len(state.Messages)-50:]
- }
- return ctx, &nState, nil
-}
-
-func (m *MyMiddleware) AfterModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- // Model response is the last message
- lastMsg := state.Messages[len(state.Messages)-1]
- m.logger.Printf("Model response: %s", lastMsg.Content)
- return ctx, state, nil
-}
-```
-
-### WrapModel
-
-Wraps model calls, can be used to intercept and modify model input and output:
-
-```go
-type ChatModelAgentMiddleware interface {
- WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error)
-}
-```
-
-Example:
-
-```go
-func (m *MyMiddleware) WrapModel(
- ctx context.Context,
- chatModel model.BaseChatModel,
- mc *adk.ModelContext,
-) (model.BaseChatModel, error) {
- return &loggingModel{
- inner: chatModel,
- logger: m.logger,
- }, nil
-}
-
-type loggingModel struct {
- inner model.BaseChatModel
- logger *log.Logger
-}
-
-func (m *loggingModel) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) {
- m.logger.Printf("Input messages: %d", len(msgs))
- resp, err := m.inner.Generate(ctx, msgs, opts...)
- m.logger.Printf("Output: %v, error: %v", resp != nil, err)
- return resp, err
-}
-
-func (m *loggingModel) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) {
- return m.inner.Stream(ctx, msgs, opts...)
-}
-```
-
-### WrapInvokableToolCall / WrapStreamableToolCall
-
-Wraps tool calls, can be used to intercept and modify tool input and output:
-
-```go
-// InvokableToolCallEndpoint is the function signature for tool calls.
-// Middleware developers add custom logic around this Endpoint.
-type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error)
-
-// StreamableToolCallEndpoint is the function signature for streaming tool calls.
-// Middleware developers add custom logic around this Endpoint.
-type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error)
-
-type ToolContext struct {
- // Name indicates the name of the tool being called
- Name string
- // CallID indicates the ToolCallID of this tool call
- CallID string
-}
-
-type ChatModelAgentMiddleware interface {
- WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error)
- WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error)
-}
-```
-
-Example:
-
-```go
-func (m *MyMiddleware) WrapInvokableToolCall(
- ctx context.Context,
- endpoint adk.InvokableToolCallEndpoint,
- tCtx *adk.ToolContext,
-) (adk.InvokableToolCallEndpoint, error) {
- return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
- m.logger.Printf("Calling tool: %s (ID: %s)", tCtx.Name, tCtx.CallID)
- start := time.Now()
-
- result, err := endpoint(ctx, argumentsInJSON, opts...)
-
- m.logger.Printf("Tool %s completed in %v", tCtx.Name, time.Since(start))
- return result, err
- }, nil
-}
-```
-
-# ChatModelAgent Usage Example
-
-## Scenario Description
-
-Create a book recommendation Agent that can recommend relevant books based on user input.
-
-## Code Implementation
-
-### Step 1: Define Tools
-
-The book recommendation Agent needs a `book_search` tool that can search for books based on user requirements (genre, rating, etc.).
-
-Using utility methods provided by Eino makes it easy to create (see [How to create a tool?](/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool)):
-
-```go
-import (
- "context"
- "log"
-
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
-)
-
-type BookSearchInput struct {
- Genre string `json:"genre" jsonschema:"description=Preferred book genre,enum=fiction,enum=sci-fi,enum=mystery,enum=biography,enum=business"`
- MaxPages int `json:"max_pages" jsonschema:"description=Maximum page length (0 for no limit)"`
- MinRating int `json:"min_rating" jsonschema:"description=Minimum user rating (0-5 scale)"`
-}
-
-type BookSearchOutput struct {
- Books []string
-}
-
-func NewBookRecommender() tool.InvokableTool {
- bookSearchTool, err := utils.InferTool("search_book", "Search books based on user preferences", func(ctx context.Context, input *BookSearchInput) (output *BookSearchOutput, err error) {
- // search code
- // ...
- return &BookSearchOutput{Books: []string{"God's blessing on this wonderful world!"}}, nil
- })
- if err != nil {
- log.Fatalf("failed to create search book tool: %v", err)
- }
- return bookSearchTool
-}
-```
-
-### Step 2: Create ChatModel
-
-Eino provides various ChatModel wrappers (such as openai, gemini, doubao, etc., see [Eino: ChatModel Usage Guide](/docs/eino/core_modules/components/chat_model_guide) for details). Here we use openai ChatModel as an example:
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/components/model"
-)
-
-func NewChatModel() model.ToolCallingChatModel {
- ctx := context.Background()
- apiKey := os.Getenv("OPENAI_API_KEY")
- openaiModel := os.Getenv("OPENAI_MODEL")
-
- cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: apiKey,
- Model: openaiModel,
- })
- if err != nil {
- log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
- }
- return cm
-}
-```
-
-### Step 3: Create ChatModelAgent
-
-In addition to configuring ChatModel and tools, you need to configure Name and Description describing the Agent's function and purpose, as well as the Instruction that instructs the ChatModel. The Instruction will ultimately be passed to ChatModel as a system message.
-
-```go
-import (
- "context"
- "fmt"
- "log"
-
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/compose"
-)
-
-func NewBookRecommendAgent() adk.Agent {
- ctx := context.Background()
-
- a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- Name: "BookRecommender",
- Description: "An agent that can recommend books",
- Instruction: `You are an expert book recommender. Based on the user's request, use the "search_book" tool to find relevant books. Finally, present the results to the user.`,
- Model: NewChatModel(),
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{NewBookRecommender()},
- },
- },
- })
- if err != nil {
- log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
- }
-
- return a
-}
-```
-
-###
-
-### Step 4: Run via Runner
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino/adk"
-
- "github.com/cloudwego/eino-examples/adk/intro/chatmodel/internal"
-)
-
-func main() {
- ctx := context.Background()
- a := internal.NewBookRecommendAgent()
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: a,
- })
- iter := runner.Query(ctx, "recommend a fiction book to me")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- msg, err := event.Output.MessageOutput.GetMessage()
- if err != nil {
- log.Fatal(err)
- }
- fmt.Printf("\nmessage:\n%v\n======", msg)
- }
-}
-```
-
-## Running Result
-
-```yaml
-message:
-assistant:
-tool_calls:
-{Index:| Field | Description |
Name | Agent name. Required when used as an AgentTool |
Description | Agent capability description. Required when used as an AgentTool |
Instruction | System Prompt. Supports {Key} placeholders; default GenModelInputrenders with SessionValues |
Model | Required. model.BaseModel[M]type; must support model.WithToolswhen using Tools |
ToolsConfig | Tool configuration, see below for details |
GenModelInput | Custom input transformation. Default uses Instruction as System Message + f-string rendering |
MaxIterations | Maximum ReAct loop iterations; exits with error when exceeded. Default 20 |
Handlers | Interface-based Middleware (TypedChatModelAgentMiddleware[M]), recommended |
Middlewares | Struct-based Middleware (AgentMiddleware), legacy compatibility |
ModelRetryConfig | Retry strategy for failed model calls |
ModelFailoverConfig | Switches to a backup model on model call failure. Requires configuring GetFailoverModeland ShouldFailover |
| Hook | Timing | Modifiable Content |
BeforeAgent | Before Agent runs (once only) | Instruction, Tools, ReturnDirectly, ToolSearchTool |
AfterAgent | After Agent succeeds | Read final state (no modification) |
BeforeModelRewriteState | Before each model call | Messages, ToolInfos, DeferredToolInfos (persisted to state) |
AfterModelRewriteState | After each model call | Messages (including model response), ToolInfos (persisted to state) |
WrapModel | Wraps model call | Retry, failover, event sending (do not modify Messages) |
WrapToolCall | Wraps tool call | Permission checks, logging, output rewriting |
| Scenario | Behavior |
Stream()initialization failure | Same as Generate, directly triggers failover evaluation |
| Mid-stream error | Received chunks are concatenated into LastOutputMessageand passed to ShouldFailover; after deciding to failover, closes the current stream and restarts with the new model |
| Client impact | Events already sent during the failed attempt are not retracted. Clients should reset partial results or deduplicate by metadata when receiving a new stream round |
+---
-The diagram above shows the core components of DeepAgents and their relationships:
+## Complete Config Definition
-- Main Agent: The entry point and commander of the system, receives initial tasks, calls tools in ReAct mode to complete tasks and is responsible for presenting the final results.
-- ChatModel (ToolCallingChatModel): Usually a large language model with tool-calling capabilities, responsible for understanding tasks, reasoning, selecting and calling tools.
-- Tools: A collection of capabilities available to MainAgent, including:
- - WriteTodos: Built-in planning tool for decomposing complex tasks into structured todo lists.
- - TaskTool: A special tool that serves as the unified entry point for calling sub-agents.
- - BuiltinTools, CustomTools: General tools built into DeepAgents and various tools customized by users according to business needs.
-- SubAgents: Responsible for executing specific, independent subtasks, with context isolated from MainAgent.
- - GeneralPurpose: A general-purpose sub-agent with the same tools as MainAgent (except TaskTool), used to execute subtasks in a "clean" context.
- - CustomSubAgents: Various sub-agents customized by users according to business needs.
+```go
+type Config = TypedConfig[*schema.Message]
-### Built-in Capabilities
+type TypedConfig[M adk.MessageType] struct {
+ Name string // Agent identifier name
+ Description string // Purpose description
+ ChatModel model.BaseModel[M] // Required; must support model.WithTools
+ Instruction string // System prompt; uses built-in default prompt when empty
-#### Filesystem
+ // Sub-Agents (bound to TaskTool)
+ SubAgents []adk.TypedAgent[M]
-> 💡
-> Currently in alpha state
+ // Custom tools
+ ToolsConfig adk.ToolsConfig
+ MaxIteration int // Maximum reasoning iteration count
-When creating DeepAgents, configure the relevant Backend, and DeepAgents will automatically load the corresponding tools:
+ // File system (choose one or combine)
+ Backend filesystem.Backend // Registers ls/read_file/write_file/edit_file/glob/grep
+ Shell filesystem.Shell // Registers execute (mutually exclusive with StreamingShell)
+ StreamingShell filesystem.StreamingShell // Registers execute (streaming, mutually exclusive with Shell)
-```
-type Config struct {
- // ...
- Backend filesystem.Backend
- Shell filesystem.Shell
- StreamingShell filesystem.StreamingShell
- // ...
-}
-```
+ // Built-in feature toggles
+ WithoutWriteTodos bool // true disables write_todos tool
+ WithoutGeneralSubAgent bool // true disables the default general-purpose sub-Agent
-| Configuration | Function | Added Tools |
| Backend | Provides file system access capability, optional | read_file, write_file, edit_file, glob, grep |
| Shell | Provides Shell capability, optional, mutually exclusive with StreamShell | execute |
| StreamingShell | Provides Shell capability with streaming results, optional, mutually exclusive with Shell | execute(streaming) |
+### Constructors
-1. The model receives user input.
-2. The model calls the WriteTodos tool with a task list generated according to the WriteTodos Description. This tool call is added to the context for future reference.
-3. The model calls TaskTool according to the todos in the context to complete the first todo.
-4. Calls WriteTodos again to update the Todos execution progress.
+```go
+// Standard version (M = *schema.Message)
+func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error)
-> 💡
-> For simple tasks, calling WriteTodos every time may have a negative effect. The WriteTodos Description includes some common positive and negative examples to avoid not calling or over-calling WriteTodos. When using DeepAgents, you can add more prompts according to actual business scenarios to make WriteTodos called at appropriate times.
+// Generic version (supports *schema.AgenticMessage)
+func NewTyped[M adk.MessageType](ctx context.Context, cfg *TypedConfig[M]) (adk.TypedResumableAgent[M], error)
+```
> 💡
-> WriteTodos will be added to the Agent by default. Configure `WithoutWriteTodos=true` to disable WriteTodos.
+> Returns ResumableAgent (includes Resume method), which can be used with Runner's checkpoint/resume mechanism.
-### Task Delegation and SubAgents Invocation
-
-**TaskTool**
+---
-All sub-agents are bound to TaskTool. When the main agent assigns subtasks to sub-agents for processing, it calls TaskTool and specifies which sub-agent is needed and the task to execute. TaskTool then routes the task to the specified sub-agent and returns the result to the main agent after execution. The default Description of TaskTool explains the general rules for calling sub-agents and concatenates the Description of each sub-agent. Developers can customize the Description of TaskTool by configuring `TaskToolDescriptionGenerator`.
+## Architecture
-> When users configure Config.SubAgents, these Agents will be bound to TaskTool based on ChatModelAgent's AgentAsTool capability
+
-**Context Isolation**
+- **Main Agent**: System entry point, completes tasks by calling tools in a ReAct manner
+- **ChatModel** (`model.BaseModel[M]`): Responsible for reasoning and tool selection
+- **Tools**:
+ - `write_todos`: Built-in planning tool that decomposes tasks into structured TODO lists
+ - `task`: Sub-Agent invocation entry (routing parameters: `subagent_type`, `description`)
+ - Built-in tools (file system/shell) + user-defined tools (`ToolsConfig`)
+- **SubAgents**: Context-isolated, independently execute sub-tasks
+ - `general-purpose`: Default sub-Agent with the same tools (except task) and configuration as the main Agent
+ - Custom sub-Agents (`Config.SubAgents`)
-Context isolation between Agents:
+---
-- Information Transfer: The main agent and sub-agents do not share context. Sub-agents only receive the subtask goals assigned by the main agent, not the entire task processing; the main agent only receives the processing results from sub-agents, not the processing of sub-agents.
-- Avoid Pollution: This isolation ensures that the execution process of sub-agents (such as numerous tool calls and intermediate steps) does not "pollute" the main agent's context. The main agent only receives concise, clear final answers.
+## Built-in File System
-**general-purpose**
+| Config Field | Registered Tools | Description |
Backend | ls, read_file, write_file, edit_file, glob, grep | File system operations |
Shell | execute | Non-streaming command execution, mutually exclusive with StreamingShell |
StreamingShell | execute (streaming) | Streaming command execution, mutually exclusive with Shell |
- - Advantages: DeepAgents provides Plan/RePlan as tools for the main agent to freely call, allowing unnecessary planning to be skipped during tasks, overall reducing model calls and lowering latency and costs.
- - Disadvantages: Task planning and delegation are completed in one model call, requiring higher model capabilities, and prompt tuning is relatively more difficult.
+The `write_todos` tool writes a structured TODO list to the session (key: `deep_agent_session_key_todos`) for reference in subsequent reasoning.
-## DeepAgents Usage Example
+**TODO Structure**:
-### Scenario Description
+```go
+type TODO struct {
+ Content string `json:"content"`
+ ActiveForm string `json:"activeForm"`
+ Status string `json:"status"` // "pending" | "in_progress" | "completed"
+}
+```
-Excel Agent is an "intelligent assistant that understands Excel". It first breaks down the problem into steps, then executes and verifies results step by step. It can understand user questions and uploaded file content, propose feasible solutions, and select appropriate tools (system commands, generate and run Python code, web queries, etc.) to complete tasks.
+**Workflow**:
-In real business, you can think of Excel Agent as an "Excel expert + automation engineer". When you provide a raw spreadsheet and target description, it will propose a solution and complete the execution:
+1. Model receives user input
+2. Calls `write_todos` to decompose tasks and write to context
+3. Executes TODOs one by one (calling task or tools directly)
+4. Calls `write_todos` again to update progress
-- **Data Cleaning and Formatting**: Complete deduplication, null value handling, and date format standardization from an Excel file containing large amounts of data.
-- **Data Analysis and Report Generation**: Extract monthly sales totals from sales data, aggregate statistics, pivot, and finally generate and export chart reports.
-- **Automated Budget Calculation**: Automatically calculate total budget based on budget applications from different departments and generate department budget allocation tables.
-- **Data Matching and Merging**: Match and merge customer information tables from multiple different sources to generate a complete customer information database.
+> 💡
+> For simple tasks, calling write_todos every time may be counterproductive. The built-in prompt already includes positive and negative examples guiding when to use it. You can further tune this through a custom Instruction. Setting WithoutWriteTodos=true completely disables it.
-The structure of Excel Agent built with DeepAgents is as follows:
+---
-
+## Sub-Agent Delegation: task Tool
-1. Add ReadFile tool to the main agent, allowing the main agent to view file content and assist in subtask formulation
-2. Add Code and WebSearch sub-agents: Code can write python code to operate excel spreadsheets; WebSearch can search for information and summarize.
+**TaskTool** is the unified invocation entry for all sub-Agents:
-### Code Implementation
+- Parameters: `subagent_type` (target sub-Agent name), `description` (task description)
+- Internally wraps each sub-Agent as a tool via `adk.NewTypedAgentTool`
+- Default description includes names and descriptions of all available sub-Agents; customizable via `TaskToolDescriptionGenerator`
-[https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
+**Context Isolation**:
-### Execution Results
+- Sub-Agents only receive the task description assigned by the main Agent, not shared conversation history
+- The main Agent only receives the sub-Agent's final result; intermediate steps are not passed back
+- Prevents large volumes of tool calls and intermediate reasoning from "polluting" the main Agent context
-```yaml
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool name: task
-arguments: {"subagent_type":"CodeAgent","description":"Please help me with a CSV file operation task. Specific requirements: 1. Read the questions.csv file in the current directory; 2. Extract the first column data from the CSV file; 3. Save the extracted first column data to a new CSV file named first_column.csv. Please use the pandas library to complete these operations, ensuring the code can execute correctly and handle possible file path issues."}
+**general-purpose Sub-Agent**:
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool name: python_runner
-arguments: {"code":"```python\nimport pandas as pd\nimport os\n\n# Get current working directory and build file path\nbase_dir = os.getcwd()\ninput_path = os.path.join(base_dir, 'questions.csv')\noutput_path = os.path.join(base_dir, 'first_column.csv')\n\ntry:\n # Read CSV file\n df = pd.read_csv(input_path)\n # Extract first column data\n first_column = df.iloc[:, 0]\n # Save as new CSV file (without index)\n first_column.to_csv(output_path, index=False)\n print(f\"Successfully saved first column data to: {output_path}\")\nexcept FileNotFoundError:\n print(f\"Error: File not found {input_path}\")\nexcept Exception as e:\n print(f\"Error during processing: {str(e)}\")\n```"}
+- Created by default, has the same tools (except task), Instruction, and ModelFailoverConfig as the main Agent
+- Used to execute general tasks in an isolated context when no specialized sub-Agent exists
+- Setting `WithoutGeneralSubAgent=true` disables it
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool response: Successfully saved first column data to: /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv
+---
+## Comparison with Other Approaches
-name: ExcelAgent
-path: [{ExcelAgent}]
-answer: Task completed. Successfully read the `questions.csv` file in the current directory, extracted the first column data, and saved the result to `first_column.csv`. The specific output path is:
+| Dimension | DeepAgents vs ReAct | DeepAgents vs Plan-and-Execute |
| Advantages | Built-in planning + sub-Agent context isolation, better for multi-step tasks | Plan/RePlan called as tools on demand, reducing unnecessary planning overhead |
| Disadvantages | Planning + sub-Agent calls increase model requests, latency, and token costs | Planning and delegation completed in a single call, higher requirements on model capability |
-The code handles path concatenation and exception catching (such as file not found or format errors) to ensure execution stability.
+- Main Agent configured with ReadFile tool to assist task formulation
+- Added Code (Python for Excel operations) and WebSearch as two sub-Agents
-name: ExcelAgent
-path: [{ExcelAgent}]
-answer: Successfully extracted the first column data from the `questions.csv` spreadsheet to a new file `first_column.csv`, saved at:
+### Code
-`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+Complete example: [https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
-The process handled path concatenation and exception catching (such as file not found, format errors, etc.) to ensure data extraction completeness and file generation stability. If you need to adjust the file path or have further requirements for data format, please let me know.
+```go
+agent, err := deep.New(ctx, &deep.Config{
+ Name: "ExcelAgent",
+ ChatModel: myModel,
+ Backend: localBackend,
+ SubAgents: []adk.Agent{codeAgent, webSearchAgent},
+ ToolsConfig: adk.ToolsConfig{
+ InvokableTools: []tool.InvokableTool{readFileTool},
+ },
+})
```
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
index 76988341bc5..f04d755f941 100644
--- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
@@ -1,17 +1,17 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
-title: 'Eino ADK: Plan-Execute Agent'
-weight: 4
+title: Plan-Execute Agent
+weight: 2
---
## Plan-Execute Agent Overview
### Import Path
-`import github.com/cloudwego/eino/adk/prebuilt/planexecute`
+`import ``github.com/cloudwego/eino/adk/prebuilt/planexecute`
### What is Plan-Execute Agent?
@@ -275,7 +275,7 @@ func newPlanExecuteAgent(ctx context.Context) adk.Agent {
replanner := newReplanner(ctx, model)
// Combine into PlanExecuteAgent (fixed execute-replan max iterations 10)
- planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{
+ planExecuteAgent, err := planexecute.New(ctx, &planexecute.PlanExecuteConfig{
Planner: planner,
Executor: executor,
Replanner: replanner,
@@ -346,7 +346,7 @@ func main() {
{"steps":["Identify the most recent and credible sources for AI developments in healthcare in 2024, such as scientific journals, industry reports, news articles, and expert analyses.","Extract and compile the key technologies emerging or advancing in AI for healthcare in 2024, including machine learning models, diagnostic tools, robotic surgery, personalized medicine, and data management solutions.","Analyze the main applications of AI in healthcare during 2024, focusing on areas such as diagnostics, patient care, drug discovery, medical imaging, and healthcare administration.","Investigate current industry trends related to AI in healthcare for 2024, including adoption rates, regulatory changes, ethical considerations, funding landscape, and market forecasts.","Synthesize the gathered information into a comprehensive summary covering the latest developments in AI for healthcare in 2024, highlighting key technologies, applications, and industry trends with examples and implications."]}
2025/09/08 11:47:47
=== Agent:Executor Output ===
-{"message":"Found 10 results successfully.","results":[{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.researchgate.net/publication/389402322_Artificial_Intelligence_in_Healthcare_2024_Year_in_Review","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in..."},{"title":"AI in Healthcare - Nature","url":"https://www.nature.com/collections/hacjaaeafj","summary":"\"AI in Healthcare\" encompasses the use of AI technologies to enhance various aspects of healthcare delivery, from diagnostics to treatment personalization, ultimately aiming to improve..."},...]}
+{"message":"Found 10 results successfully.","results":[{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.researchgate.net/publication/389402322_Artificial_Intelligence_in_Healthcare_2024_Year_in_Review","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in..."},{"title":"AI in Healthcare - Nature","url":"https://www.nature.com/collections/hacjaaeafj","summary":"\"AI in Healthcare\" encompasses the use of AI technologies to enhance various aspects of healthcare delivery, from diagnostics to treatment personalization, ultimately aiming to improve..."},{"title":"Evolution of artificial intelligence in healthcare: a 30-year ...","url":"https://www.frontiersin.org/journals/medicine/articles/10.3389/fmed.2024.1505692/full","summary":"Conclusion: This study reveals a sustained explosive growth trend in AI technologies within the healthcare sector in recent years, with increasingly profound applications in medicine. Additionally, medical artificial intelligence research is dynamically evolving with the advent of new technologies."},{"title":"The Impact of Artificial Intelligence on Healthcare: A Comprehensive ...","url":"https://onlinelibrary.wiley.com/doi/full/10.1002/hsr2.70312","summary":"This review analyzes the impact of AI on healthcare using data from the Web of Science (2014-2024), focusing on keywords like AI, ML, and healthcare applications."},{"title":"Artificial intelligence in healthcare (Review) - PubMed","url":"https://pubmed.ncbi.nlm.nih.gov/39583770/","summary":"Furthermore, the barriers and constraints that may impede the use of AI in healthcare are outlined, and the potential future directions of AI-augmented healthcare systems are discussed."},{"title":"Full article: Towards new frontiers of healthcare systems research ...","url":"https://www.tandfonline.com/doi/full/10.1080/2047..."},...]}
...
```
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
deleted file mode 100644
index bca088eb1cf..00000000000
--- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
+++ /dev/null
@@ -1,359 +0,0 @@
----
-Description: ""
-date: "2026-03-02"
-lastmod: ""
-tags: []
-title: 'Eino ADK: Supervisor Agent'
-weight: 3
----
-
-## Supervisor Agent Overview
-
-### Import Path
-
-`import github.com/cloudwego/eino/adk/prebuilt/supervisor`
-
-### What is Supervisor Agent?
-
-Supervisor Agent is a centralized multi-agent collaboration pattern consisting of one Supervisor Agent and multiple SubAgents. The Supervisor is responsible for task allocation, monitoring the execution process of sub-agents, and summarizing results and making decisions after sub-agents complete; sub-agents focus on executing specific tasks and automatically transfer task control back to the Supervisor via WithDeterministicTransferTo after completion.
-
-
-
-This pattern is suitable for scenarios that require dynamic coordination of multiple specialized agents to complete complex tasks, such as:
-
-- Research project management (Supervisor assigns research, experiment, report writing tasks to different sub-agents).
-- Customer service processes (Supervisor assigns tasks to technical support, after-sales, sales sub-agents based on user question types).
-
-### Supervisor Agent Structure
-
-The core structure of Supervisor pattern is as follows:
-
-- **Supervisor Agent**: As the collaboration core, has task allocation logic (such as rule-based or LLM decision), can include sub-agents under management via `SetSubAgents`.
-- **SubAgents**: Each sub-agent is enhanced with WithDeterministicTransferTo, with `ToAgentNames` preset to the Supervisor name, ensuring automatic transfer back to Supervisor after task completion.
-
-### Supervisor Agent Features
-
-1. **Deterministic Callback**: After sub-agent execution completes (not interrupted), WithDeterministicTransferTo automatically triggers Transfer event, transferring task control back to Supervisor, avoiding collaboration flow interruption.
-2. **Centralized Control**: Supervisor uniformly manages sub-agents, can dynamically adjust task allocation based on sub-agent execution results (such as assigning to other sub-agents or directly generating final results).
-3. **Loosely Coupled Extension**: Sub-agents can be independently developed, tested, and replaced; just ensure they implement the Agent interface and bind to Supervisor to join the collaboration flow.
-4. **Support for Interrupt and Resume**: If sub-agent or Supervisor supports `ResumableAgent` interface, collaboration flow can resume after interruption, maintaining task context continuity.
-
-### Supervisor Agent Execution Flow
-
-The typical collaboration flow of Supervisor pattern is as follows:
-
-1. **Task Start**: Runner triggers Supervisor to run, inputs initial task (e.g., "Complete a report on LLM development history").
-2. **Task Allocation**: Supervisor transfers task to designated sub-agent (e.g., "Research Agent") via Transfer event based on task requirements.
-3. **Sub-Agent Execution**: Sub-agent executes specific task (e.g., researches LLM key milestones) and generates execution result events.
-4. **Automatic Callback**: After sub-agent completes, WithDeterministicTransferTo triggers Transfer event, transferring task back to Supervisor.
-5. **Result Processing**: Supervisor receives sub-agent results, decides next step (e.g., assign to "Report Writing Agent" to continue processing, or directly output final result).
-
-## Supervisor Agent Usage Example
-
-### Scenario Description
-
-Create a research report generation system:
-
-- **Supervisor**: Based on user input research topic, assigns tasks to "Research Agent" and "Writer Agent", and summarizes final report.
-- **Research Agent**: Responsible for generating research plan (e.g., key stages of LLM development).
-- **Writer Agent**: Responsible for writing complete report based on research plan.
-
-### Code Implementation
-
-#### Step 1: Implement Sub-Agents
-
-First create two sub-agents, responsible for research and writing tasks respectively:
-
-```go
-// Research Agent: Generates research plan
-func NewResearchAgent(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ResearchAgent",
- Description: "Generates a detailed research plan for a given topic.",
- Instruction: `
-You are a research planner. Given a topic, output a step-by-step research plan with key stages and milestones.
-Output ONLY the plan, no extra text.`,
- Model: model,
- })
- return agent
-}
-
-// Writer Agent: Writes report based on research plan
-func NewWriterAgent(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "WriterAgent",
- Description: "Writes a report based on a research plan.",
- Instruction: `
-You are an academic writer. Given a research plan, expand it into a structured report with details and analysis.
-Output ONLY the report, no extra text.`,
- Model: model,
- })
- return agent
-}
-```
-
-#### Step 2: Implement Supervisor Agent
-
-Create Supervisor Agent, define task allocation logic (simplified here as rule-based: first assign to Research Agent, then assign to Writer Agent):
-
-```go
-// Supervisor Agent: Coordinates research and writing tasks
-func NewReportSupervisor(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ReportSupervisor",
- Description: "Coordinates research and writing to generate a report.",
- Instruction: `
-You are a project supervisor. Your task is to coordinate two sub-agents:
-- ResearchAgent: generates a research plan.
-- WriterAgent: writes a report based on the plan.
-
-Workflow:
-1. When receiving a topic, first transfer the task to ResearchAgent.
-2. After ResearchAgent finishes, transfer the task to WriterAgent with the plan as input.
-3. After WriterAgent finishes, output the final report.`,
- Model: model,
- })
- return agent
-}
-```
-
-#### Step 3: Combine Supervisor and Sub-Agents
-
-Use `NewSupervisor` to combine Supervisor and sub-agents:
-
-```go
-import (
- "context"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/adk/prebuilt/supervisor"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-func main() {
- ctx := context.Background()
-
- // 1. Create LLM model (e.g., GPT-4o)
- model, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: "YOUR_API_KEY",
- Model: "gpt-4o",
- })
-
- // 2. Create sub-agents and Supervisor
- researchAgent := NewResearchAgent(model)
- writerAgent := NewWriterAgent(model)
- reportSupervisor := NewReportSupervisor(model)
-
- // 3. Combine Supervisor and sub-agents
- supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{
- Supervisor: reportSupervisor,
- SubAgents: []adk.Agent{researchAgent, writerAgent},
- })
-
- // 4. Run Supervisor pattern
- iter := supervisorAgent.Run(ctx, &adk.AgentInput{
- Messages: []adk.Message{
- schema.UserMessage("Write a report on the history of Large Language Models."),
- },
- EnableStreaming: true,
- })
-
- // 5. Consume event stream (print results)
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Output != nil && event.Output.MessageOutput != nil {
- msg, _ := event.Output.MessageOutput.GetMessage()
- println("Agent[" + event.AgentName + "]:\n" + msg.Content + "\n===========")
- }
- }
-}
-```
-
-### Execution Results
-
-```markdown
-Agent[ReportSupervisor]:
-
-===========
-Agent[ReportSupervisor]:
-successfully transferred to agent [ResearchAgent]
-===========
-Agent[ResearchAgent]:
-1. **Scope Definition & Background Research**
- - Task: Define "Large Language Model" (LLM) for the report (e.g., size thresholds, key characteristics: transformer-based, large-scale pretraining, general-purpose).
- - Task: Identify foundational NLP/AI concepts pre-LLMs (statistical models, early neural networks, word embeddings) to contextualize origins.
- - Milestone: 3-day literature review of academic definitions, industry reports, and AI historiographies to finalize scope.
-
-2. **Chronological Periodization**
- - Task: Divide LLM history into distinct eras (e.g., Pre-2017: Pre-transformer foundations; 2017-2020: Transformer revolution & early LLMs; 2020-Present: Scaling & mainstream adoption).
- ...
-
-Agent[ResearchAgent]:
-successfully transferred to agent [ReportSupervisor]
-===========
-Agent[ReportSupervisor]:
-successfully transferred to agent [WriterAgent]
-===========
-Agent[WriterAgent]:
-# The History of Large Language Models: From Foundations to Mainstream Revolution
-
-## Abstract
-Large Language Models (LLMs) represent one of the most transformative technological innovations of the 21st century...
-
-## 1. Introduction: Defining Large Language Models
-A **Large Language Model (LLM)** is a type of machine learning model designed to process and generate human language...
-
-...
-
-## 7. Conclusion: A Revolution in Five Years
-The history of LLMs is a story of exponential progress: from the transformer's 2017 invention to ChatGPT's 2022 viral explosion...
-
-## References
-- Devlin, J., et al. (2018). *BERT: Pre-training of deep bidirectional transformers for language understanding*. NAACL.
-...
-===========
-Agent[WriterAgent]:
-successfully transferred to agent [ReportSupervisor]
-===========
-```
-
-## WithDeterministicTransferTo
-
-### What is WithDeterministicTransferTo?
-
-`WithDeterministicTransferTo` is an Agent enhancement tool provided by Eino ADK, used to inject task transfer capability into Agents. It allows developers to preset fixed task transfer paths for target Agents. When the Agent completes its task (not interrupted), it automatically generates a Transfer event to transfer the task flow to the preset target Agent.
-
-This capability is the foundation for building the Supervisor Agent collaboration pattern, ensuring sub-agents can reliably transfer task control back to the Supervisor after execution, forming a "allocate-execute-feedback" closed-loop collaboration flow.
-
-### WithDeterministicTransferTo Core Implementation
-
-#### Configuration Structure
-
-Define core task transfer parameters through `DeterministicTransferConfig`:
-
-```go
-// Wrapper method
-func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent
-
-// Configuration details
-type DeterministicTransferConfig struct {
- Agent Agent // Target Agent to be enhanced
- ToAgentNames []string // List of target Agent names to transfer to after task completion
-}
-```
-
-- `Agent`: The original Agent that needs transfer capability added.
-- `ToAgentNames`: When `Agent` completes task and is not interrupted, automatically transfers task to target Agent name list (transfers in order).
-
-#### Agent Wrapping
-
-WithDeterministicTransferTo wraps the original Agent. Based on whether it implements the `ResumableAgent` interface (supports interrupt and resume), it returns `agentWithDeterministicTransferTo` or `resumableAgentWithDeterministicTransferTo` instance respectively, ensuring enhanced capability is compatible with Agent's original functions (such as `Resume` method).
-
-The wrapped Agent overrides the `Run` method (for `ResumableAgent`, also overrides `Resume` method), appending Transfer events to the original Agent's event stream:
-
-```go
-// Wrapper for regular Agent
-type agentWithDeterministicTransferTo struct {
- agent Agent // Original Agent
- toAgentNames []string // Target Agent name list
-}
-
-// Run method: Executes original Agent task, appends Transfer event after task completion
-func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] {
- aIter := a.agent.Run(ctx, input, options...)
-
- iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
-
- // Asynchronously process original event stream and append Transfer event
- go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
-
- return iterator
-}
-```
-
-For `ResumableAgent`, additionally implements `Resume` method, ensuring deterministic transfer still triggers after resume execution:
-
-```go
-type resumableAgentWithDeterministicTransferTo struct {
- agent ResumableAgent // Original Agent supporting resume
- toAgentNames []string // Target Agent name list
-}
-
-// Resume method: Resumes execution of original Agent task, appends Transfer event after completion
-func (a *resumableAgentWithDeterministicTransferTo) Resume(ctx context.Context, info *ResumeInfo, opts ...AgentRunOption) *AsyncIterator[*AgentEvent] {
- aIter := a.agent.Resume(ctx, info, opts...)
- iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
- go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
- return iterator
-}
-```
-
-#### Event Stream Append Transfer Event
-
-`appendTransferAction` is the core logic implementing deterministic transfer. It consumes the original Agent's event stream and automatically generates and sends Transfer events to target Agents after the Agent task ends normally (not interrupted):
-
-```go
-func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent], generator *AsyncGenerator[*AgentEvent], toAgentNames []string) {
- defer func() {
- // Exception handling: Capture panic and pass error via event
- if panicErr := recover(); panicErr != nil {
- generator.Send(&AgentEvent{Err: safe.NewPanicErr(panicErr, debug.Stack())})
- }
- generator.Close() // Event stream ends, close generator
- }()
-
- interrupted := false
-
- // 1. Forward all events from original Agent
- for {
- event, ok := aIter.Next()
- if !ok { // Original event stream ended
- break
- }
- generator.Send(event) // Forward event to caller
-
- // Check if interruption occurred (e.g., InterruptAction)
- if event.Action != nil && event.Action.Interrupted != nil {
- interrupted = true
- } else {
- interrupted = false
- }
- }
-
- // 2. If not interrupted and target Agent exists, generate Transfer event
- if !interrupted && len(toAgentNames) > 0 {
- for _, toAgentName := range toAgentNames {
- // Generate transfer message (system prompt + Transfer action)
- aMsg, tMsg := GenTransferMessages(ctx, toAgentName)
- // Send system prompt event (notify user of task transfer)
- aEvent := EventFromMessage(aMsg, nil, schema.Assistant, "")
- generator.Send(aEvent)
- // Send Transfer action event (trigger task transfer)
- tEvent := EventFromMessage(tMsg, nil, schema.Tool, tMsg.ToolName)
- tEvent.Action = &AgentAction{
- TransferToAgent: &TransferToAgentAction{
- DestAgentName: toAgentName, // Target Agent name
- },
- }
- generator.Send(tEvent)
- }
- }
-}
-```
-
-**Key Logic**:
-
-- **Event Forwarding**: All events generated by the original Agent (such as thinking, tool calls, output results) are fully forwarded, ensuring business logic is unaffected.
-- **Interruption Check**: If Agent is interrupted during execution (e.g., `InterruptAction`), Transfer is not triggered (interruption is considered task not completed normally).
-- **Transfer Event Generation**: After task ends normally, two events are generated for each `ToAgentNames`:
- 1. System prompt event (`schema.Assistant` role): Notifies user that task will be transferred to target Agent.
- 2. Transfer action event (`schema.Tool` role): Carries `TransferToAgentAction`, triggers ADK runtime to transfer task to the Agent corresponding to `DestAgentName`.
-
-## Summary
-
-WithDeterministicTransferTo provides reliable task transfer capability for Agents, which is the core foundation for building Supervisor pattern; Supervisor pattern achieves efficient collaboration between multiple Agents through centralized coordination and deterministic callbacks, significantly reducing development and maintenance costs for complex tasks. By combining both, developers can quickly build flexible, scalable multi-Agent systems.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
deleted file mode 100644
index 8581e2d329c..00000000000
--- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
+++ /dev/null
@@ -1,1265 +0,0 @@
----
-Description: ""
-date: "2026-01-20"
-lastmod: ""
-tags: []
-title: 'Eino ADK: Workflow Agents'
-weight: 2
----
-
-# Workflow Agents Overview
-
-## Import Path
-
-`import "github.com/cloudwego/eino/adk"`
-
-## What Are Workflow Agents
-
-Workflow Agents are a special type of Agent in Eino ADK that allows developers to organize and execute multiple sub-agents in a predefined flow.
-
-Unlike the Transfer pattern based on LLM autonomous decision-making, Workflow Agents use **predefined decisions**, running sub-agents according to the execution flow defined in code, providing a more predictable and controllable multi-agent collaboration approach.
-
-Eino ADK provides three basic Workflow Agent types:
-
-- **SequentialAgent**: Executes sub-agents sequentially in order
-- **LoopAgent**: Loops through a sequence of sub-agents
-- **ParallelAgent**: Executes multiple sub-agents concurrently
-
-These Workflow Agents can be nested with each other to build more complex execution flows, meeting various business scenario requirements.
-
-# SequentialAgent
-
-## Features
-
-SequentialAgent is the most basic Workflow Agent. It executes a series of sub-agents sequentially according to the order provided in the configuration. After each sub-agent completes execution, its output is passed to the next sub-agent through the History mechanism, forming a linear execution chain.
-
-
-
-```go
-type SequentialAgentConfig struct {
- Name string // Agent name
- Description string // Agent description
- SubAgents []Agent // List of sub-agents, arranged in execution order
-}
-
-func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
-```
-
-SequentialAgent execution follows these rules:
-
-1. **Linear execution**: Strictly follows the order of the SubAgents array
-2. **History passing**: Each agent's execution result is added to History, allowing subsequent agents to access the execution history of previous agents
-3. **Early exit**: If any sub-agent produces an ExitAction / Interrupt, the entire Sequential flow terminates immediately
-
-SequentialAgent is suitable for the following scenarios:
-
-- **Multi-step processing flows**: Such as data preprocessing -> analysis -> report generation
-- **Pipeline processing**: Each step's output serves as the next step's input
-- **Task sequences with dependencies**: Subsequent tasks depend on results from previous tasks
-
-## Example
-
-This example demonstrates how to use SequentialAgent to create a three-step document processing pipeline:
-
-1. **DocumentAnalyzer**: Analyzes document content
-2. **ContentSummarizer**: Summarizes analysis results
-3. **ReportGenerator**: Generates the final report
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-// Create ChatModel instance
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// Document analysis Agent
-func NewDocumentAnalyzerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "DocumentAnalyzer",
- Description: "Analyzes document content and extracts key information",
- Instruction: "You are a document analysis expert. Please carefully analyze the document content provided by the user, extracting key information, main points, and important data.",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Content summarization Agent
-func NewContentSummarizerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ContentSummarizer",
- Description: "Summarizes analysis results",
- Instruction: "Based on the previous document analysis results, generate a concise and clear summary highlighting the most important findings and conclusions.",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Report generation Agent
-func NewReportGeneratorAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ReportGenerator",
- Description: "Generates the final analysis report",
- Instruction: "Based on the previous analysis and summary, generate a structured analysis report including an executive summary, detailed analysis, and recommendations.",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // Create three processing step Agents
- analyzer := NewDocumentAnalyzerAgent()
- summarizer := NewContentSummarizerAgent()
- generator := NewReportGeneratorAgent()
-
- // Create SequentialAgent
- sequentialAgent, err := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
- Name: "DocumentProcessingPipeline",
- Description: "Document processing pipeline: Analysis → Summary → Report Generation",
- SubAgents: []adk.Agent{analyzer, summarizer, generator},
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // Create Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: sequentialAgent,
- })
-
- // Execute document processing flow
- input := "Please analyze the following market report: In Q3 2024, company revenue grew 15%, mainly due to the successful launch of new product lines. However, operating costs also increased by 8%, requiring efficiency optimization."
-
- fmt.Println("Starting document processing pipeline...")
- iter := runner.Query(ctx, input)
-
- stepCount := 1
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatal(event.Err)
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- fmt.Printf("\n=== Step %d: %s ===\n", stepCount, event.AgentName)
- fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
- stepCount++
- }
- }
-
- fmt.Println("\nDocument processing pipeline completed!")
-}
-```
-
-Run result:
-
-```markdown
-Starting document processing pipeline...
-
-=== Step 1: DocumentAnalyzer ===
-Market Report Key Information Analysis:
-
-1. Revenue Growth:
- - In Q3 2024, company revenue grew 15% year-over-year.
- - The main driver of revenue growth was the successful launch of new product lines.
-
-2. Cost Situation:
- - Operating costs increased by 8%.
- - The cost increase reminds the company of the need for efficiency optimization.
-
-Key Points Summary:
-- The new product line launch significantly drove revenue growth, showing good results in product innovation.
-- Although revenue increased, the rise in operating costs somewhat affected profitability, highlighting the importance of improving operational efficiency.
-
-Important Data:
-- Revenue growth rate: 15%
-- Operating cost growth rate: 8%
-
-=== Step 2: ContentSummarizer ===
-Summary: In Q3 2024, the company achieved 15% revenue growth, mainly attributed to the successful launch of new product lines, demonstrating significant improvement in product innovation capability. However, operating costs also increased by 8%, putting some pressure on profitability and emphasizing the urgent need for operational efficiency optimization. Overall, the company needs to seek a better balance between growth and cost control to ensure sustainable healthy development.
-
-=== Step 3: ReportGenerator ===
-Analysis Report
-
-I. Executive Summary
-In Q3 2024, the company achieved 15% year-over-year revenue growth, mainly driven by the successful launch of new product lines, demonstrating strong product innovation capability. However, operating costs also increased 8% year-over-year, putting some pressure on profit margins. To ensure continued profitable growth, focus should be on optimizing operational efficiency and promoting balanced development of cost control and revenue growth.
-
-II. Detailed Analysis
-1. Revenue Growth Analysis
-- The company's 15% revenue growth reflects good market acceptance of new product lines, effectively expanding revenue sources.
-- The launch of new product lines demonstrates improved R&D and market responsiveness, laying a foundation for future sustained growth.
-
-2. Operating Cost Situation
-- The 8% increase in operating costs may come from various aspects including raw material price increases, decreased production efficiency, or increased sales and promotion expenses.
-- This cost increase somewhat offsets the profit gains from revenue growth, affecting overall profitability.
-
-3. Profitability and Efficiency Considerations
-- The mismatch between revenue and cost growth indicates room for improvement in current operational efficiency.
-- Optimizing supply chain management, improving production automation, and strengthening cost control will become key measures.
-
-III. Recommendations
-1. Strengthen follow-up support for new product lines, including marketing and customer feedback mechanisms, to continue driving revenue growth.
-2. Conduct in-depth analysis of operating cost composition, identify main cost drivers, and develop targeted cost reduction strategies.
-3. Promote internal process optimization and technology upgrades to improve production and operational efficiency and alleviate cost pressure.
-4. Establish a dynamic financial monitoring system to achieve real-time tracking and adjustment of revenue and costs, ensuring company financial health.
-
-IV. Conclusion
-The company demonstrated good growth momentum in Q3 2024 but also faces challenges from rising costs. Through continuous product innovation combined with effective cost management, there is potential to achieve dual improvement in profitability and market competitiveness, driving steady company development.
-
-Document processing pipeline completed!
-```
-
-# LoopAgent
-
-## Features
-
-LoopAgent is built on SequentialAgent. It repeatedly executes the configured sub-agent sequence until the maximum iteration count is reached or a sub-agent produces an ExitAction. LoopAgent is particularly suitable for scenarios requiring iterative optimization, repeated processing, or continuous monitoring.
-
-
-
-```go
-type LoopAgentConfig struct {
- Name string // Agent name
- Description string // Agent description
- SubAgents []Agent // List of sub-agents
- MaxIterations int // Maximum iteration count, 0 means infinite loop
-}
-
-func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
-```
-
-LoopAgent execution follows these rules:
-
-1. **Loop execution**: Repeatedly executes the SubAgents sequence, with each loop being a complete Sequential execution process
-2. **History accumulation**: Results from each iteration accumulate in History, allowing subsequent iterations to access all historical information
-3. **Conditional exit**: Supports terminating the loop via ExitAction or reaching maximum iteration count; setting `MaxIterations=0` means infinite loop
-
-LoopAgent is suitable for the following scenarios:
-
-- **Iterative optimization**: Tasks requiring repeated improvement such as code optimization, parameter tuning
-- **Continuous monitoring**: Periodically checking status and executing corresponding operations
-- **Repeated processing**: Tasks that need multiple rounds of processing to achieve satisfactory results
-- **Self-improvement**: Agent continuously improves its output based on previous execution results
-
-## Example
-
-This example demonstrates how to use LoopAgent to create a code optimization loop:
-
-1. **CodeAnalyzer**: Analyzes code issues
-2. **CodeOptimizer**: Optimizes code based on analysis results
-3. **ExitController**: Determines whether to exit the loop
-
-The loop continues until code quality meets standards or maximum iteration count is reached.
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// Code analysis Agent
-func NewCodeAnalyzerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "CodeAnalyzer",
- Description: "Analyzes code quality and performance issues",
- Instruction: `You are a code analysis expert. Please analyze the provided code and identify the following issues:
-1. Performance bottlenecks
-2. Code duplication
-3. Readability issues
-4. Potential bugs
-5. Non-compliance with best practices
-
-If the code is already excellent, output "EXIT: Code quality has met standards" to end the optimization process.`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Code optimization Agent
-func NewCodeOptimizerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "CodeOptimizer",
- Description: "Optimizes code based on analysis results",
- Instruction: `Based on the previous code analysis results, optimize and improve the code:
-1. Fix identified performance issues
-2. Eliminate code duplication
-3. Improve code readability
-4. Fix potential bugs
-5. Apply best practices
-
-Please provide the complete optimized code.`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Create a special Agent to handle exit logic
-func NewExitControllerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ExitController",
- Description: "Controls the exit of the optimization loop",
- Instruction: `Check the previous analysis results. If the code analyst believes the code quality has met standards (contains "EXIT" keyword),
-output "TERMINATE" and generate an exit action to end the loop. Otherwise continue to the next optimization round.`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // Create optimization flow Agents
- analyzer := NewCodeAnalyzerAgent()
- optimizer := NewCodeOptimizerAgent()
- controller := NewExitControllerAgent()
-
- // Create LoopAgent, execute up to 5 optimization rounds
- loopAgent, err := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
- Name: "CodeOptimizationLoop",
- Description: "Code optimization loop: Analysis → Optimization → Check exit condition",
- SubAgents: []adk.Agent{analyzer, optimizer, controller},
- MaxIterations: 5, // Maximum 5 optimization rounds
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // Create Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: loopAgent,
- })
-
- // Code example to optimize
- codeToOptimize := `
-func processData(data []int) []int {
- result := []int{}
- for i := 0; i < len(data); i++ {
- for j := 0; j < len(data); j++ {
- if data[i] > data[j] {
- result = append(result, data[i])
- break
- }
- }
- }
- return result
-}
-`
-
- fmt.Println("Starting code optimization loop...")
- iter := runner.Query(ctx, "Please optimize the following Go code:\n"+codeToOptimize)
-
- iteration := 1
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatal(event.Err)
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- fmt.Printf("\n=== Round %d - %s ===\n", iteration, event.AgentName)
- fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
-
- // Check if exit is needed
- if event.AgentName == "ExitController" {
- if event.Action != nil && event.Action.Exit {
- fmt.Println("\nOptimization loop ended early!")
- break
- }
- iteration++
- }
- }
- }
-
- fmt.Println("\nCode optimization loop completed!")
-}
-```
-
-Run result:
-
-```java
-Starting code optimization loop...
-
-=== Round 1 - CodeAnalyzer ===
-Analyzing the provided code:
-
-```go
-func processData(data []int) []int {
- result := []int{}
- for i := 0; i < len(data); i++ {
- for j := 0; j < len(data); j++ {
- if data[i] > data[j] {
- result = append(result, data[i])
- break
- }
- }
- }
- return result
-}
-```
-
-### 1. Performance Bottlenecks
-
-- Double nested loop with O(n²) time complexity, poor performance for large datasets.
-- Inner loop breaks immediately when condition is met, reducing some unnecessary comparisons, but overall still quadratic complexity.
-
-### 2. Code Duplication
-
-- No obvious code duplication, but logic can be simplified.
-
-### 3. Readability Issues
-
-- The code's intent is not very clear. Based on the code logic, the function filters out elements that have at least one element smaller than them in the array. In other words, it removes all elements that are equal to or are the minimum.
-- Readability can be improved through comments or renaming.
-
-### 4. Potential Bugs
-
-- No obvious bugs currently, but returning an empty array for empty input is expected behavior.
-
-### 5. Non-compliance with Best Practices
-
-- The process is relatively inefficient, not using Go language features to optimize performance.
-- Missing comments or documentation.
-
----
-
-## Optimization Suggestions
-
-If the goal is indeed to return all elements greater than some element in the array (excluding the minimum element), it can be changed to:
-
-- Find the minimum element minVal in the array.
-- Iterate through the array, adding elements greater than minVal to the result.
-- This reduces time complexity to O(n).
-
-Optimized code:
-
-```go
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-### Optimization Points Explained
-
-- Use one traversal to determine minimum value, O(n).
-- Another traversal to construct result, O(n).
-- Properly use slice capacity initialization to reduce memory allocation.
-- Code intent is clear and performance improvement is significant.
-
----
-
-If you expect different logic, please let me know for further adjustments.
-
-=== Round 1 - CodeOptimizer ===
-
-```go
-// processData returns all values in the input slice that are greater than the minimum element.
-// Returns nil if input is empty.
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- // Find the minimum value in the slice
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- // Collect all elements greater than the minimum value
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== Round 1 - ExitController ===
-TERMINATE
-
-=== Round 2 - CodeAnalyzer ===
-EXIT: Code quality has met standards
-
-=== Round 2 - CodeOptimizer ===
-
-```go
-// processData returns all values in the input slice that are greater than the minimum element.
-// Returns nil if input is empty.
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- // Find the minimum value in the slice
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- // Collect all elements greater than the minimum value
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== Round 2 - ExitController ===
-TERMINATE
-
-=== Round 3 - CodeAnalyzer ===
-EXIT: Code quality has met standards
-
-=== Round 3 - CodeOptimizer ===
-
-```go
-// processData returns all values in the input slice that are greater than the minimum element.
-// Returns nil if input is empty.
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== Round 3 - ExitController ===
-TERMINATE
-
-=== Round 4 - CodeAnalyzer ===
-EXIT: Code quality has met standards
-
-=== Round 4 - CodeOptimizer ===
-
-```go
-// processData returns all elements in the slice that are greater than the minimum element.
-// Returns nil if input is empty.
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== Round 4 - ExitController ===
-TERMINATE
-
-=== Round 5 - CodeAnalyzer ===
-EXIT: Code quality has met standards
-
-=== Round 5 - CodeOptimizer ===
-
-```go
-// processData returns all values in the input slice that are greater than the minimum element.
-// Returns nil if input is empty.
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== Round 5 - ExitController ===
-TERMINATE
-
-Code optimization loop completed!
-
-```
-
-
-
-
-## BreakLoop
-
-
-In a Loop Agent, when an Agent needs to interrupt the loop execution, you can use the corresponding Break Action provided by ADK.
-
-```go
-// BreakLoopAction is a programmatic-only agent action used to prematurely
-// terminate the execution of a loop workflow agent.
-// When a loop workflow agent receives this action from a sub-agent, it will stop its
-// current iteration and will not proceed to the next one.
-// It will mark the BreakLoopAction as Done, signalling to any 'upper level' loop agent
-// that this action has been processed and should be ignored further up.
-// This action is not intended to be used by LLMs.
-type BreakLoopAction struct {
- // From records the name of the agent that initiated the break loop action.
- From string
- // Done is a state flag that can be used by the framework to mark when the
- // action has been handled.
- Done bool
- // CurrentIterations is populated by the framework to record at which
- // iteration the loop was broken.
- CurrentIterations int
-}
-
-// NewBreakLoopAction creates a new BreakLoopAction, signaling a request
-// to terminate the current loop.
-func NewBreakLoopAction(agentName string) *AgentAction {
- return &AgentAction{BreakLoop: &BreakLoopAction{
- From: agentName,
- }}
-}
-```
-
-Break Action achieves the interruption purpose without affecting other Agents outside the Loop Agent, while Exit Action immediately interrupts all subsequent Agent execution.
-
-Using the following diagram as an example:
-
-
-
-- When Agent1 issues a BreakAction, the Loop Agent will be interrupted, and Sequential continues to run Agent3
-- When Agent1 issues an ExitAction, the Sequential execution flow terminates entirely, and neither Agent2 nor Agent3 will run
-
-# ParallelAgent
-
-## Features
-
-ParallelAgent allows multiple sub-agents to execute concurrently based on the same input context. All sub-agents start execution simultaneously and wait for all to complete before ending. This pattern is particularly suitable for tasks that can be processed independently in parallel, significantly improving execution efficiency.
-
-
-
-```go
-type ParallelAgentConfig struct {
- Name string // Agent name
- Description string // Agent description
- SubAgents []Agent // List of sub-agents to execute concurrently
-}
-
-func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
-```
-
-ParallelAgent execution follows these rules:
-
-1. **Concurrent execution**: All sub-agents start simultaneously, executing in parallel in independent goroutines
-2. **Shared input**: All sub-agents receive the same initial input and context
-3. **Wait and result aggregation**: Internally uses sync.WaitGroup to wait for all sub-agents to complete, collecting all sub-agent execution results and outputting them in the order received
-
-Additionally, Parallel internally includes exception handling mechanisms by default:
-
-- **Panic recovery**: Each goroutine has independent panic recovery mechanism
-- **Error isolation**: Errors from a single sub-agent do not affect execution of other sub-agents
-- **Interrupt handling**: Supports sub-agent interrupt and resume mechanisms
-
-ParallelAgent is suitable for the following scenarios:
-
-- **Independent task parallel processing**: Multiple unrelated tasks can execute simultaneously
-- **Multi-angle analysis**: Analyzing the same problem from different angles simultaneously
-- **Performance optimization**: Reducing overall execution time through parallel execution
-- **Multi-expert consultation**: Consulting multiple specialized domain Agents simultaneously
-
-## Example
-
-This example demonstrates how to use ParallelAgent to analyze a product proposal from four different angles simultaneously:
-
-1. **TechnicalAnalyst**: Technical feasibility analysis
-2. **BusinessAnalyst**: Business value analysis
-3. **UXAnalyst**: User experience analysis
-4. **SecurityAnalyst**: Security risk analysis
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
- "sync"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
-)
-
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// Technical analysis Agent
-func NewTechnicalAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "TechnicalAnalyst",
- Description: "Analyzes content from a technical perspective",
- Instruction: `You are a technical expert. Please analyze the provided content from technical implementation, architecture design, and performance optimization perspectives.
-Focus on:
-1. Technical feasibility
-2. Architecture rationality
-3. Performance considerations
-4. Technical risks
-5. Implementation complexity`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Business analysis Agent
-func NewBusinessAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "BusinessAnalyst",
- Description: "Analyzes content from a business perspective",
- Instruction: `You are a business analysis expert. Please analyze the provided content from business value, market prospects, and cost-effectiveness perspectives.
-Focus on:
-1. Business value
-2. Market demand
-3. Competitive advantages
-4. Cost analysis
-5. Revenue model`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// User experience analysis Agent
-func NewUXAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "UXAnalyst",
- Description: "Analyzes content from a user experience perspective",
- Instruction: `You are a user experience expert. Please analyze the provided content from user experience, usability, and user satisfaction perspectives.
-Focus on:
-1. User friendliness
-2. Operational convenience
-3. Learning cost
-4. User satisfaction
-5. Accessibility`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// Security analysis Agent
-func NewSecurityAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "SecurityAnalyst",
- Description: "Analyzes content from a security perspective",
- Instruction: `You are a security expert. Please analyze the provided content from information security, data protection, and privacy compliance perspectives.
-Focus on:
-1. Data security
-2. Privacy protection
-3. Access control
-4. Security vulnerabilities
-5. Compliance requirements`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // Create four analysis Agents from different angles
- techAnalyst := NewTechnicalAnalystAgent()
- bizAnalyst := NewBusinessAnalystAgent()
- uxAnalyst := NewUXAnalystAgent()
- secAnalyst := NewSecurityAnalystAgent()
-
- // Create ParallelAgent for simultaneous multi-angle analysis
- parallelAgent, err := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
- Name: "MultiPerspectiveAnalyzer",
- Description: "Multi-angle parallel analysis: Technical + Business + User Experience + Security",
- SubAgents: []adk.Agent{techAnalyst, bizAnalyst, uxAnalyst, secAnalyst},
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // Create Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: parallelAgent,
- })
-
- // Product proposal to analyze
- productProposal := `
-Product Proposal: Intelligent Customer Service System
-
-Overview: Develop an intelligent customer service system based on large language models that can automatically answer user questions, handle common business inquiries, and transfer to human agents when necessary.
-
-Main Features:
-1. Natural language understanding and response
-2. Multi-turn conversation management
-3. Knowledge base integration
-4. Sentiment analysis
-5. Human agent transfer
-6. Conversation history recording
-7. Multi-channel access (Web, WeChat, App)
-
-Technical Architecture:
-- Frontend: React + TypeScript
-- Backend: Go + Gin framework
-- Database: PostgreSQL + Redis
-- AI Model: GPT-4 API
-- Deployment: Docker + Kubernetes
-`
-
- fmt.Println("Starting multi-angle parallel analysis...")
- iter := runner.Query(ctx, "Please analyze the following product proposal:\n"+productProposal)
-
- // Use map to collect results from different analysts
- results := make(map[string]string)
- var mu sync.Mutex
-
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Printf("Error during analysis: %v", event.Err)
- continue
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- mu.Lock()
- results[event.AgentName] = event.Output.MessageOutput.Message.Content
- mu.Unlock()
-
- fmt.Printf("\n=== %s analysis completed ===\n", event.AgentName)
- }
- }
-
- // Output all analysis results
- fmt.Println("\n" + "============================================================")
- fmt.Println("Multi-angle Analysis Results Summary")
- fmt.Println("============================================================")
-
- analysisOrder := []string{"TechnicalAnalyst", "BusinessAnalyst", "UXAnalyst", "SecurityAnalyst"}
- analysisNames := map[string]string{
- "TechnicalAnalyst": "Technical Analysis",
- "BusinessAnalyst": "Business Analysis",
- "UXAnalyst": "User Experience Analysis",
- "SecurityAnalyst": "Security Analysis",
- }
-
- for _, agentName := range analysisOrder {
- if result, exists := results[agentName]; exists {
- fmt.Printf("\n【%s】\n", analysisNames[agentName])
- fmt.Printf("%s\n", result)
- fmt.Println("----------------------------------------")
- }
- }
-
- fmt.Println("\nMulti-angle parallel analysis completed!")
- fmt.Printf("Received %d analysis results\n", len(results))
-}
-```
-
-Run result:
-
-```markdown
-Starting multi-angle parallel analysis...
-
-=== BusinessAnalyst analysis completed ===
-
-=== UXAnalyst analysis completed ===
-
-=== SecurityAnalyst analysis completed ===
-
-=== TechnicalAnalyst analysis completed ===
-
-============================================================
-Multi-angle Analysis Results Summary
-============================================================
-
-【Technical Analysis】
-For this intelligent customer service system proposal, here is a detailed analysis from technical implementation, architecture design, and performance optimization perspectives:
-
----
-
-### I. Technical Feasibility
-
-1. **Natural Language Understanding and Response**
- - Using GPT-4 API for natural language understanding and automatic response is a mature and feasible solution. GPT-4 has strong language understanding and generation capabilities, suitable for handling complex and diverse questions.
-
-2. **Multi-turn Conversation Management**
- - Relies on backend to maintain context state, combined with GPT-4 model can handle multi-turn interactions well. Need to design reasonable context management mechanism (such as conversation history maintenance, key slot extraction, etc.) to ensure context information integrity.
-
-3. **Knowledge Base Integration**
- - Can add specific knowledge base retrieval results to GPT-4 API (retrieval-augmented generation), or integrate knowledge base through local retrieval interface. Technically feasible, but has high requirements for real-time and accuracy.
-
-4. **Sentiment Analysis**
- - Sentiment analysis function can be implemented with independent lightweight models (such as fine-tuned BERT), or try using GPT-4 output, but cost is higher. Sentiment analysis capability helps intelligent customer service better understand user emotions and improve user experience.
-
-5. **Human Agent Transfer**
- - Technically achievable through establishing event trigger rules (such as turn count, emotion threshold, keyword detection) to implement automatic transfer to human. System needs to support ticket or session transfer mechanism and ensure seamless session switching.
-
-6. **Multi-channel Access**
- - Multi-channel access including web, WeChat, App can all be achieved through unified API gateway, technology is mature, while needing to handle channel differences (message format, authentication, push mechanism, etc.).
-
----
-
-### II. Architecture Rationality
-
-- **Frontend React + TypeScript**
- Very suitable for building responsive customer service interface, mature ecosystem, convenient for multi-channel component sharing.
-
-- **Backend Go + Gin**
- Go language has excellent performance, Gin framework is lightweight and high-performance, suitable for high-concurrency scenarios. Backend handles GPT-4 API integration, state management, multi-channel message forwarding and other responsibilities, reasonable choice.
-
-- **Database PostgreSQL + Redis**
- - PostgreSQL handles structured data storage, such as user information, conversation history, knowledge base metadata.
- - Redis handles session state caching, hot knowledge base, rate limiting, etc., improving access performance.
- Architecture design follows common large internet product patterns, with clear component division.
-
-- **AI Model GPT-4 API**
- Using mature API reduces development difficulty and model maintenance cost; disadvantage is high dependency on network and API calls.
-
-- **Deployment Docker + Kubernetes**
- Containerization and K8s orchestration ensure system elastic scaling, high availability and canary deployment, suitable for production environment, follows modern microservices architecture trends.
-
----
-
-### III. Performance Considerations
-
-1. **Response Time**
- - GPT-4 API calls have inherent latency (usually hundreds of milliseconds to 1 second), significantly affecting response time. Need to handle interface asynchronously and design frontend experience well (such as loading animations, partial progressive response).
-
-2. **Concurrent Processing Capability**
- - Backend Go has high concurrent processing advantages, combined with Redis caching hot data, can greatly improve overall throughput.
- - But GPT-4 API calls are limited by OpenAI service QPS limits and call costs, need to reasonably design call frequency and degradation strategies.
-
-3. **Caching Strategy**
- - Cache user conversation context and common question answers to reduce repeated API calls.
- - Match key questions locally first, call GPT-4 only on failure, improving efficiency.
-
-4. **Multi-channel Load Balancing**
- - Need to design unified message bus and reliable async queue to prevent traffic spikes from one channel affecting overall system stability.
-
----
-
-### IV. Technical Risks
-
-1. **GPT-4 API Dependency**
- - High dependency on third-party API, risks include service interruption, interface changes and cost fluctuations.
- - Recommend designing local cache and limited alternative response logic to handle API exceptions.
-
-2. **Multi-turn Conversation Context Management Difficulty**
- - Context too long or complex will reduce answer quality, need to design context length limits and selective important information retention mechanism.
-
-3. **Knowledge Base Integration Complexity**
- - How to achieve knowledge base and...
-----------------------------------------
-
-【Business Analysis】
-Here is the business perspective analysis of the intelligent customer service system product proposal:
-
-1. Business Value
-- Improve customer service efficiency: Automatically answer user questions and common inquiries, reduce human agent pressure, lower labor costs.
-- Improve user experience: Multi-turn conversation and sentiment analysis make interactions more natural, enhance customer satisfaction and stickiness.
-- Data-driven decision support: Conversation history and knowledge base integration provide valuable user feedback and behavior data for enterprises, optimizing products and services.
-- Support business expansion: Multi-channel access (web, WeChat, App) meets different customer access habits, improving coverage.
-
-2. Market Demand
-- Market demand for intelligent customer service continues to grow, especially in e-commerce, finance, healthcare, education and other industries, customer service automation is an important direction for enterprise digital transformation.
-- With the maturity of AI technology, enterprises expect to use large language models to improve customer service intelligence level.
-- Users' demand for instant response and 24/7 service is increasing, driving widespread adoption of intelligent customer service systems.
-
-3. Competitive Advantages
-- Using advanced GPT-4 large language model, has strong natural language understanding and generation capabilities, improving Q&A accuracy and conversation naturalness.
-- Sentiment analysis function helps accurately identify user emotions, dynamically adjust response strategies, improve customer satisfaction.
-- Multi-channel access design meets enterprise diversified customer reach needs, enhancing product applicability.
-- Technical architecture uses microservices, containerized deployment, convenient for elastic scaling and maintenance, improving system stability and scalability.
-
-4. Cost Analysis
-- AI model call cost is high, depends on GPT-4 API, need to adjust budget based on call volume and response speed.
-- Technical R&D investment is large, involving frontend and backend, multi-channel integration, AI and knowledge base management.
-- Operation and server costs need to consider multi-channel concurrent access.
-- In the long term, human agent count can be significantly reduced, saving labor costs.
-- Can reduce initial hardware investment through cloud services, but cloud resource usage needs careful management to control costs.
-
-5. Revenue Model
-- SaaS subscription service: Charge monthly/yearly service fees to enterprise customers, tiered pricing based on access channels, concurrency, and feature levels.
-- Charge by call count or conversation count, suitable for customers with large business fluctuations.
-- Value-added services: Data analysis report customization, industry knowledge base integration, human agent collaboration tools, etc.
-- For medium and large customers, can provide custom development and technical support, charging project fees.
-- Through continuous model and service optimization, increase customer retention and renewal rates.
-
-In summary, this intelligent customer service system based on mature technology and AI advantages has good business value and market potential. Its multi-channel access and sentiment analysis features enhance competitiveness, but need to reasonably control AI call costs and operating expenses. Recommend focusing on SaaS subscription and value-added services, combined with marketing, quickly capture customer resources and improve profitability.
-----------------------------------------
-
-【User Experience Analysis】
-For this intelligent customer service system proposal, I will analyze from user experience, usability, user satisfaction and accessibility perspectives:
-
-1. User Friendliness
-- Natural language understanding and response capability improves user communication experience with the system, allowing users to express needs in natural language, reducing communication barriers.
-- Multi-turn conversation management allows the system to understand context, reducing repeated explanations, enhancing conversation coherence, further improving user experience.
-- Sentiment analysis function helps the system identify user emotions, making more thoughtful responses, improving interaction personalization and humanization.
-- Multi-channel access covers users' commonly used access paths, convenient for users to get service anytime anywhere, improving friendliness.
-
-2. Operational Convenience
-- Automatically answering common business inquiries can reduce user waiting time and operational burden, improving response speed.
-- Human agent transfer mechanism ensures complex issues can be handled timely, ensuring service continuity and seamless operation handoff.
-- Conversation history recording convenient for users to review consultation content, avoiding repeated queries, improving operational convenience.
-- Using modern tech stack (React, TypeScript) provides good frontend interaction performance and response speed, indirectly enhancing operational smoothness.
-
-3. Learning Cost
-- Based on natural language processing, users don't need to learn special commands, lowering usage threshold.
-- Multi-turn conversation natural connection makes it easier for users to understand system response logic, reducing confusion and frustration.
-- Consistent interface across different channels (such as keeping similar experience on web and WeChat) helps users get started quickly.
-- More precise feedback provided through sentiment analysis reduces time cost of users frequently trying due to misunderstanding.
-
-4. User Satisfaction
-- Fast and accurate automatic replies and multi-turn conversation reduce user waiting and repeated input, improving satisfaction.
-- Sentiment analysis makes the system better understand user emotions, bringing warmer interaction experience, increasing user stickiness.
-- Human agent intervention ensures complex issues are properly handled, improving service quality perception.
-- Multi-channel coverage meets different users' usage scenarios, enhancing overall satisfaction.
-
-5. Accessibility
-- Multi-channel access covers web, WeChat, App, adapting to different users' devices and environments, improving accessibility.
-- The proposal doesn't explicitly mention accessibility design (such as screen reader compatibility, high contrast mode, etc.), which may be an area to supplement in the future.
-- Frontend using React and TypeScript is conducive to implementing responsive design and accessibility features, but need to ensure development standards are implemented.
-- Backend architecture and deployment solution ensure system stability and scalability, indirectly improving user continuous accessibility.
-
-Summary:
-This intelligent customer service system proposal is fairly comprehensive in user experience and usability considerations, using large language models to achieve natural multi-turn conversation, sentiment analysis and knowledge base integration, meeting users' diverse needs. Meanwhile, multi-channel access enhances system coverage. Recommend strengthening accessibility design in specific implementation to achieve more comprehensive accessibility assurance, while continuing to optimize conversation strategies to improve user satisfaction.
-----------------------------------------
-
-【Security Analysis】
-For this intelligent customer service system proposal, here is the analysis from information security, data protection and privacy compliance perspectives:
-
-I. Data Security
-
-1. Data Transmission Security
-- Recommend all client-server communications use TLS/SSL encryption to ensure data confidentiality and integrity during transmission.
-- Since multi-channel access is supported (web, WeChat, App), need to ensure each entry point strictly implements encrypted transmission.
-
-2. Data Storage Security
-- PostgreSQL stores sensitive information like conversation history and user data, need to enable database encryption (such as transparent data encryption TDE or field-level encryption) to prevent data leakage.
-- Redis as cache may store temporary session data, also need to enable access authentication and encrypted transmission.
-- Implement minimum storage principle for user sensitive data, avoid storing unrelated data beyond scope.
-- Data backup process needs encrypted storage, and backup access should also be controlled.
-
-3. API Call Security
-- GPT-4 API calls generate large amounts of user data interaction, should evaluate its data processing and storage policies to ensure compliance with data security requirements.
-- Add call permission management, limit API key access scope and permissions to prevent abuse.
-
-4. Log Security
-- System logs should avoid storing plaintext sensitive information, especially personal identity information and conversation content. Log access needs strict control.
-
-II. Privacy Protection
-
-1. Personal Data Processing
-- Collection and storage of user personal data (name, contact information, account information, etc.) must clearly inform users and obtain user consent.
-- Implement data anonymization/de-identification technology, especially for identity information processing in conversation history.
-
-2. User Privacy Rights
-- Meet users' rights to access, correct, and delete data in relevant laws and regulations (such as Personal Information Protection Law, GDPR).
-- Provide privacy policy clearly disclosing data collection, use and sharing situations.
-
-3. Interaction Privacy
-- Multi-turn conversation and sentiment analysis features should consider avoiding excessive invasion of user privacy, such as transparent notification and restriction of sensitive emotion data usage.
-
-4. Third-party Compliance
-- GPT-4 API is provided by third party, need to ensure its service complies with relevant privacy compliance requirements and data protection standards.
-
-III. Access Control
-
-1. User Identity Verification
-- When system involves user identity information query and management, need to establish reliable identity authentication mechanism.
-- Support multi-factor authentication to enhance security.
-
-2. Permission Management
-- Backend management interface and human agent transfer module need to use role-based access control (RBAC) to ensure minimum operation permissions.
-- Operations accessing sensitive data need detailed audit and monitoring.
-
-3. Session Management
-- Need effective session management mechanism for multi-channel sessions to prevent session hijacking.
-- Conversation history access permissions should be limited to only relevant users or authorized personnel.
-
-IV. Security Vulnerabilities
-
-1. Application Security
-- Frontend React+TypeScript should prevent XSS, CSRF attacks, reasonably use Content Security Policy (CSP).
-- Backend Go application needs to prevent SQL injection, request forgery and permission deficiency. Gin framework provides middleware support, recommend fully utilizing security modules.
-
-2. AI Model Risks
-- GPT-4 API input/output may have sensitive information leakage or model misuse risks, need to limit input content and filter sensitive information.
-- Prevent generating malicious answers or information leakage, establish content review mechanism.
-
-3. Container and Deployment Security
-- Docker containers must use secure images and patch timely. Kubernetes cluster network policies and access control need to be complete.
-- Container runtime permissions minimized to avoid container escape risks.
-
-V. Compliance Requirements
-
-1. Data Protection Regulations
-- Based on operating region, need to comply with Personal Information Protection Law (PIPL), EU General Data Protection Regulation (GDPR) or other relevant legal requirements.
-- Clearly define user data collection, processing, transmission and storage processes comply with regulations.
-
-2. User Privacy Notice and Consent
-- Should provide clear privacy policy and terms of use, explaining data purposes and processing methods.
-- Implement user consent management mechanism.
-
-3. Cross-border Data Transfer Compliance
-- If system involves cross-border data flow, need to assess compliance risks and take corresponding technical...
-----------------------------------------
-
-Multi-angle parallel analysis completed!
-Received 4 analysis results
-```
-
-# Summary
-
-Workflow Agents provide powerful multi-agent collaboration capabilities for Eino ADK. By reasonably selecting and combining these Workflow Agents, developers can build efficient and reliable multi-agent collaboration systems to meet various complex business requirements.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_interface.md b/content/en/docs/eino/core_modules/eino_adk/agent_interface.md
index 0c57ea1982d..e22d4cd2e7a 100644
--- a/content/en/docs/eino/core_modules/eino_adk/agent_interface.md
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_interface.md
@@ -1,390 +1,198 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
-title: 'Eino ADK: Agent Abstraction'
+title: Agent Abstraction
weight: 3
---
-# Agent Definition
+# Agent Interface
-Eino defines a basic interface for Agents. Any struct implementing this interface can be considered an Agent:
+All ADK functionality revolves around the `Agent` interface:
```go
-// github.com/cloudwego/eino/adk/interface.go
+// github.com/cloudwego/eino/adk
-type Agent interface {
+type TypedAgent[M MessageType] interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
- Run(ctx context.Context, input *AgentInput, opts ...AgentRunOption) *AsyncIterator[*AgentEvent]
+ Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]
}
+
+// Default type alias (uses *schema.Message)
+type Agent = TypedAgent[*schema.Message]
```
| Method | Description |
| Name | The name of the Agent, serving as the Agent's identifier |
| Description | Description of the Agent's capabilities, mainly used for other Agents to understand and determine this Agent's responsibilities or functions |
| Run | The core execution method of the Agent, returns an iterator through which callers can continuously receive events produced by the Agent |
Name | Agent name identifier |
Description | Capability description, for other Agents or the framework to understand abilities |
Run | Core execution method, asynchronously returns event stream (Future pattern) |
-
-## AgentRunOption
-
-`AgentRunOption` is defined by the Agent implementation and can modify Agent configuration or control Agent behavior at the request level.
-
-Eino ADK provides some commonly defined Options for users:
-
-- `WithSessionValues`: Set cross-Agent read/write data
-- `WithSkipTransferMessages`: When configured, if the Event is a Transfer to SubAgent, the messages in the Event will not be appended to History
-
-Eino ADK provides `WrapImplSpecificOptFn` and `GetImplSpecificOptions` methods for Agents to wrap and read custom `AgentRunOptions`.
-
-When using the `GetImplSpecificOptions` method to read `AgentRunOptions`, AgentRunOptions that don't match the required type (like options in the example) will be ignored.
+| Generic Type | Default Alias |
TypedAgent[*schema.Message] | Agent |
TypedAgentInput[*schema.Message] | AgentInput |
TypedAgentEvent[*schema.Message] | AgentEvent |
TypedAgentOutput[*schema.Message] | AgentOutput |
TypedMessageVariant[*schema.Message] | MessageVariant |
| Component/Middleware | Affected Prompts |
| FileSystem Middleware | File system tool descriptions, system prompts, execution tool prompts |
| Reduction Middleware | Tool result truncation/cleanup prompt text |
| Skill Middleware | Skill system prompts, skill tool descriptions |
| ChatModelAgent | Built-in system prompts |
+# ADK Architecture
## Agent Interface
-The core of Eino ADK is the Agent abstraction (Agent Interface). All ADK functionality is designed around the Agent abstraction. For details, see [Eino ADK: Agent Interface](/docs/eino/core_modules/eino_adk/agent_interface)
+All ADK functionality revolves around the `Agent` interface:
```go
type Agent interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
-
- // Run runs the agent.
- // The returned AgentEvent within the AsyncIterator must be safe to modify.
- // If the returned AgentEvent within the AsyncIterator contains MessageStream,
- // the MessageStream MUST be exclusive and safe to be received directly.
- // NOTE: it's recommended to use SetAutomaticClose() on the MessageStream of AgentEvents emitted by AsyncIterator,
- // so that even the events are not processed, the MessageStream can still be closed.
Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
}
```
-The definition of `Agent.Run` is:
-
-1. Get task details and related data from the input AgentInput, AgentRunOption, and optional Context Session
-2. Execute the task and write the execution process and results to the AgentEvent Iterator
-
-`Agent.Run` requires the Agent implementation to execute asynchronously in a Future pattern. The core is divided into three steps. For specifics, refer to the implementation of the Run method in ChatModelAgent:
+Semantics of `Run`:
-1. Create a pair of Iterator and Generator
-2. Start the Agent's asynchronous task and pass in the Generator to process AgentInput. The Agent executes core logic in this asynchronous task (e.g., ChatModelAgent calls LLM) and writes new events to the Generator for the Agent caller to consume from the Iterator
-3. Return the Iterator immediately after starting the task in step 2
+1. Obtain task information from `AgentInput` and Context
+2. Execute the task asynchronously, writing produced events into the `AsyncIterator`
+3. Return the Iterator immediately after starting the async task (Future pattern)
-## Multi-Agent Collaboration
+## ChatModelAgent
-Around the Agent abstraction, Eino ADK provides various simple, easy-to-use composition primitives for rich scenarios, supporting the development of diverse Multi-Agent collaboration strategies such as Supervisor, Plan-Execute, Group-Chat, and other Multi-Agent scenarios. This enables different Agent division of labor and cooperation patterns to handle more complex tasks. For details, see [Eino ADK: Agent Collaboration](/docs/eino/core_modules/eino_adk/agent_collaboration)
+The core ADK implementation. Uses ChatModel as the decision-maker and autonomously drives problem-solving through a ReAct Loop.
-The collaboration primitives defined by Eino ADK during Agent collaboration are as follows:
+**ChatModelAgent = ChatModel + Tools + ReAct Loop + Middleware**
-- Collaboration methods between Agents
+For details, see: [Eino ADK: ChatModelAgent Introduction](/docs/eino/overview/eino_adk_quickstart)
-| Collaboration Method | Description |
| Transfer | Directly transfer the task to another Agent. The current Agent exits after execution and does not care about the task execution status of the transferred Agent |
| ToolCall(AgentAsTool) | Call an Agent as a ToolCall, wait for the Agent's response, and obtain the output result of the called Agent for the next round of processing |
| Context Strategy | Description | |
| Upstream Agent Full Dialogue | Get the complete dialogue record of this Agent's upstream Agent | |
| New Task Description | Ignore the complete dialogue record of the upstream Agent and provide a new task summary as the sub-Agent's AgentInput | |
| Collaboration Method | Mechanism | Use Cases |
| AgentAsTool (recommended) | Sub-Agent wrapped as a Tool, parent Agent autonomously decides whether to invoke | Delegating subtasks, capability composition |
| Workflow | Sequential / Loop / Parallel deterministic orchestration | Multi-step tasks with fixed processes |
| Decision Autonomy | Description |
| Autonomous Decision | Inside the Agent, based on its available downstream Agents, when assistance is needed, autonomously select downstream Agents for assistance. Generally, the Agent makes decisions based on LLM internally, but even if selection is based on preset logic, it is still considered autonomous decision from outside the Agent |
| Preset Decision | Pre-set the next Agent after an Agent executes a task. The execution order of Agents is predetermined and predictable |
| Category | ChatModel Agent | Workflow Agents | Custom Logic | EinoBuiltInAgent (supervisor, plan‑execute) |
| Function | Thinking, generation, tool calls | Control execution flow among agents | Run custom logic | Out‑of‑the‑box multi‑agent pattern encapsulation |
| Core | LLM | Predetermined execution flows (sequential/parallel/loop) | Custom code | High‑level encapsulation based on Eino practical experience |
| Purpose | Generation, dynamic decisions | Structured processing, orchestration | Customization needs | Turnkey solutions for specific scenarios |
| Component | Responsibility | Documentation | ||
| ChatModelAgent | ReAct Loop: Reasoning → Action → Feedback, autonomous decision-making | ChatModelAgent Introduction | ||
| Middleware | Inject behaviors at lifecycle points of the ReAct Loop (compression, search, retry, etc.) | ChatModelAgentMiddleware | ||
| Runner | Single Agent run entry point: Query / Run → Event Stream | Agent Runner and Extension | ||
| TurnLoop | Multi-turn runtime: Push / Preempt / Stop + declarative checkpoint/resume | Agent Cancel and TurnLoop | ||
| DeepAgents | Pre-built Agents: Task planning (PlanTask) + subtask delegation (TaskTool) | DeepAgents |
+## Other Agent Types
-# ADK Examples
+In addition to ChatModelAgent, ADK also provides deterministic orchestration primitives:
-The [Eino‑examples](https://github.com/cloudwego/eino-examples/tree/main/adk) project provides various ADK implementation examples. You can refer to the example code and descriptions to build an initial understanding of ADK capabilities:
+- **Workflow Agents**: Sequential / Loop / Parallel Agents, for structured orchestration of predefined processes.
+- **Custom Agent**: Implement the `Agent` interface to integrate with the framework.
-| Project Path | Introduction | Diagram |
| Sequential workflow example | This example code demonstrates a sequential multi-agent workflow built using Eino ADK's Workflow paradigm. | ![]() |
| Loop workflow example | This example code builds a reflection-iteration agent framework based on Eino ADK's Workflow paradigm using LoopAgent. | ![]() |
| Parallel workflow example | This example code builds a concurrent information collection framework based on Eino ADK's Workflow paradigm using ParallelAgent: | ![]() |
| supervisor | This use case employs a single-layer Supervisor managing two relatively comprehensive sub-Agents: Research Agent handles retrieval tasks, Math Agent handles various mathematical operations (add, multiply, divide), but all math operations are uniformly processed within the same Math Agent rather than being split into multiple sub-Agents. This design simplifies the agent hierarchy, suitable for scenarios where tasks are relatively concentrated and don't require excessive decomposition, facilitating rapid deployment and maintenance. | ![]() |
| layered‑supervisor | This use case implements a multi-tier intelligent agent supervision system, where the top-level Supervisor manages Research Agent and Math Agent, and Math Agent is further subdivided into three sub-Agents: Subtract, Multiply, and Divide. The top-level Supervisor is responsible for assigning research tasks and math tasks to lower-level Agents, while Math Agent as a mid-tier supervisor further dispatches specific math operation tasks to its sub-Agents. | ![]() |
| plan‑execute example | This example implements a multi-Agent travel planning system using the plan-execute-replan pattern based on Eino ADK. The core function is to process complex user travel requests (such as "3-day Beijing trip, need flights from New York, hotel recommendations, must-see attractions") through a "plan-execute-replan" loop to complete tasks: 1. Plan: Planner Agentgenerates a step-by-step execution plan based on the large model (e.g., "Step 1: check Beijing weather, Step 2: search New York to Beijing flights"); 2. Execute: Executor Agentcalls mock tools **weather (get_weather), flights (search_flights), hotels (search_hotels), attractions (search_attractions)** to execute each step. If user input information is missing (e.g., budget not specified), it calls ask_for_clarificationtool to ask follow-up questions; 3. Replan: Replanner Agentevaluates whether the plan needs adjustment based on tool execution results (e.g., if no flight tickets available, reselect dates). Execute and Replan continuously loop until all steps in the plan are completed; 4. Supports session trajectory tracking (CozeLoop callback) and state management, ultimately outputting a complete travel plan. Structurally, plan-execute-replan has two layers: | ![]() |
| book recommendation agent (interrupt and resume) | This code demonstrates a book recommendation chat agent implementation built on the Eino ADK framework, showcasing Agent interrupt and resume functionality. | ![]() |
+# What's Next
diff --git a/content/en/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md b/content/en/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md
new file mode 100644
index 00000000000..508aede7472
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md
@@ -0,0 +1,540 @@
+---
+Description: ""
+date: "2026-05-17"
+lastmod: ""
+tags: []
+title: Agent Cancel and TurnLoop Quick Start
+weight: 10
+---
+
+A quick start guide for the two core features in Eino ADK: **Agent Cancel** and **TurnLoop**. Introduced since [v0.9.0-alpha.9](https://github.com/cloudwego/eino/releases/tag/v0.9.0-alpha.9).
+
+## Type Conventions
+
+All examples in this document use the following generic instantiations:
+
+- `T = string` (the business item type pushed to TurnLoop)
+- `M = *schema.Message` (Agent message type, i.e., standard `Message`)
+
+Related type aliases in ADK:
+
+```go
+type Agent = TypedAgent[*schema.Message]
+type AgentInput = TypedAgentInput[*schema.Message]
+type AgentEvent = TypedAgentEvent[*schema.Message]
+```
+
+When using `*schema.AgenticMessage`, simply replace `M` with the corresponding type — all API signatures are fully symmetric.
+
+---
+
+## Part 1: Agent Cancel
+
+### Scenario
+
+After a user sends a request to an agent, they want to cancel the current execution due to waiting too long or a change in requirements.
+
+### Core API
+
+```go
+// Create cancel option and cancel function
+cancelOpt, cancelFunc := adk.WithCancel()
+
+// Start the agent, passing the cancel option
+iter := runner.Run(ctx, []*schema.Message{schema.UserMessage("hello")}, cancelOpt)
+
+// Initiate cancellation (can be called from any goroutine)
+handle, contributed := cancelFunc(adk.WithAgentCancelMode(adk.CancelImmediate))
+// contributed == true: this call affected the execution result
+// contributed == false: agent already ended or cancel already completed, this call has no effect
+
+err := handle.Wait()
+```
+
+Three possible return values from `CancelHandle.Wait()`:
+
+```go
+switch {
+case err == nil:
+ // Cancel succeeded
+case errors.Is(err, adk.ErrCancelTimeout):
+ // Safe point timeout, automatically escalated to immediate cancel
+case errors.Is(err, adk.ErrExecutionEnded):
+ // Agent ended naturally before cancel took effect
+}
+```
+
+### Three Cancel Modes
+
+| Mode | Behavior | Use Case |
CancelImmediate | Interrupts immediately without waiting for a safe point | Emergency stop, timeout fallback |
CancelAfterChatModel | Waits for the current ChatModel call to complete before canceling | Need complete model response |
CancelAfterToolCalls | Waits for all current ToolCalls to complete before canceling | Ensure tool side effects are complete |
+
+### Basic Usage
+
+```go
+loop := adk.NewTurnLoop(adk.TurnLoopConfig[string, *schema.Message]{
+ // GenInput: receives all items in the buffer, decides which to consume this turn
+ GenInput: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], items []string) (*adk.GenInputResult[string, *schema.Message], error) {
+ return &adk.GenInputResult[string, *schema.Message]{
+ Input: &adk.AgentInput{Messages: []*schema.Message{schema.UserMessage(strings.Join(items, "\n"))}},
+ Consumed: items,
+ }, nil
+ },
+
+ // PrepareAgent: builds the Agent based on consumed items for this turn
+ PrepareAgent: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], consumed []string) (adk.Agent, error) {
+ return myAgent, nil
+ },
+
+ // OnAgentEvents: handles the agent event stream (optional)
+ OnAgentEvents: func(ctx context.Context, tc *adk.TurnContext[string, *schema.Message], events *adk.AsyncIterator[*adk.AgentEvent]) error {
+ for {
+ event, ok := events.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ return event.Err
+ }
+ log.Printf("Received event: agent=%s", event.AgentName)
+ }
+ return nil
+ },
+})
+
+loop.Push("message 1")
+loop.Push("message 2")
+loop.Run(ctx) // Non-blocking, starts background processing
+loop.Push("message 3") // Can still push while running
+loop.Stop()
+result := loop.Wait() // Blocks until exit
+```
+
+### Core Callbacks
+
+| Callback | Required | Responsibility |
GenInput | ✅ | Receives all buffered items, returns Consumed(processed this turn) and Remaining(kept for subsequent turns). Items not in either will be discarded. |
PrepareAgent | ✅ | Builds the Agent based on Consumed items (set prompt, tools, middleware, etc.) |
OnAgentEvents | ❌ | Handles agent event stream. When not set, events are drained by default and the first error is returned |
GenResume | ❌ | Called when resuming from checkpoint, decides how to merge interrupted/unhandled/new items |
| Checkpoint State | Behavior |
| Mid-turn checkpoint exists (agent interrupted during execution) | Calls GenResume, passing interrupted/unhandled/new items for application-layer decision before resuming |
| Between-turns checkpoint exists (stopped between turns) | Adds buffered items to the buffer, processes normally via GenInput |
| No checkpoint exists | Starts from scratch |
-When at least one Tool in the Tools list is configured with ReturnDirectly, the ReAct Agent structure becomes more complex: a Branch is added after ToolsNode to determine whether a ReturnDirectly Tool was called. If so, it goes directly to END; otherwise, it proceeds to ChatModel as usual.
+When at least one Tool in the Tools list is configured with ReturnDirectly, the ReAct Agent structure becomes more complex: after ToolsNode, a Branch is added to determine whether a ReturnDirectly Tool was called. If so, it goes directly to END; otherwise, it proceeds to ChatModel as usual.
## Initialization
-The ReactAgent initialization function is provided. Required parameters are Model and ToolsConfig. Optional parameters are MessageModifier, MaxStep, ToolReturnDirectly, and StreamToolCallChecker.
+An initialization function for ReactAgent is provided. Required parameters are Model and ToolsConfig; optional parameters include MessageModifier, MaxStep, ToolReturnDirectly, and StreamToolCallChecker.
```bash
go get github.com/cloudwego/eino-ext/components/model/openai@latest
@@ -43,16 +43,16 @@ import (
)
func main() {
- // first initialize the required chatModel
+ // First initialize the required chatModel
toolableChatModel, err := openai.NewChatModel(...)
- // initialize the required tools
+ // Initialize the required tools
tools := compose.ToolsNodeConfig{
InvokableTools: []tool.InvokableTool{mytool},
StreamableTools: []tool.StreamableTool{myStreamTool},
}
- // create agent
+ // Create the agent
agent, err := react.NewAgent(ctx, &react.AgentConfig{
ToolCallingModel: toolableChatModel,
ToolsConfig: tools,
@@ -63,9 +63,9 @@ func main() {
### Model
-Since ReAct Agent needs to make tool calls, the Model needs to have ToolCall capability, so you need to configure a ToolCallingChatModel.
+Since the ReAct Agent needs to make tool calls, the Model must have ToolCall capability, so a ToolCallingChatModel must be configured.
-Inside the Agent, the WithTools interface is called to register the Agent's tool list with the model. The definition is:
+Internally, the Agent calls the WithTools interface to register the Agent's tool list with the model. The definition is:
```go
// BaseChatModel defines the basic interface for chat models.
@@ -91,11 +91,13 @@ type ToolCallingChatModel interface {
}
```
-Currently, eino provides implementations such as openai and ark, as long as the underlying model supports tool call.
+Currently, Eino provides implementations such as openai and ark—any underlying model that supports tool call will work.
+
```bash
go get github.com/cloudwego/eino-ext/components/model/openai@latest
go get github.com/cloudwego/eino-ext/components/model/ark@latest
```
+
```go
import (
"github.com/cloudwego/eino-ext/components/model/openai"
@@ -131,7 +133,8 @@ func arkExample() {
### ToolsConfig
-toolsConfig type is `compose.ToolsNodeConfig`. In eino, to build a Tool node, you need to provide the Tool's information and the function to call the Tool. The tool interface definition is as follows:
+ToolsConfig is of type `compose.ToolsNodeConfig`. In Eino, to build a Tool node, you need to provide the Tool's information and the function to invoke the Tool. The tool interface is defined as:
+
```go
type InvokableRun func(ctx context.Context, arguments string, opts ...Option) (content string, err error)
type StreamableRun func(ctx context.Context, arguments string, opts ...Option) (content *schema.StreamReader[string], err error)
@@ -153,20 +156,21 @@ type StreamableTool interface {
}
```
-Users can implement the required tools according to the tool interface definition. The framework also provides a more convenient method to build tools:
+Users can implement the required tools according to the tool interface definition. The framework also provides a simpler way to build tools:
+
```go
userInfoTool := utils.NewTool(
&schema.ToolInfo{
Name: "user_info",
- Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
+ Desc: "Query a user's company, position, and salary information based on their name and email",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"name": {
Type: "string",
- Desc: "用户的姓名",
+ Desc: "The user's name",
},
"email": {
Type: "string",
- Desc: "用户的邮箱",
+ Desc: "The user's email",
},
}),
},
@@ -187,14 +191,14 @@ toolConfig := &compose.ToolsNodeConfig{
### MessageModifier
-MessageModifier is executed before each time all historical messages are passed to ChatModel. The definition is:
+MessageModifier is executed each time before passing all history messages to ChatModel. It is defined as:
```go
// modify the input messages before the model is called.
type MessageModifier func(ctx context.Context, input []*schema.Message) []*schema.Message
```
-Configuring MessageModifier in the Agent can modify the messages passed to the model, commonly used to add a preceding system message:
+Configuring MessageModifier in the Agent allows you to modify the messages passed to the model, commonly used to prepend a system message:
```go
import (
@@ -210,24 +214,24 @@ func main() {
MessageModifier: func(ctx context.Context, input []*schema.Message) []*schema.Message {
res := make([]*schema.Message, 0, len(input)+1)
- res = append(res, schema.SystemMessage("你是一个 golang 开发专家."))
+ res = append(res, schema.SystemMessage("You are a golang development expert."))
res = append(res, input...)
return res
},
})
- agent.Generate(ctx, []*schema.Message{schema.UserMessage("Write a hello world code")})
- // The actual input to the model is:
+ agent.Generate(ctx, []*schema.Message{schema.UserMessage("Write a hello world program")})
+ // The actual input received by the model:
// []*schema.Message{
// {Role: schema.System, Content:"You are a golang development expert."},
- // {Role: schema.Human, Content: "Write a hello world code"}
+ // {Role: schema.Human, Content: "Write a hello world program"}
//}
}
```
### MessageRewriter
-MessageRewriter is executed before each ChatModel call and modifies and updates the historical messages saved in the global state:
+MessageRewriter is executed before each ChatModel call and modifies and updates the history messages stored in the global state:
```go
// MessageRewriter modifies message in the state, before the ChatModel is called.
@@ -238,17 +242,17 @@ MessageRewriter is executed before each ChatModel call and modifies and updates
MessageRewriter MessageModifier
```
-Commonly used for context compression, which is a message change that needs to take effect continuously across multiple ReAct loops.
+Commonly used for context compression and other message changes that need to persist across multiple ReAct loops.
-Compared to MessageModifier (which only changes without persisting, thus suitable for system prompts), MessageRewriter's changes are visible in subsequent ReAct loops.
+Compared to MessageModifier (changes are not persisted, thus suitable for system prompts), MessageRewriter's changes are visible in subsequent ReAct loops.
### MaxStep
-Specify the Agent's maximum running step length. Each transition from one node to the next node counts as one step. The default value is node count + 2.
+Specifies the maximum number of steps the Agent can run. Each transition from one node to the next counts as one step. The default value is the number of nodes + 2.
-Since one loop in the Agent is ChatModel + Tools, which equals 2 steps, the default value of 12 allows up to 6 loops. However, since the last step must be a ChatModel return (because ChatModel must determine that no tool needs to run before returning the final result), at most 5 tools can be run.
+Since one loop in the Agent consists of ChatModel + Tools (2 steps), the default value of 12 allows up to 6 loops. However, since the last step must be a ChatModel return (because the Agent only returns the final result when ChatModel determines no more tools need to be run), it can run at most 5 tool invocations.
-Similarly, if you want to run at most 10 loops (10 ChatModel + 9 Tools), you need to set MaxStep to 20. If you want to run at most 20 loops, MaxStep needs to be 40.
+Similarly, if you want to allow up to 10 loops (10 ChatModel + 9 Tools), set MaxStep to 20. For up to 20 loops, MaxStep should be 40.
```go
func main() {
@@ -262,7 +266,7 @@ func main() {
### ToolReturnDirectly
-If you want the Agent to directly return the Tool's Response ToolMessage after ChatModel selects a specific Tool and executes it, you can configure this Tool in ToolReturnDirectly.
+If you want the Agent to directly return the Tool's Response ToolMessage after ChatModel selects and executes a specific Tool, you can configure that Tool in ToolReturnDirectly.
```go
a, err = NewAgent(ctx, &AgentConfig{
@@ -278,9 +282,9 @@ a, err = NewAgent(ctx, &AgentConfig{
### StreamToolCallChecker
-Different models may output tool calls differently in streaming mode: some models (like OpenAI) output tool calls directly; some models (like Claude) output text first, then output tool calls. Therefore, different methods are needed to determine this. This field is used to specify the function that determines whether the model's streaming output contains tool calls.
+Different models may output tool calls differently in streaming mode: some models (e.g., OpenAI) output tool calls directly; others (e.g., Claude) output text first, then tool calls. Therefore, different methods are needed to determine this. This field specifies the function for checking whether the model's streaming output contains tool calls.
-Optional. If not set, the default checks whether the first "non-empty chunk" contains a tool call:
+This is optional. When not specified, a "non-empty chunk" check is used by default:
```go
func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
@@ -308,9 +312,9 @@ func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[
}
```
-The default implementation is suitable for: models whose Tool Call Message contains only Tool Calls.
+The default implementation above is suitable for: models where Tool Call Messages contain only Tool Calls.
-The default implementation is NOT suitable for: cases where there are non-empty content chunks before the Tool Call output. In such cases, you need to define a custom tool call checker:
+Cases where the default implementation is not suitable: when there are non-empty content chunks before the Tool Call output. In such cases, a custom tool call checker is needed:
```go
toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
@@ -334,12 +338,12 @@ toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Mes
}
```
-The custom StreamToolCallChecker above may need to check **all chunks** for ToolCalls in extreme cases, which can cause the "streaming decision" effect to be lost. To preserve the "streaming decision" effect as much as possible, the recommendation is:
+The custom StreamToolCallChecker above may, in extreme cases, need to check **all chunks** for ToolCalls, potentially losing the "streaming judgment" effect. If you want to preserve the "streaming judgment" effect as much as possible, the recommendation is:
> 💡
-> Try adding a prompt to constrain the model not to output additional text when calling tools, for example: "If you need to call a tool, output the tool directly, do not output text."
+> Try adding a prompt to constrain the model from outputting extra text during tool calls, e.g.: "If you need to call a tool, output the tool directly without any text."
>
-> Different models may be affected differently by prompts, so you need to adjust the prompt and verify the effect in actual use.
+> Different models may respond differently to prompts. In practice, you need to adjust and verify the prompt's effect for your specific model.
## Invocation
@@ -350,7 +354,7 @@ agent, _ := react.NewAgent(...)
var outMessage *schema.Message
outMessage, err = agent.Generate(ctx, []*schema.Message{
- schema.UserMessage("写一个 golang 的 hello world 程序"),
+ schema.UserMessage("Write a golang hello world program"),
})
```
@@ -361,7 +365,7 @@ agent, _ := react.NewAgent(...)
var msgReader *schema.StreamReader[*schema.Message]
msgReader, err = agent.Stream(ctx, []*schema.Message{
- schema.UserMessage("写一个 golang 的 hello world 程序"),
+ schema.UserMessage("Write a golang hello world program"),
})
for {
@@ -383,7 +387,7 @@ for {
### WithCallbacks
-Callback is a callback executed at specific timings during Agent runtime. Since the Agent Graph only has ChatModel and ToolsNode, the Agent's Callback is the Callback for ChatModel and Tool. The react package provides a helper function to help users quickly build Callback Handlers for these two component types.
+Callbacks are executed at specific moments during Agent runtime. Since the Agent Graph only contains ChatModel and ToolsNode, Agent Callbacks are essentially ChatModel and Tool callbacks. The react package provides a helper function to quickly build Callback Handlers for these two component types.
```go
import (
@@ -402,9 +406,9 @@ func BuildAgentCallback(modelHandler *template.ModelCallbackHandler, toolHandler
### Options
-React agent supports dynamic modification through runtime Options.
+React agent supports dynamic modification via runtime Options.
-Scenario 1: Modify the Model configuration in the Agent at runtime:
+Scenario 1: Modify the Agent's Model configuration at runtime:
```go
// WithChatModelOptions returns an agent option that specifies model.Option for the chat model in agent.
@@ -424,7 +428,7 @@ func WithToolList(tools ...tool.BaseTool) agent.AgentOption {
Additionally, you also need to modify the tools bound in ChatModel: `WithChatModelOptions(model.WithTools(...))`
-Scenario 3: Modify the options for a specific Tool at runtime:
+Scenario 3: Modify a specific Tool's options at runtime:
```go
// WithToolOptions returns an agent option that specifies tool.Option for the tools in agent.
@@ -435,11 +439,11 @@ func WithToolOptions(opts ...tool.Option) agent.AgentOption {
### Prompt
-Modifying the prompt at runtime is essentially passing different Message lists when calling Generate or Stream.
+Modifying the prompt at runtime simply means passing different Message lists when calling Generate or Stream.
-### Get Intermediate Results
+### Getting Intermediate Results
-If you want to get the `*schema.Message` generated during the ReAct Agent execution process in real-time, you can first obtain a runtime Option and a MessageFuture through WithMessageFuture:
+If you want to receive `*schema.Message` produced during React Agent execution in real time, you can first obtain a runtime Option and a MessageFuture via WithMessageFuture:
```go
// WithMessageFuture returns an agent option and a MessageFuture interface instance.
@@ -470,14 +474,14 @@ func WithMessageFuture() (agent.AgentOption, MessageFuture) {
}
```
-This runtime Option is passed normally to the Generate or Stream method. The MessageFuture can use GetMessages or GetMessageStreams to get the Messages of various intermediate states.
+Pass this runtime Option to the Generate or Stream method as normal. Use the MessageFuture's GetMessages or GetMessageStreams to retrieve intermediate Messages.
> 💡
-> After passing the MessageFuture Option, the Agent will still run in a blocking manner. Receiving intermediate results through MessageFuture needs to be asynchronous with the Agent running (read MessageFuture in a goroutine or run the Agent in a goroutine).
+> After passing the MessageFuture Option, the Agent still runs in a blocking manner. Receiving intermediate results via MessageFuture must be asynchronous with Agent execution (read MessageFuture in a goroutine, or run the Agent in a goroutine).
## Agent In Graph/Chain
-Agent can be embedded into other Graphs as a Lambda:
+An Agent can be embedded into other Graphs as a Lambda:
```go
agent, _ := NewAgent(ctx, &AgentConfig{
@@ -506,9 +510,9 @@ res, _ := r.Invoke(ctx, []*schema.Message{{Role: schema.User, Content: "hello"}}
## Demo
-### Basic Info
+### Basic Information
-Description: This is a `Food Recommender` with two tools (query_restaurants and query_dishes).
+Description: This is a `Food Recommendation Expert` with two tools (query_restaurants and query_dishes)
Repository: [eino-examples/flow/agent/react](https://github.com/cloudwego/eino-examples/tree/main/flow/agent/react)
@@ -518,14 +522,14 @@ Usage:
2. Provide an `OPENAI_API_KEY`: `export OPENAI_API_KEY=xxxxxxx`
3. Run the demo: `go run flow/agent/react/react.go`
-### Running Process
+### Execution Process
-### Running Process Explanation
+### Execution Process Explanation
-- Simulating user input: `I'm in Haidian District, recommend some dishes for me, need some spicy dishes, recommend at least 2 restaurants`
-- The agent runs the first node `ChatModel`, the LLM determines that a ToolCall needs to be made to query restaurants, with the following parameters:
+- The simulated user input: `I'm in Haidian District, recommend some dishes, need some spicy options, recommend at least 2 restaurants`
+- The agent runs the first node `ChatModel`. The LLM determines it needs to make a ToolCall to query restaurants, with the following parameters:
```json
"function": {
@@ -534,13 +538,13 @@ Usage:
}
```
-- Entering the `Tools` node, calling the query_restaurants tool and getting the result. The result returns information about 2 restaurants in Haidian District:
+- Enters the `Tools` node, calls the restaurant query tool, and gets results returning 2 restaurants in Haidian District:
```json
-[{"id":"1001","name":"Old Place Restaurant","place":"Beijing Old Hutong 5F, turn left to enter","desc":"","score":3},{"id":"1002","name":"Human Taste Restaurant","place":"Beijing Big World Mall -1F","desc":"","score":5}]
+[{"id":"1001","name":"Old Place Restaurant","place":"Beijing Old Hutong 5F, turn left","desc":"","score":3},{"id":"1002","name":"Human Flavor Restaurant","place":"Beijing Grand World Mall -1F","desc":"","score":5}]
```
-- After getting the tool result, the conversation history now contains the tool result. Running `ChatModel` again, the LLM determines that another ToolCall needs to be made to query what dishes the restaurants have. Note that since there are two restaurants, the LLM returns 2 ToolCalls as follows:
+- After getting the tool results, the conversation history now contains the tool results. Running `ChatModel` again, the LLM determines it needs to call another ToolCall to query what dishes the restaurants have. Since there are two restaurants, the LLM returns 2 ToolCalls:
```json
"Message": {
@@ -567,21 +571,21 @@ Usage:
}
```
-- Entering the `Tools` node again. Since there are 2 tool calls, the Tools node executes these two calls concurrently internally, and both are added to the conversation history. From the callback debug logs, you can see the results as follows:
+- Enters the `Tools` node again. Since there are 2 tool calls, the Tools node executes both concurrently, adding both results to the conversation history. From the callback debug logs, the results are:
```json
=========[OnToolStart]=========
{"restaurant_id": "1001", "topn": 5}
=========[OnToolEnd]=========
-[{"name":"Braised Pork","desc":"A piece of braised pork","price":20,"score":8},{"name":"Spring Beef","desc":"Lots of boiled beef","price":50,"score":8},{"name":"Stir-fried Pumpkin","desc":"Mushy stir-fried pumpkin","price":5,"score":5},{"name":"Korean Spicy Cabbage","desc":"This is blessed spicy cabbage, very delicious","price":20,"score":9},{"name":"Hot and Sour Potato Shreds","desc":"Sour and spicy potato shreds","price":10,"score":9}]
+[{"name":"Braised Pork","desc":"A piece of braised pork","price":20,"score":8},{"name":"Spring Beef","desc":"Lots of boiled beef","price":50,"score":8},{"name":"Stir-fried Baby Pumpkin","desc":"Overcooked pumpkin","price":5,"score":5},{"name":"Korean Spicy Kimchi","desc":"This blessed kimchi is really delicious","price":20,"score":9},{"name":"Hot and Sour Shredded Potatoes","desc":"Sour and spicy shredded potatoes","price":10,"score":9}]
=========[OnToolStart]=========
{"restaurant_id": "1002", "topn": 5}
=========[OnToolEnd]=========
-[{"name":"Braised Spare Ribs","desc":"Piece by piece spare ribs","price":43,"score":7},{"name":"Big Knife Twice-cooked Pork","desc":"Classic twice-cooked pork, big pieces of meat","price":40,"score":8},{"name":"Fiery Kiss","desc":"Cold pig snout, spicy but not greasy","price":60,"score":9},{"name":"Chili Mixed with Preserved Egg","desc":"Pounded chili preserved egg, a rice killer","price":15,"score":8}]
+[{"name":"Braised Spare Ribs","desc":"Piece by piece spare ribs","price":43,"score":7},{"name":"Big Knife Twice-cooked Pork","desc":"Classic twice-cooked pork, big pieces","price":40,"score":8},{"name":"Fiery Kiss","desc":"Cold dressed pig snout, spicy but not greasy","price":60,"score":9},{"name":"Chili Pepper with Century Egg","desc":"Smashed pepper century egg, perfect with rice","price":15,"score":8}]
```
-- After getting all the tool call results, entering the `ChatModel` node again. This time the LLM finds that it has all the information needed to answer the user's question, so it integrates the information and outputs the conclusion. Since the `Stream` method was used for the call, the LLM result is returned in a streaming manner.
+- After getting all tool call results, enters the `ChatModel` node again. This time the LLM finds it has all the information needed to answer the user's question. It synthesizes the information and outputs a conclusion. Since the `Stream` method was used, the LLM's result is returned in streaming fashion.
## Related Reading
-- [Eino Tutorial: Host Multi-Agent ](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
+- [Eino Tutorial: Host Multi-Agent](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
diff --git a/content/en/docs/eino/ecosystem_integration/_index.md b/content/en/docs/eino/ecosystem_integration/_index.md
index 7c831883a7e..39e602db695 100644
--- a/content/en/docs/eino/ecosystem_integration/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/_index.md
@@ -1,67 +1,8 @@
---
Description: ""
-date: "2026-01-20"
+date: "2026-05-17"
lastmod: ""
tags: []
-title: 'Eino: Component Integration'
-weight: 6
+title: Component Integration
+weight: 5
---
-
-## Component Integration
-
-### ChatModel
-
-- openai: [OpenAI](/docs/eino/ecosystem_integration/chat_model/agentic_model_openai)
-- ark: [ARK](/docs/eino/ecosystem_integration/chat_model/agentic_model_ark)
-- More components: [ChatModel component list](/docs/eino/ecosystem_integration/chat_model)
-
-### Document
-
-#### Loader
-
-- file: [Loader - local file](/docs/eino/ecosystem_integration/document/loader_local_file)
-- s3: [Loader - amazon s3](/docs/eino/ecosystem_integration/document/loader_amazon_s3)
-- web url: [Loader - web url](/docs/eino/ecosystem_integration/document/loader_web_url)
-
-#### Parser
-
-- html: [Parser - html](/docs/eino/ecosystem_integration/document/parser_html)
-- pdf: [Parser - pdf](/docs/eino/ecosystem_integration/document/parser_pdf)
-
-#### Transformer
-
-- markdown splitter: [Splitter - markdown](/docs/eino/ecosystem_integration/document/splitter_markdown)
-- recursive splitter: [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive)
-- semantic splitter: [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
-
-### Embedding
-
-- ark: [Embedding - ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark)
-- openai: [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai)
-
-### Indexer
-
-- volc vikingdb: [Indexer - volc VikingDB](/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb)
-- Milvus 2.5+: [Indexer - Milvus 2 (v2.5+)](/docs/eino/ecosystem_integration/indexer/indexer_milvusv2)
-- Milvus 2.4: [Indexer - Milvus](/docs/eino/ecosystem_integration/indexer/indexer_milvus)
-- OpenSearch 3: [Indexer - OpenSearch 3](/docs/eino/ecosystem_integration/indexer/indexer_opensearch3)
-- OpenSearch 2: [Indexer - OpenSearch 2](/docs/eino/ecosystem_integration/indexer/indexer_opensearch2)
-- ElasticSearch 9: [Indexer - Elasticsearch 9](/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9)
-- Elasticsearch 8: [Indexer - ES8](/docs/eino/ecosystem_integration/indexer/indexer_es8)
-- ElasticSearch 7: [Indexer - Elasticsearch 7 ](/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7)
-
-### Retriever
-
-- volc vikingdb: [Retriever - volc VikingDB](/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb)
-- Milvus 2.5+: [Retriever - Milvus 2 (v2.5+) ](/docs/eino/ecosystem_integration/retriever/retriever_milvusv2)
-- Milvus 2.4: [Retriever - Milvus](/docs/eino/ecosystem_integration/retriever/retriever_milvus)
-- OpenSearch 3: [Retriever - OpenSearch 3](/docs/eino/ecosystem_integration/retriever/retriever_opensearch3)
-- OpenSearch 2: [Retriever - OpenSearch 2](/docs/eino/ecosystem_integration/retriever/retriever_opensearch2)
-- ElasticSearch 9: [Retriever - Elasticsearch 9](/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9)
-- ElasticSearch 8: [Retriever - ES8](/docs/eino/ecosystem_integration/retriever/retriever_es8)
-- ElasticSearch 7: [Retriever - ES 7](/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7)
-
-### Tools
-
-- googlesearch: [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch)
-- duckduckgo search: [Tool - DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
diff --git a/content/en/docs/eino/overview/eino_adk_quickstart.md b/content/en/docs/eino/overview/eino_adk_quickstart.md
new file mode 100644
index 00000000000..c4134ebb781
--- /dev/null
+++ b/content/en/docs/eino/overview/eino_adk_quickstart.md
@@ -0,0 +1,255 @@
+---
+Description: ""
+date: "2026-05-17"
+lastmod: ""
+tags: []
+title: Get Started with Eino ADK in 5 Minutes
+weight: 9
+---
+
+This guide is for developers already familiar with Eino, focusing on the most important autonomous decision-making primitive in ADK: **ChatModelAgent** and its runtime enhancement mechanism **ChatModelAgentMiddleware**.
+
+## First, Meet ChatModelAgent
+
+When we talk about "Agent," we almost always mean: an entity powered by a large model at its core, equipped with tools, capable of autonomous decision-making and solving complex real-world problems. `ChatModelAgent` is Eino ADK's direct implementation of this concept.
+
+**ChatModelAgent = A ReAct Agent that uses ChatModel as its decision maker, Tools as its action space, and tool feedback plus history as the context for the next decision.**
+
+Four key components:
+
+1. **ChatModel**: The large model, responsible for reasoning and decision-making.
+2. **Tools**: The tool collection, defining the range of actions the Agent can take.
+3. **Feedback**: Tool execution results flow back to the model context, serving as the basis for the next decision.
+4. **History**: Complete preservation of the reasoning traces, tool calls, and tool results during problem-solving.
+
+Therefore, `ChatModelAgent` is not a single model call, but a sustained problem-solving process.
+
+## ChatModelAgent's Execution Structure: ReAct Loop
+
+The core capability of `ChatModelAgent` is **autonomous decision-making** — in a single `Run`, the model can repeatedly reason, act, and receive feedback until the problem is solved. The execution structure supporting this capability is the ReAct Loop.
+
+Autonomous decision-making requires four elements to coexist:
+
+1. **Decision Maker (ChatModel)**: Each iteration judges what to do next based on the current context.
+2. **Action Space (Tools)**: Defines the concrete actions the Agent can take.
+3. **Feedback Signal (Tool Feedback)**: Action results are injected into the context, serving as the basis for subsequent decisions — this allows the Agent to correct course based on actual execution results rather than guessing everything in one shot.
+4. **Accumulated Context (History)**: Complete preservation of reasoning traces, tool calls, and results. Each iteration, the model sees not an isolated query, but the complete problem-solving process from start to current.
+
+All four are indispensable: without a decision maker there's no reasoning, without an action space there's no execution, without feedback there's no correction, without accumulated context there's no better judgment based on history.
+
+
+
+Key characteristic: **Progressive decision-making driven by accumulated context**. Each loop iteration doesn't start from scratch, but continues on top of the complete trace of all prior reasoning and actions. Every model decision is made based on a continuously growing problem-solving context, enabling the Agent to handle complex tasks requiring multi-step reasoning, trial-and-error, and correction.
+
+## What Makes Your ChatModelAgent Different
+
+The structure of the ReAct Loop is fixed. So what makes **your** ChatModelAgent different from others, tailored to your specific problem?
+
+Four dimensions:
+
+1. **ChatModel** — Which model to use for decision-making.
+2. **Instruction** — System instruction: role definition, behavioral constraints, few-shot examples.
+3. **Tools** — Tool collection: determines what the Agent can do.
+4. **Middleware (ChatModelAgentMiddleware)** — Injects behavior at specific lifecycle points in the ReAct Loop: intercepting, modifying, and enhancing inputs and outputs within the loop.
+
+The first three define what the Agent "is" — decision capability, role constraints, action scope.
+
+Middleware defines "how the Agent runs" — it doesn't change the Loop structure (reason → act → feedback remains unchanged), but controls runtime behavior within the loop. For example: compressing context before model calls, dynamically injecting tools before execution, performing permission checks on tool calls, retrying or switching to backup models on failure. These are all runtime enhancements injected at specific points in the Loop.
+
+## Middleware: Injecting Behavior into the ReAct Loop
+
+When building a ChatModelAgent, you'll encounter these typical problems:
+
+- **Agent needs to read/write files, execute commands?** → Need to inject a set of general-purpose tools before execution.
+- **Agent needs to reuse predefined instructions and knowledge?** → Need to package reusable capabilities as Skills, loaded on demand.
+- **Context keeps growing, exceeding model window?** → Need to automatically compress history before each model call.
+- **Too many tools, putting them all in the prompt dilutes attention?** → Need to search and load tools on demand.
+- **Model occasionally fails or returns garbage?** → Need automatic retry or switch to backup model.
+
+The common thread: they don't need to change the ReAct Loop structure, only intercept and enhance at specific points in the loop. That's what Middleware does.
+
+Corresponding built-in Middlewares:
+
+| Scenario | Middleware | What It Does |
| Need filesystem capabilities | FileSystem | Injects ls/read/write/edit/grep/execute tools before execution |
| Reuse predefined capabilities | Skill | Packages instructions, knowledge, tools as skill units loadable on demand |
| Context exceeds window | Reduction / Summarization | Compresses messages and tool results before model call |
| Too many tools | ToolSearch | Searches and loads Tools on demand rather than exposing all at once |
| Unstable model calls | ModelRetry / ModelFailover | Retry / failover at the individual model call level |
+
+Hook point summary:
+
+| Hook Point | Timing | Typical Use |
BeforeAgent | Before Agent runs (once only) | Enhance Instruction, inject Tools |
BeforeModelRewriteState | Before each model call | Modify Messages / ToolInfos |
AfterModelRewriteState | After each model call | Modify model response or patch state |
WrapModel | Individual model call level | Retry, failover, rewrite model output |
WrapToolCall | Individual tool call level | Permissions, safety, output rewriting |
AfterAgent | After Agent succeeds | Post-processing, state cleanup |
+
+## Appendix: Middleware Quick Reference
+
+### Instance Overview
+
+| Middleware | Description |
| Reduction | Truncates overly long tool outputs / writes to filesystem to prevent token overflow |
| Summarization | Compresses history messages via summarization |
| Skill | Reusable instructions/knowledge exposed as Tools, loaded on demand by Agent |
| FileSystem | ls/read/write/edit/glob/grep/execute file operation toolset |
| ToolSearch | tool_searchmeta-tool for on-demand tool discovery (reduces resident tool list size) |
| PatchToolCall | Patches dangling tool calls in message history (missing tool results) |
| SafeTool | WrapToolCall-level interception of tool execution errors, converted to readable text for the model so Agent can self-correct instead of crashing |
| ModelRetry | Retries failed model calls with configurable strategy [built-in config] |
| ModelFailover | Switches to backup model on failure [built-in config] |
| AgentsMD | Injects Agents.md knowledge files into model context to improve context quality |
| PlanTask | Persistent task management toolset (create/get/update/list) with dependency tracking |
| WriteTodos | Lightweight TODO list tool; Agent can create and track structured to-do items [DeepAgent built-in] |
| TaskTool | Sub-Agent delegation tool; main Agent delegates subtasks to sub-Agents for independent execution [DeepAgent built-in] |
| Permission | Tool call permission control [WIP] |
| Category | Problem Solved | Includes |
| Extend General Tools | Give Agent more capabilities | FileSystem, Skill, ToolSearch, PlanTask, WriteTodos, TaskTool |
| Handle Errors During ReAct | Improve reliability | ModelRetry, ModelFailover, SafeTool, PatchToolCall |
| Keep Context Within Window Limit | Prevent token overflow | Reduction, Summarization, ToolSearch |
| Safety and Permissions | Constrain Agent behavior | Permission |
| Improve Context Content Quality | Give model better context | Skill, AgentsMD |
This seemingly simple screenshot represents two forms of "AI applications":
-- The "Agent" represented by the "chat box". **Agents use LLM (Large Language Model) as the decision center, autonomously plan and can conduct multi-turn interactions**, naturally suited for handling open-ended, continuous tasks, manifesting as a "dialogue" form.
-- The "Graph" represented by "buttons" or "APIs". For example, the "Recording Summary" button above - the Graph behind it is roughly "Recording" → "LLM understands and summarizes" → "Save recording" - this kind of fixed process. **The core of Graph lies in the determinism of its process and the closure of tasks**, completing specific goals through predefined nodes and edges, manifesting as a "function" form. For example, video generation is an "API" form AI application:
+- The "Agent" represented by the "chat box." **An Agent uses an LLM (Large Language Model) as its decision center, autonomously plans, and can conduct multi-turn interactions**, naturally suited for open-ended, ongoing tasks, manifesting as a "conversation" form.
+- The "Graph" represented by "buttons" or "APIs." For example, a "Meeting Summary" button above, whose underlying Graph roughly follows a fixed flow of "recording" → "LLM understanding and summarization" → "save recording." **The core of a Graph lies in the determinism of its flow and the bounded nature of its tasks**, completing specific goals through predefined nodes and edges, manifesting as a "feature" form. For example, video generation is an "API-form" AI application:
@@ -39,8 +39,8 @@ flowchart TD
G2("Deterministic Output")
S --> D
- D -->|"Open or Uncertain"| A
- D -->|"Closed and Deterministic"| G
+ D -->|"Open-ended or uncertain"| A
+ D -->|"Bounded and deterministic"| G
A --> A1
A --> A2
G --> G1
@@ -51,29 +51,29 @@ flowchart TD
class A,G,A1,A2,G1,G2 process_style
```
-This article explores in detail the differences and connections between Agent and Graph, two forms of AI applications, proposes that "the best integration point is to encapsulate Graph as Agent's Tool", and provides recommended usage patterns for [Eino](https://github.com/cloudwego/eino) developers.
+This article explores in detail the differences and connections between the Agent and Graph forms of AI applications, proposes that "the best integration point for both is to wrap a Graph as an Agent's Tool," and provides recommended usage patterns for [Eino](https://github.com/cloudwego/eino) developers.
## Core Concept Analysis
### Basic Definitions
-- **Graph**: A flowchart **predefined** by developers with a clear topology. Its nodes can be code functions, API calls, or LLMs, and inputs and outputs are typically structured. **The core characteristic is "determinism"** - given the same input, the execution path and final output are predictable.
-- **Agent**: An entity centered on LLM that can **autonomously plan, decide, and execute** tasks. It completes goals through **dynamic interaction** with the environment (Tools, users, other Agents), and its behavior is uncertain. **The core characteristic is "autonomy"**.
-- **Tool**: Any external capability that an Agent can call, typically a **function or API that encapsulates specific functionality**. Tools themselves can be synchronous or asynchronous, stateful or stateless. They are only responsible for execution and do not have autonomous decision-making capabilities.
-- **Orchestration**: The process of **organizing and coordinating multiple compute units (nodes, Agents) to work together**. In this article, it specifically refers to predefining static processes through Graphs.
+- **Graph**: A flowchart **predefined** by developers with a clear topological structure. Its nodes can be code functions, API calls, or LLMs, and inputs/outputs are typically structured. **The core characteristic is "determinism"**—given the same input, the execution path and final output are predictable.
+- **Agent**: An entity with an LLM at its core that can **autonomously plan, make decisions, and execute** tasks. It accomplishes goals through **dynamic interaction** with the environment (Tools, users, other Agents), and its behavior is non-deterministic. **The core characteristic is "autonomy."**
+- **Tool**: Any external capability that an Agent can call, typically a **function or API that encapsulates specific functionality**. A Tool can be synchronous or asynchronous, stateful or stateless. It is only responsible for execution and does not possess autonomous decision-making capability.
+- **Orchestration**: The process of **organizing and coordinating multiple computational units (nodes, Agents) to work together**. In this article, it specifically refers to predefining static flows through Graph.
-### Deep Comparison
+### In-depth Comparison
| Feature Dimension | Agent | Graph |
| Core Driver | LLM Autonomous Decision | Developer Preset Process |
| Dimension | Agent | Graph |
| Core Driver | LLM autonomous decision-making | Developer-preset flow |
| Input | Unstructured natural language, images, etc. | Structured data |
| Deliverable | Process and result equally important | Focused on final result |
| State Management | Long-term, cross-execution | Single execution, stateless |
| Runtime Mode | Tends toward asynchronous | Tends toward synchronous |
| Deliverable | Process and result equally important | Focus on final result |
| State Management | Long-duration, cross-execution | Single execution, stateless |
| Execution Mode | Tends toward asynchronous | Tends toward synchronous |
| Feature Dimension | Graph | Tool |
| Dimension | Graph | Tool |
| Input | Structured data | Structured data |
| Deliverable | Focused on final result | Focused on final result |
| Deliverable | Focus on final result | Focus on final result |
| State Management | Single execution, stateless | Single execution, stateless |
| Runtime Mode | Synchronous as a whole | Tool is synchronous from LLM's perspective |
| Execution Mode | Synchronous overall | From LLM's perspective, Tool is synchronous |
| Chapter | Topic | Entry |
| Chapter 1 | ChatModel and Message (Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch01_chatmodel_agent_console.md |
| Chapter 1 | ChatModel and AgenticMessage (Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch01_chatmodel_agent_console.md |
| Chapter 2 | Agent and Runner (Console multi-turn) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch02_chatmodel_agent_runner_console.md |
| Chapter 3 | Memory and Session (persistent conversation) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch03_memory_session_jsonl.md |
| Chapter 4 | Tools and file system access | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch04_tool_backend_filesystem.md |
| Chapter 5 | Middleware pattern | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch05_middleware.md |
| Chapter 4 | Tool and file system access | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch04_tool_backend_filesystem.md |
| Chapter 5 | Middleware (middleware pattern) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch05_middleware.md |
| Chapter 6 | Callback and Trace (observability) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch06_callback.md |
| Chapter 7 | Interrupt/Resume | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch07_interrupt_resume.md |
| Chapter 8 | Graph Tool (complex workflows) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch08_graph_tool.md |
| Chapter 9 | Skill (Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch09_skill.md |
| Final | A2UI (Web) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md |
| Chapter 10 | A2UI (Web) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md |
| Chapter 11 | TurnLoop | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch11_turnloop.md | |
| Chapter | Topic | Core content | Capability gain |
| Chapter 1 | ChatModel and Message | Understand the Component abstraction and implement a single-turn chat | Basic conversation |
| Chapter 2 | Agent and Runner | Introduce execution abstractions and implement multi-turn chat | Session management |
| Chapter 3 | Memory and Session | Persist chat history and support session recovery | Persistence |
| Chapter 4 | Tools and file system | Add file access to read source code | Tool calling |
| Chapter 5 | Middleware | Middleware mechanism and unified cross-cutting concerns | Extensibility |
| Chapter 6 | Callback | Callbacks to observe the Agent execution process | Observability |
| Chapter 7 | Interrupt and Resume | Interrupt and resume to support long-running tasks | Reliability |
| Chapter 8 | Graph and Tool | Use Graph to orchestrate complex workflows | Complex orchestration |
| Chapter 9 | A2UI | Integration from Agent to UI | Production-grade delivery |
| Chapter | Topic | Core Content | Capability Gained |
| Chapter 1 | ChatModel and AgenticMessage | Understand Component abstraction, implement a single conversation | Basic conversation |
| Chapter 2 | Agent and Runner | Introduce execution abstraction, implement multi-turn conversation | Session management |
| Chapter 3 | Memory and Session | Persist conversation history, support session resumption | Persistence |
| Chapter 4 | Tool and file system | Add file access capability, read source code | Tool invocation |
| Chapter 5 | Middleware | Middleware mechanism for handling cross-cutting concerns uniformly | Enhanced extensibility |
| Chapter 6 | Callback | Callback mechanism for monitoring Agent execution | Observability |
| Chapter 7 | Interrupt and Resume | Interrupt and resume support for long-running tasks | Enhanced reliability |
| Chapter 8 | Graph and Tool | Orchestrate complex workflows using Graph | Complex orchestration |
| Chapter 9 | Skill | Load and reuse skill documents via Skill middleware | Knowledge reuse |
| Final chapter | A2UI | Agent-to-UI integration solution | Production-grade application |
| Dimension | ChatModel | ChatModelAgent |
| Positioning | Component | Agent (intelligent agent) |
| Core interface | Generate()/ Stream() | Run() -> AsyncIterator[*AgentEvent] |
| Output form | Directly returns message content | Returns an event stream (containing messages, control actions, etc.) |
| Core capabilities | Pure large language model invocation | Supports extending with tools, middleware, interrupt, and more |
| Use case | Simple conversational interaction scenarios | Complex intelligent agent application development |
| Capability | ChatModelAgent | DeepAgent |
| Multi-turn conversation | ✅ | ✅ |
| Add custom Tools | ✅ Manually register each Tool | ✅ Manual or automatic registration |
| File system access (Backend) | ❌ Must manually create and register all file tools | ✅ First-class config, auto-registered |
| Command execution (StreamingShell) | ❌ Must manually create | ✅ First-class config, auto-registered |
| Built-in task management | ❌ | ✅ write_todos tool |
| Sub-Agent support | ❌ | ✅ |
| Middleware | Description |
| reduction | Tool output reduction—automatically truncates and offloads to file system when tool output is too long, preventing context overflow |
| summarization | Automatic conversation history summarization—generates summaries to compress history when token count exceeds threshold |
| skill | Skill loading middleware—enables the Agent to dynamically load and execute predefined skills |
| Timing Constant | Handler Method | Trigger Point | Input / Output |
| TimingOnStart | OnStart | Before component starts processing | CallbackInput |
| TimingOnEnd | OnEnd | After component returns successfully | CallbackOutput |
| TimingOnError | OnError | When component returns an error | error |
| TimingOnStartWithStreamInput | OnStartWithStreamInput | When component receives streaming input | StreamReader[CallbackInput] |
| TimingOnEndWithStreamOutput | OnEndWithStreamOutput | When component returns streaming output | StreamReader[CallbackOutput] |
| Capability | Ch10 (Runner, single-turn) | Ch11 (TurnLoop, multi-turn) |
| Streaming output | ✅ | ✅ |
| Approval / interrupt | ✅ | ✅ |
| Cross-turn persistence, real-time response to new input | ❌ Each Run() is independent | ✅ Push() at any time |
| Preempt an ongoing response | ❌ | ✅ Push(item, WithPreempt(...)) |
| Abort Agent | ❌ | ✅ loop.Stop(WithImmediate()) |
| Flexible per-turn input construction | ❌ Business layer assembles manually | ✅ GenInput callback |
| Mode | Specific Behavior |
| AfterToolCalls | Waits for currently executing tool calls to complete, then cancels the current turn and starts a new one |
| AfterChatModel | Waits for the current model call to complete, then cancels the current turn and starts a new one |
| AnySafePoint | Cancels at any safe point (e.g., between tool calls, between model calls) and immediately starts a new turn |
| Mode | Specific Behavior |
| loop.Stop() | Turn boundary exit: waits for the current turn to complete then exits |
| loop.Stop(WithImmediate()) | Immediate exit: cancels the current turn's context |
| loop.Stop(WithGraceful()) | Safe point exit: exits at the next safe point (e.g., between tool calls) |
| Callback | When Called | Responsibility |
| GenInput | When items are in the queue | Select which items to consume, build Agent input (can decide which items to keep for next turn) |
| PrepareAgent | After GenInput | Return the Agent instance for this turn, supports dynamic Agent configuration adjustment |
| OnAgentEvents | When Agent produces event stream | Consume events, render output, persist results — the core entry point for business-layer Agent output handling |
| GenResume | When resuming from checkpoint | Extract approval results from newly Pushed items, build ResumeParamsto automate approval recovery |
| Store + CheckpointID | — | Enable declarative checkpoint; TurnLoop automatically handles execution state save and restore |
| @@ -28,9 +25,9 @@ Agent-level Callback support |
| Phase | Trigger Time | Effect |
| Truncation | After tool returns | Truncate overlong output, save to file |
| Clear | Before model invocation | Clear historical tool results, free up tokens |
| Phase | Trigger | Effect |
| Truncation | After tool returns | Truncates overlong output, saves to file |
| Clear | Before model call | Clears historical tool results, frees tokens |
| Type | Change Item |
| Type | Change |
| API Change | ShellBackend→ Shellinterface rename |
| Behavior Change | AgentEventsending mechanism changed to Middleware |
| Behavior Change | ReadRequest.Offsetchanged from 0-based to 1-based |
| Behavior Change | FileInfo.Pathno longer guaranteed to be absolute path |
| Behavior Change | WriteRequestchanged from error on file exists to overwrite |
| Behavior Change | WriteRequestchanged from error on existing file to overwrite |
| Behavior Change | GrepRequest.Patternchanged from literal to regular expression |
| 目录 | 名称 | 说明 |
| adk/agent/ralph-loop | Ralph Loop | 自主迭代模式:外部 for循环配合 Runner.Run实现单轮迭代,Agent 通过文件系统感知先前工作,验证门控检查 BUG 标记后才接受完成承诺 |
| 目录 | 名称 | 说明 |
| adk/cancel/graceful-exit | Graceful Exit | 演示 Agent Cancel + Resume:捕获终端信号后以 CancelAfterChatModel+ WithRecursive模式取消嵌套 Agent,等待安全点保存 Checkpoint,然后恢复继续执行 |
| 目录 | 名称 | 说明 |
| adk/middlewares/skill | Skill 中间件 | 从文件系统加载 Agent 技能(如 log_analyzer),展示技能中间件的使用方式 |
| quickstart/chat | Chat 快速开始 | 最基础的 LLM 对话示例,包含模板、生成、流式输出 |
| quickstart/eino_assistant | Eino 助手 | 完整的 RAG 应用示例,包含知识索引、Agent 服务、Web 界面 |
| quickstart/todoagent | Todo Agent | 简单的 Todo 管理 Agent 示例 |
| quickstart/chatwitheino | Chat with Eino (教程) | 9 章渐进式教程,从 ChatModel → Runner → Session → Tool → Middleware → Callback → Interrupt → GraphTool → Skill,逐步构建完整 Agent |
| 问题 | 机制 | 配置位置 |
| 参数 JSON 不合法 | ToolArgumentsHandler | ToolsNodeConfig/ ToolsConfig |
| 调用不存在的工具 | UnknownToolsHandler | ToolsNodeConfig/ ToolsConfig |
| 工具名/参数名变化 | ToolAliases | ToolsNodeConfig/ ToolsConfig |
| 工具执行报错需自动纠错 | Middleware 错误转换 | ADK Handlers或 ToolCallMiddlewares |
| markdown | README_zh.md | README.md |
| recursive | README_zh.md | README.md |
| semantic | README_zh.md | README.md |
| 字段 | 类型 | 必填 | 说明 |
| PatchedContentGenerator | func(ctx, toolName, toolCallID string) (string, error) | 否 | 自定义生成占位符消息内容的函数。参数包含工具名和调用 ID,返回要填充的内容 |
| PatchedContentGenerator | func(ctx context.Context, toolName, toolCallID string) (string, error) | 否 | 自定义生成占位符消息内容的函数。未设置时使用内置默认消息模板 |
-
-**处理逻辑:**
-
-1. 在 `BeforeModelRewriteState` 钩子中执行
-2. 遍历所有消息,查找包含 `ToolCalls` 的 Assistant 消息
-3. 对于每个 ToolCall,检查后续消息中是否存在对应的 Tool 消息(通过 `ToolCallID` 匹配)
-4. 如果找不到对应的 Tool 消息,则插入一个占位符消息
-5. 返回修复后的消息列表
+> 💡
+> 对于 `*schema.Message`,通过 `msg.Role == schema.Tool && msg.ToolCallID` 匹配;对于 `*schema.AgenticMessage`,通过 `ContentBlock.FunctionToolResult.CallID` 匹配。
-## 示例场景
+### 示例场景
-### 修复前的消息历史
+**修复前:**
```
-[User] "帮我查询天气"
-[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
-[Tool] "call_1: 晴天,25°C"
-[User] "不用查位置了,直接告诉我北京的天气" <- 用户中断
+[User] "帮我查询天气"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: 晴天,25°C"
+[User] "不用查位置了,直接告诉我北京的天气" <- 用户中断
```
-### 修复后的消息历史
+**修复后:**
```
-[User] "帮我查询天气"
-[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
-[Tool] "call_1: 晴天,25°C"
-[Tool] "call_2: 工具调用 get_location(ID 为 call_2)已被取消..." <- 自动插入
-[User] "不用查位置了,直接告诉我北京的天气"
+[User] "帮我查询天气"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: 晴天,25°C"
+[Tool] "call_2: 工具调用 get_location(ID 为 call_2)已被取消..." <- 自动插入
+[User] "不用查位置了,直接告诉我北京的天气"
```
## 多语言支持
@@ -153,7 +172,8 @@ adk.SetLanguage(adk.LanguageEnglish) // 英文(默认)
## 注意事项
> 💡
-> 此中间件仅在 `BeforeModelRewriteState` 钩子中修改本次运行的历史消息,不会影响实际存储的消息历史。修复只是临时的,仅用于本轮 agent 调用。
+> `BeforeModelRewriteState` 返回的 state 会被框架持久化到 agent 内部状态(参见 `wrappers.go` 中的 `ProcessState` 调用)。因此 PatchToolCalls 插入的占位符消息**会保留在后续迭代中**,不需要每轮重复修补。
- 建议将此中间件放在中间件链的**前面**,确保其他中间件处理的是完整的消息历史
-- 如果你的场景需要持久化修复后的消息,请在 `PatchedContentGenerator` 中实现相应逻辑
+- `cfg` 参数可传 `nil`,等价于 `&Config{}`
+- 如果消息列表为空(`len(state.Messages) == 0`),中间件直接返回,不做任何处理
diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
index daf5662c286..1622e947a52 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
@@ -1,33 +1,28 @@
---
Description: ""
-date: "2026-03-09"
+date: "2026-05-17"
lastmod: ""
tags: []
title: PlanTask
weight: 6
---
-# PlanTask 中间件
-
-adk/middlewares/plantask
-
> 💡
-> 本中间件在 v0.8.0 版本引入。
+> 本中间件在 v0.8.0 版本引入。包路径:`github.com/cloudwego/eino/adk/middlewares/plantask`
## 概述
-`plantask` 是一个任务管理中间件,让 Agent 可以创建和管理任务列表。中间件通过 `BeforeAgent` 钩子注入四个工具:
-
-- **TaskCreate**: 创建任务
-- **TaskGet**: 查看任务详情
-- **TaskUpdate**: 更新任务
-- **TaskList**: 列出所有任务
+`plantask` 是一个任务管理中间件,通过 `BeforeAgent` 钩子向 Agent 注入四个工具,使其具备结构化任务规划能力:
-主要用途:
+| 工具 | 功能 |
TaskCreate | 创建任务 |
TaskGet | 获取单个任务详情 |
TaskUpdate | 更新任务状态/字段、设置依赖、删除任务 |
TaskList | 列出所有任务摘要 |
| 状态 | 说明 |
pending | 待处理(默认) |
| 状态值 | 说明 |
pending | 待处理(创建时默认) |
in_progress | 进行中 |
completed | 已完成 |
deleted | 删除(会删掉文件) |
deleted | 删除(物理删除 JSON 文件,并从其他任务的依赖列表中移除) |
| 参数 | 类型 | 必填 | 说明 |
subject | string | 是 | 标题 |
description | string | 是 | 描述 |
activeForm | string | 否 | 进行时文案,比如"正在运行测试" |
metadata | object | 否 | 自定义数据 |
subject | string | 是 | 任务标题(祈使句形式) |
description | string | 是 | 任务详细描述,包含上下文和验收标准 |
activeForm | string | 否 | 进行时文案(如"正在运行测试"),in_progress 时展示给用户 |
metadata | object | 否 | 自定义键值对 |
| 参数 | 类型 | 必填 | 说明 |
taskId | string | 是 | 任务 ID |
taskId | string | 是 | 任务 ID(纯数字字符串) |
| 参数 | 类型 | 必填 | 说明 |
taskId | string | 是 | 任务 ID |
subject | string | 否 | 新标题 |
description | string | 否 | 新描述 |
activeForm | string | 否 | 新的进行时文案 |
status | string | 否 | 新状态 |
addBlocks | []string | 否 | 添加被阻塞的任务 |
addBlockedBy | []string | 否 | 添加阻塞自己的任务 |
owner | string | 否 | 负责 agent |
metadata | object | 否 | 自定义数据(设 null 删除) |
activeForm | string | 否 | 新进行时文案 |
status | string | 否 | 新状态,enum: pending/ in_progress/ completed/ deleted |
addBlocks | []string | 否 | 添加被当前任务阻塞的任务 ID(双向写入) |
addBlockedBy | []string | 否 | 添加阻塞当前任务的任务 ID(双向写入) |
owner | string | 否 | 负责的 agent 名称 |
metadata | object | 否 | 合并到现有 metadata;设 key 为 null 则删除该 key |
| 机制 | 说明 |
| ID 分配 | .highwatermark文件存储当前最大 ID,创建时 +1 |
| 并发安全 | 四个工具共享同一 sync.Mutex,同一 middleware 实例串行执行 |
| 文件格式 | 每个任务一个 {id}.json 文件,JSON 序列化使用 sonic |
| 自动清理 | TaskUpdate 将任务标记为 completed 后检查——若所有任务均 completed 则批量删除 |
| ID 校验 | 纯数字正则 ^\d+$ |
| 删除级联 | 删除任务时遍历所有任务文件,移除对该 ID 的引用 |
+
+1. **发现(Discovery)**:Agent 仅加载每个可用 Skill 的 name 和 description,足以判断何时可能需要使用该 Skill
+2. **激活(Activation)**:当任务匹配某个 Skill 时,Agent 将完整的 `SKILL.md` 内容读入上下文
+3. **执行(Execution)**:Agent 遵循指令执行任务,按需加载其他文件或执行捆绑代码
> 💡
> Ref: [https://agentskills.io/home](https://agentskills.io/home)
@@ -34,7 +36,7 @@ Skill 使用**渐进式展示(Progressive Disclosure)**来高效管理上下
## FrontMatter
-Skill 的元数据结构,用于在发现阶段快速展示 Skill 信息,避免加载完整内容:
+Skill 的元数据结构,从 SKILL.md 的 YAML frontmatter 中解析。用于在发现阶段快速展示 Skill 信息:
```go
type FrontMatter struct {
@@ -48,14 +50,14 @@ type FrontMatter struct {
| 字段 | 类型 | 说明 |
Name | string | Skill 的唯一标识符。Agent 通过此名称调用 Skill ,建议使用简短、有意义的名称(如 pdf-processing、 web-research)。对应 SKILL.md 中 frontmatter 的 name字段 |
Description | string | Skill 的功能描述。这是 Agent 判断是否使用该 Skill 的关键依据,应清晰说明技 Skill 能适用的场景和能力。对应 SKILL.md 中 frontmatter 的 description字段 |
Context | ContextMode | 上下文模式。可选值:fork_with_context(复制历史消息创建新 Agent 执行)、 fork(隔离上下文创建新 Agent 执行)。留空表示内联模式(直接返回 Skill 内容) |
Agent | string | 指定使用的 Agent 名称。配合 Context字段使用,通过 AgentHub获取对应的 Agent 工厂函数。留空时使用默认 Agent |
Model | string | 指定使用的模型名称。通过 ModelHub获取对应的模型实例。在 Context 模式下传递给 Agent 工厂;在内联模式下切换后续 ChatModel 调用使用的模型 |
Name | string | Skill 的唯一标识符。建议使用简短、有意义的名称(如 pdf-processing、 web-research) |
Description | string | Skill 的功能描述。Agent 判断是否使用该 Skill 的关键依据,应清晰说明适用场景和能力 |
Context | ContextMode | 上下文模式。可选值:fork(隔离上下文)、 fork_with_context(复制历史消息)。留空表示内联模式 |
Agent | string | 指定使用的 Agent 名称,配合 Context使用,通过 AgentHub获取对应 Agent。留空使用默认 Agent |
Model | string | 指定使用的模型名称,通过 ModelHub获取对应模型实例 |
| 模式 | 说明 |
| 内联(默认) | Skill 内容直接作为工具结果返回,由当前 Agent 继续处理 |
| ForkWithContext | 创建新 Agent,复制当前对话历史,独立执行 Skill 任务后返回结果 |
| Fork | 创建新 Agent,使用隔离的上下文(仅包含 Skill 内容),独立执行后返回结果 |
fork_with_context | 创建新 Agent,复制当前对话历史,独立执行 Skill 任务后返回结果 |
fork | 创建新 Agent,使用隔离上下文(仅包含 Skill 内容),独立执行后返回结果 |
| 字段 | 类型 | 说明 |
FrontMatter | FrontMatter | 嵌入的元数据结构,包含 Name、 Description、 Context、 Agent、 Model |
Content | string | SKILL.md 文件中 frontmatter 之后的正文内容。包含 Skill 的详细指令、工作流程、示例等,Agent 激活 Skill 后会读取此内容 |
BaseDirectory | string | Skill 目录的绝对路径。Agent 可以使用此路径访问 Skill 目录中的其他资源文件(如脚本、模板、参考文档等) |
FrontMatter | FrontMatter | 嵌入的元数据结构 |
Content | string | SKILL.md 中 frontmatter 之后的正文内容,包含详细指令、工作流程、示例等 |
BaseDirectory | string | Skill 目录的绝对路径,Agent 可用此路径访问目录中的其他资源文件 |
| 方法 | 说明 |
List | 列出所有可用技能的元数据。在 Agent 启动时调用,用于构建技能工具的描述信息,让 Agent 知道有哪些技能可用 |
Get | 根据名称获取完整的技能内容。当 Agent 决定使用某个技能时调用,返回包含详细指令的完整 Skill 结构 |
List | 列出所有可用技能的元数据。Agent 启动时调用,用于构建技能工具的描述 |
Get | 根据名称获取完整的技能内容。Agent 决定使用某个技能时调用 |
| 字段 | 类型 | 必需 | 说明 |
| Backend | filesystem.Backend | 是 | 文件系统后端实现,用于文件操作 |
| BaseDir | string | 是 | 技能根目录的路径。会扫描此目录下的所有一级子目录,查找包含 SKILL.md文件的目录作为技能 |
Backend | filesystem.Backend | 是 | 文件系统后端实现,用于文件操作 |
BaseDir | string | 是 | 技能根目录路径。扫描此目录下的一级子目录,查找包含 SKILL.md文件的目录 |
| 字段 | 类型 | 必需 | 默认值 | 说明 |
Backend | Backend | 是 | 技能后端实现。负责技能的存储和检索,可使用内置的 LocalBackend或自定义实现 | |
SkillToolName | *string | 否 | "skill" | 技能工具的名称。Agent 通过此名称调用技能工具。如果你的 Agent 已有同名工具,可以通过此字段自定义名称避免冲突 |
AgentHub | AgentHub | 否 | 提供 Agent 工厂函数。当 Skill 使用 context: fork或 context: isolate时必填 | |
ModelHub | ModelHub | 否 | 提供模型实例。当 Skill 指定 model字段时使用 | |
CustomSystemPrompt | SystemPromptFunc | 否 | 内置提示词 | 自定义系统提示词函数 |
CustomToolDescription | ToolDescriptionFunc | 否 | 内置描述 | 自定义工具描述函数 |
Backend | Backend | 是 | - | 技能后端实现,负责技能的存储和检索 |
SkillToolName | *string | 否 | "skill" | 技能工具名称。如已有同名工具,可自定义避免冲突 |
AgentHub | TypedAgentHub[M] | 否 | - | 提供 Agent 实例。使用 context: fork或 fork_with_context时必填 |
ModelHub | TypedModelHub[M] | 否 | - | 提供模型实例。Context 模式下传给 AgentHub;内联模式下通过 WrapModel 切换后续 ChatModel 调用的模型 |
CustomSystemPrompt | SystemPromptFunc | 否 | 内置提示词 | 自定义系统提示词。签名:func(ctx, toolName) string |
CustomToolDescription | ToolDescriptionFunc | 否 | 内置描述 | 自定义工具描述。签名:func(ctx, skills []FrontMatter) string |
CustomToolParams | func | 否 | 仅 skill参数 | 自定义工具参数 schema。接收默认参数,返回自定义参数,始终保留 skill为必填 |
BuildContent | func | 否 | 默认格式化 | 自定义 Skill 内容生成,可在内容中注入额外上下文 |
BuildForkMessages | func | 否 | 见下文 | 自定义 fork 模式下传给子 Agent 的初始消息。默认:fork→ [UserMessage(content)], fork_with_context→ [history..., ToolMessage(content, callID)] |
FormatForkResult | func | 否 | 拼接内容 | 自定义子 Agent 结果格式化。默认将 assistant message 内容拼接后返回 |
-
-> 💡
-> Skill Middleware 仅提供了如上图所示的加载 SKILL.md 能力,如果 Skill 需要 agent 具备读取文件、执行脚本等能力,需要用户另外为 agent 配置。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
index f59fe4dbc17..e8b5fce7d83 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
@@ -1,210 +1,343 @@
---
Description: ""
-date: "2026-03-09"
+date: "2026-05-17"
lastmod: ""
tags: []
title: Summarization
weight: 4
---
+> 💡
+> 本中间件在 v0.8.0 版本引入。包路径:`github.com/cloudwego/eino/adk/middlewares/summarization`
+
## 概述
-Summarization 中间件会在对话的 token 数量超过配置阈值时,自动压缩对话历史。这有助于在长对话中保持上下文连续性,同时控制在模型的 token 限制范围内。
+Summarization 中间件在对话 token 数超过阈值时自动调用摘要模型压缩对话历史,使长对话在模型上下文窗口内保持连贯。中间件挂载在 `BeforeModelRewriteState` 钩子上,每轮模型调用前检查触发条件,触发后执行:计数 → 摘要生成(含重试/降级)→ 后处理 → 替换 state。
-> 💡
-> 本中间件在 v0.8.0 版本引入。
+## 泛型体系
-## 快速开始
+本包全部核心类型和函数均提供 **Typed 泛型版本**(`M adk.MessageType`)与 **非泛型别名**(固定为 `*schema.Message`)。
-```go
-import (
- "context"
- "github.com/cloudwego/eino/adk/middlewares/summarization"
-)
+| 泛型版本 | 非泛型别名(= Typed\[*schema.Message\]) |
TypedConfig[M] | Config |
NewTyped[M](ctx, *TypedConfig[M]) | New(ctx, *Config) |
TypedTokenCounterFunc[M] | TokenCounterFunc |
TypedGenModelInputFunc[M] | GenModelInputFunc |
TypedGetFailoverModelFunc[M] | GetFailoverModelFunc |
TypedFinalizeFunc[M] | FinalizeFunc |
TypedCallbackFunc[M] | CallbackFunc |
TypedUserMessageFilterFunc[M] | UserMessageFilterFunc |
TypedPreserveUserMessages[M] | PreserveUserMessages |
TypedRetryConfig[M] | RetryConfig |
TypedFailoverConfig[M] | FailoverConfig |
TypedFailoverContext[M] | FailoverContext |
TypedFinalizerBuilder[M] | FinalizerBuilder |
| 字段 | 类型 | 必填 | 默认值 | 说明 |
| Model | model.BaseChatModel | 是 | 用于生成摘要的聊天模型 | |
| ModelOptions | []model.Option | 否 | 传递给模型生成摘要时的选项 | |
| TokenCounter | TokenCounterFunc | 否 | 约 4 字符/token | 自定义 token 计数函数 |
| Trigger | *TriggerCondition | 否 | 190,000 tokens | 触发摘要的条件 |
| UserInstruction | string | 否 | 内置 prompt | 自定义摘要指令 |
| TranscriptFilePath | string | 否 | 完整对话记录文件路径 | |
| GenModelInput | GenModelInputFunc | 否 | 自定义摘要模型输入的预处理函数 | |
| Finalize | FinalizeFunc | 否 | 自定义最终消息的后处理函数 | |
| Callback | CallbackFunc | 否 | 在 Finalize 之后调用,用于观察状态变化(只读) | |
| EmitInternalEvents | bool | 否 | false | 是否发送内部事件 |
| PreserveUserMessages | *PreserveUserMessages | 否 | Enabled: true | 是否在摘要中保留原始用户消息 |
| Model | model.BaseModel[M] | 是 | — | 用于生成摘要的模型 |
| ModelOptions | []model.Option | 否 | — | 传递给摘要模型的选项 |
| TokenCounter | TypedTokenCounterFunc[M] | 否 | 基于最近 assistant 消息的 total\_tokens 作为基线,增量消息按 ~4 字符/token 估算 | 自定义 token 计数函数 |
| Trigger | *TriggerCondition | 否 | ContextTokens=160,000 | 触发摘要的条件 |
| UserInstruction | string | 否 | 内置 prompt | 自定义用户级摘要指令,覆盖默认指令 |
| TranscriptFilePath | string | 否 | — | 完整对话记录文件路径,附加到摘要中提醒模型原始上下文位置。仅在未设置 Finalize 时生效 |
| GenModelInput | TypedGenModelInputFunc[M] | 否 | sysInstruction → contextMsgs → userInstruction | 完全控制摘要模型输入的构建 |
| Finalize | TypedFinalizeFunc[M] | 否 | 内置后处理 | 自定义摘要后处理。设置后中间件不再执行任何默认后处理 |
| Callback | TypedCallbackFunc[M] | 否 | — | 在 Finalize 后调用,参数为 before, after adk.TypedChatModelAgentState[M](值类型),只读 |
| EmitInternalEvents | bool | 否 | false | 是否在关键节点发送内部事件 |
| PreserveUserMessages | *TypedPreserveUserMessages[M] | 否 | Enabled: true | 在摘要中保留原始用户消息。仅在未设置 Finalize 时生效 |
| Retry | *TypedRetryConfig[M] | 否 | nil(不重试) | 主模型摘要生成的重试策略 |
| Failover | *TypedFailoverConfig[M] | 否 | nil | 主模型失败后的降级策略 |
+
+**触发条件检查**:先检查 `ContextMessages`(消息数),再通过 `TokenCounter` 计算 token 数与 `ContextTokens` 对比。满足任一即触发。
+
+**默认后处理**(未设置 Finalize 时):
+
+1. 将摘要中 `| 事件类型 | 触发时机 | 携带数据 |
| ActionTypeBeforeSummarize | 生成摘要之前 | 原始消息列表 |
| ActionTypeAfterSummarize | 完成总结之后 | 最终消息列表 |
ActionTypeBeforeSummarize | 触发条件满足后,调用模型前 | TypedBeforeSummarizeAction[M]{Messages}:原始消息列表 |
ActionTypeGenerateSummary | 每次模型生成尝试后(含重试/降级) | TypedGenerateSummaryAction[M]{Attempt, Phase, ModelResponse, GetError()} |
ActionTypeAfterSummarize | 摘要完成、Finalize 之后 | TypedAfterSummarizeAction[M]{Messages}:最终消息列表 |
| 字段 | 类型 | 说明 |
| Backend | Backend | 存储后端。SkipTruncation为 false 时必填;仅做 Clear 且不需要 offload 时可为 nil。 |
| SkipTruncation | bool | 跳过截断阶段。 |
| SkipClear | bool | 跳过清理阶段。 |
| ReadFileToolName | string | 用于读取卸载内容的工具名。默认 "read_file"。 |
| RootDir | string | 保存内容的根目录。默认 "/tmp"。截断内容存到 {RootDir}/trunc/{tool_call_id},清理内容存到 {RootDir}/clear/{tool_call_id}。 |
| GenTruncOffloadFilePath | func(ctx, *ToolDetail) (string, error) | 自定义截断文件路径生成。设置后 RootDir 对截断不生效。适用于 tool_call_id 不唯一的场景。 |
| GenClearOffloadFilePath | func(ctx, *ToolDetail) (string, error) | 自定义清理文件路径生成。设置后 RootDir 对清理不生效。 |
| MaxLengthForTrunc | int | 触发截断的最大字符长度。默认 50000。 |
| TruncExcludeTools | []string | 不截断的工具名列表。 |
| TokenCounter | func(ctx, []M, []*schema.ToolInfo) (int64, error) | token 计数函数。默认使用字符数/4 估算。建议用 tiktoken-go/tokenizer 替换。 |
| MaxTokensForClear | int64 | 触发清理的 token 阈值。默认 160000。 |
| ClearRetentionSuffixLimit | int | 保留最近 N 轮 assistant 消息不清理。默认 1。 |
| ClearAtLeastTokens | int64 | 清理至少释放的 token 量。未达标则不执行清理(避免无谓破坏 prompt cache)。默认 0。 |
| ClearExcludeTools | []string | 不清理的工具名列表。 |
| ClearMessageRewriter | func(ctx, M, []M) ([]M, error) | 清理前的消息重写回调。参数为 toolCallMsg 和对应的 toolResponseMsgs。可用于将 write_file/edit_file 调用重写为 system-reminder。返回 nil 表示移除该组消息。 |
| ClearPostProcess | func(ctx, *adk.TypedChatModelAgentState[M]) context.Context | 清理完成后的回调,可保存状态或发送通知。返回可能更新后的 context。 |
| ToolConfig | map[string]*ToolReductionConfig | 按工具名配置,优先级高于全局。 |
| 参数 | 类型 | 必填 | 说明 |
regex_pattern | string | 是 | 匹配工具名的正则表达式 |
query | string | 是 | 查找工具的查询字符串。支持三种模式:关键字搜索、select:直接选择、 +keyword必须匹配 |
max_results | integer | 否 | 返回的最大结果数(默认:5)。仅对关键字搜索模式生效,直接选择模式不受此限制 |
| 模式 | 语法 | 说明 |
| 关键字搜索 | "weather forecast" | 按关键字在工具名和描述中匹配,按相关性评分排序。支持 camelCase 和 _/ __(MCP)分隔符拆分 |
| 直接选择 | "select:tool_a,tool_b" | 按精确名称选择一个或多个工具,逗号分隔。不受 max_results限制 |
| 必须匹配 | "+slack send message" | +前缀的关键字为必须匹配项,不含该关键字的工具会被过滤掉。其余关键字用于排序 |
| 匹配规则 | 得分 |
| 工具名拆分后的部分完全匹配关键字 | 10 |
| 工具名拆分后的部分包含关键字(子串) | 5 |
| 工具全名包含关键字 | 3 |
| 工具描述包含关键字 | 2 |
+默认模式从 state.ToolInfos中移除所有 DynamicTools,使模型初始只能看到静态工具和 tool_search |
+模型原生模式1. 将 DynamicTools 从 state.ToolInfos提取到 state.DeferredToolInfos2. 从 state.ToolInfos中移除 tool_search(由模型原生处理) |
| 特性 | AgentMiddleware (结构体) | ChatModelAgentMiddleware (接口) |
| 扩展性 | 封闭,用户无法添加新方法 | 开放,用户可实现自定义 handler |
| Context 传播 | 回调只返回 error | 所有方法返回 (context.Context, ..., error) |
| 配置管理 | 分散在闭包中 | 集中在结构体字段中 |
| AgentMiddleware 字段 | ChatModelAgentMiddleware 替代 |
AdditionalInstruction | BeforeAgent中修改 runCtx.Instruction |
AdditionalTools | BeforeAgent中修改 runCtx.Tools |
BeforeChatModel | BeforeModelRewriteState |
AfterChatModel | AfterModelRewriteState |
WrapToolCall | WrapInvokableToolCall/ WrapStreamableToolCall等 |
| 方法 | 输入 | 输出 | 作用范围 |
BeforeAgent | Agent 运行环境 (*ChatModelAgentContext) | 修改后的 Agent 运行环境 | 整个 Run 生命周期,仅调用一次 |
BeforeModelRewriteState | 持久化状态 + Model 运行环境 | 修改后的持久化状态 | 跨 iteration 的持久化状态(消息列表) |
WrapModel | 被 wrap 的 ChatModel + Model 运行环境 | 包装后的 Model | 单次 Model 请求的输入、输出和配置 |
AfterModelRewriteState | 持久化状态(含响应)+ Model 运行环境 | 修改后的持久化状态 | 跨 iteration 的持久化状态(消息列表) |
WrapInvokableToolCall | 被 wrap 的 Tool + Tool 运行环境 | 包装后的 endpoint | 单次 Tool 请求的输入、输出和配置 |
WrapStreamableToolCall | 被 wrap 的 Tool + Tool 运行环境 | 包装后的 endpoint | 单次 Tool 请求的输入、输出和配置 |
| 方法 | 功能 | 返回 |
LsInfo | 列出指定路径下的文件和目录信息 | []FileInfo |
Read | 读取文件内容,支持按行分页(offset + limit) | *FileContent |
GrepRaw | 在文件中搜索匹配 pattern 的内容 | []GrepMatch |
GlobInfo | 根据 glob pattern 查找匹配文件 | []FileInfo |
Write | 写入或创建文件 | error |
Edit | 替换文件中的字符串内容 | error |
| 类型 | 描述 | |
FileInfo | 文件/目录信息:路径、是否目录、大小、修改时间 | |
FileContent | 文件内容 + 行号信息 | |
GrepMatch | 搜索匹配结果:内容、路径、行号 | |
ReadRequest | 读取请求:路径、offset(从第几行开始,1-based)、limit(读取行数) | |
GrepRequest | 搜索请求:pattern(支持正则)、路径、glob 过滤、文件类型过滤等 | |
WriteRequest | 写入请求:路径、内容 | |
EditRequest | 编辑请求:路径、旧字符串、新字符串、是否全部替换 | |
ExecuteRequest | 命令执行请求:命令字符串、是否后台运行 | |
ExecuteResponse | 命令执行结果:输出内容、退出码、是否被截断 | |
| 类型 | 字段 | 说明 |
LsInfoRequest | Path string | 要列出的目录路径 |
ReadRequest | FilePath string Offset int Limit int | 文件路径;起始行号(1-based,<1 视为 1);最大读取行数(0=全部) |
MultiModalReadRequest | 嵌入 ReadRequest Pages string | 继承 ReadRequest 所有字段;Pages 指定 PDF 页码范围(如 "1-5"、"3") |
GrepRequest | Pattern string Path string Glob string FileType string CaseInsensitive bool EnableMultiline bool AfterLines int BeforeLines int | 正则搜索模式(ripgrep 语法);搜索目录;glob 文件过滤;文件类型过滤(如 "go"、"py");忽略大小写;启用多行匹配;匹配后显示 N 行;匹配前显示 N 行 |
GlobInfoRequest | Pattern string Path string | glob 表达式(支持 *、 **、 ?、 [abc]);搜索起始目录 |
WriteRequest | FilePath string Content string | 目标文件路径;写入内容 |
EditRequest | FilePath string OldString string NewString string ReplaceAll bool | 文件路径;被替换的精确字符串(非空);替换后的字符串;false 时要求 OldString 在文件中仅出现一次 |
ExecuteRequest | Command string RunInBackendGround bool | 要执行的命令字符串;是否后台运行 |
| 类型 | 字段 | 说明 |
FileInfo | Path string IsDir bool Size int64 ModifiedAt string | 文件/目录路径;是否为目录;文件大小(字节);最后修改时间(ISO 8601 格式) |
FileContent | Content string | 文件的纯文本内容 |
MultiFileContent | *FileContent Parts []FileContentPart | 嵌入 FileContent;多模态输出部分。Parts 与 FileContent 互斥:Parts 非空时 FileContent 被忽略 |
FileContentPart | Type FileContentPartType MIMEType string Data []byte | 内容类型("image"或 "pdf");MIME 类型(如 "image/png");原始二进制数据 |
GrepMatch | Content string Path string Line int | 匹配的行内容;文件路径;1-based 行号 |
ExecuteResponse | Output string ExitCode *int Truncated bool | 命令输出内容;退出码(指针,可能为 nil);输出是否被截断 |
| 方法 | 签名 | 说明 |
LsInfo | (ctx, *LsInfoRequest) ([]FileInfo, error) | 列出目录内容 |
Read | (ctx, *ReadRequest) (*FileContent, error) | 读取文件,支持行级分页(Offset 1-based,Limit 0=全部) |
Write | (ctx, *WriteRequest) error | 写入文件;自动创建父目录;文件已存在则覆盖 |
Edit | (ctx, *EditRequest) error | 字符串替换;支持 ReplaceAll; OldString不唯一时报错(非 ReplaceAll 模式) |
GrepRaw | (ctx, *GrepRequest) ([]GrepMatch, error) | 基于 ripgrep 搜索,支持完整正则语法;支持大小写不敏感、多行匹配、上下文行 |
GlobInfo | (ctx, *GlobInfoRequest) ([]FileInfo, error) | Glob 模式匹配文件,支持 */ **/ ?/ [abc] |
| 方法 | 签名 | 说明 |
ExecuteStreaming | (ctx, *ExecuteRequest) (*StreamReader[*ExecuteResponse], error) | 流式执行 shell 命令,实时输出;支持后台运行(RunInBackendGround) |
| 特性 | Local | Agentkit |
| 执行模型 | 本地直接 | 远程沙箱 |
| 网络依赖 | 无 | 需要 |
| 配置复杂度 | 零配置 | 需要凭证 |
| 安全模型 | OS 权限 + ValidateCommand | 隔离沙箱 |
| 流式输出 | 支持(StreamingShell) | 不支持 |
| 平台支持 | Unix/Linux/macOS | 任意 |
| 适用场景 | 开发/本地环境 | 多租户/生产环境 |
| 方法 | 描述 |
| LsInfo | 列出目录内容 |
| Read | 读取文件内容(支持分页,默认 200 行) |
| Write | 创建新文件(已存在则报错) |
| Edit | 替换文件内容 |
| GrepRaw | 搜索文件内容(字面量匹配) |
| GlobInfo | 按模式查找文件 |
| Execute | 执行 shell 命令 |
| ExecuteStreaming | 流式执行命令 |
| 特性 | Local | Agentkit |
| 执行模型 | 本地直接 | 远程沙箱 |
| 网络依赖 | 无 | 需要 |
| 配置复杂度 | 零配置 | 需要凭证 |
| 安全模型 | OS 权限 | 隔离沙箱 |
| 流式输出 | 支持 | 不支持 |
| 平台支持 | Unix/Linux/macOS | 任意 |
| 适用场景 | 开发/本地环境 | 多租户/生产环境 |
| 参数 | 类型 | 必填 | 默认值 | 说明 |
Backend | Backend | 是 | - | 文件读取后端,负责实际的文件 I/O |
AgentsMDFiles | []string | 是 | - | 要加载的 Agents.md 文件路径列表(至少一个) |
AllAgentsMDMaxBytes | int | 否 | 0(无限制) | 所有文件的总字节数上限 |
OnLoadWarning | func(string, error) | 否 | log.Printf | 非致命错误的回调函数 |
Backend | Backend | 是 | — | 文件读取后端,负责实际的文件 I/O |
AgentsMDFiles | []string | 是 | — | 要加载的 Agents.md 文件路径列表(至少一个),按顺序加载和注入 |
AllAgentsMDMaxBytes | int | 否 | 0(无限制) | 所有文件的总字节数上限;超过后跳过后续文件,但每个文件始终完整加载 |
OnLoadWarning | func(string, error) | 否 | log.Printf | 非致命错误的回调函数(文件缺失、循环 @import、深度超限等) |
| 规则 | 说明 |
| 路径解析 | 相对路径基于当前文件所在目录解析;绝对路径直接使用 |
| 最大递归深度 | 5 层(超过后跳过并触发 OnLoadWarning) |
| 循环引用检测 | 当前祖先链中已存在的路径会被跳过(触发 OnLoadWarning) |
| 全局去重 | 整次加载中同一文件路径只会被读取和注入一次 |
| 原文保留 | @import 引用的文件作为独立段落追加,原文中的 @path文本不被移除 |
| 字节预算 | 累计字节数超过 AllAgentsMDMaxBytes后,跳过后续 import |
| 场景 | 行为 |
文件不存在 (os.ErrNotExist) | 跳过该文件,触发 OnLoadWarning |
循环 @import | 跳过循环文件,触发 OnLoadWarning |
@import深度超过 5 层 | 跳过,触发 OnLoadWarning |
文件不存在(os.ErrNotExist) | 跳过该文件,触发 OnLoadWarning |
| 循环 @import | 跳过循环文件,触发 OnLoadWarning |
| @import 深度超过 5 层 | 跳过,触发 OnLoadWarning |
累计大小超过 AllAgentsMDMaxBytes | 跳过后续文件,触发 OnLoadWarning(第一个文件始终完整加载) |
| 权限被拒 / I/O 错误 | 中止加载,返回 error |
| 所有文件内容为空 | 不注入,原样传递输入消息 |
| 所有文件内容为空 | 不注入,原样传递消息 |
| 函数签名 | 说明 |
New(ctx, *MiddlewareConfig) (ChatModelAgentMiddleware, error) | 推荐。返回 ChatModelAgentMiddleware,支持通过 BeforeAgent钩子动态修改 Instruction 和 Tools。 |
NewTyped[M MessageType](ctx, *MiddlewareConfig) (TypedChatModelAgentMiddleware[M], error) | 泛型版本,类型参数 M支持 *schema.Message和 *schema.AgenticMessage。 New等价于 NewTyped[*schema.Message]。 |
| 字段 | 类型 | 说明 |
Backend | filesystem.Backend | 必填。提供文件系统操作能力,驱动 ls、read\_file、write\_file、edit\_file、glob、grep 共 6 个工具。接口定义在 github.com/cloudwego/eino/adk/filesystem包。 |
Shell | filesystem.Shell | 可选。提供命令执行能力,设置后注册 execute工具。与 StreamingShell互斥。 |
StreamingShell | filesystem.StreamingShell | 可选。提供流式命令执行能力,设置后注册流式 execute工具。与 Shell互斥。 |
UseMultiModalRead | bool | 可选,默认 false。开启后 read_file工具变为 EnhancedInvokableTool,支持返回图片/PDF 等多模态内容。要求 Backend 同时实现 filesystem.MultiModalReader 接口。 |
CustomSystemPrompt | *string | 可选。覆盖追加到 Agent Instruction 的系统提示词。若为 nil,不追加任何系统提示词。 |
| 字段 | 对应工具 |
LsToolConfig | ls |
ReadFileToolConfig | read\_file |
WriteFileToolConfig | write\_file |
EditFileToolConfig | edit\_file |
GlobToolConfig | glob |
GrepToolConfig | grep |
| 工具 | 默认名称 | 描述 | 条件 |
| 列出目录 | ls | 列出指定路径下的文件和目录 | Backend 不为 nil 时注入 |
| 读取文件 | read_file | 读取文件内容,支持按行分页(offset + limit) | Backend 不为 nil 时注入 |
| 写入文件 | write_file | 创建或覆盖文件 | Backend 不为 nil 时注入 |
| 编辑文件 | edit_file | 替换文件中的字符串 | Backend 不为 nil 时注入 |
| Glob 查找 | glob | 按 glob pattern 查找文件 | Backend 不为 nil 时注入 |
| 内容搜索 | grep | 按 pattern 搜索文件内容,支持多种输出模式 | Backend 不为 nil 时注入 |
| 命令执行 | execute | 执行 shell 命令 | 需配置 Shell 或 StreamingShell |
| 工具 | 默认名称 | 注册条件 | 功能说明 |
| ls | ls | Backend ≠ nil | 列出目录下的文件和子目录 |
| read\_file | read_file | Backend ≠ nil | 读取文件内容,支持 offset/limit 分页。开启 UseMultiModalRead后可读取图片和 PDF |
| write\_file | write_file | Backend ≠ nil | 创建或覆盖写入文件 |
| edit\_file | edit_file | Backend ≠ nil | 精确字符串替换编辑,支持 replace_all |
| glob | glob | Backend ≠ nil | 按 glob 模式匹配文件路径 |
| grep | grep | Backend ≠ nil | 正则搜索文件内容,支持多种输出模式和分页 |
| execute | execute | Shell ≠ nil 或 StreamingShell ≠ nil | 执行 Shell 命令 |
| 字段 | 说明 |
WithoutLargeToolResultOffloading bool | 设为 true禁用卸载,默认 false(启用) |
LargeToolResultOffloadingTokenLimit int | Token 阈值,默认 20000 |
LargeToolResultOffloadingPathGen func(ctx, *compose.ToolInput) (string, error) | 卸载路径生成函数,默认 /large_tool_result/{ToolCallID} |
| 协作方式 | 描述 |
| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
| 上下文策略 | 描述 |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 |
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
-
-在 Eino ADK 中,当为一个 Agent 构建 AgentInput 时,它能看到的 History 是“所有在我之前产生的 AgentEvent”。
-
-值得一提的是 ParallelWorkflowAgent:并行的两个子 Agent(A,B),在并行执行过程中,相互不可见对方产生的 AgentEvent,因为并行的 A、B 没有谁是在另一个之前。
-
-#### RunPath
-
-History 中每个 AgentEvent 都是由“特定 Agent 在特定的执行序列中产生的”,也就是 AgentEvent 有自身的 RunPath。RunPath 的作用是传递出这个信息,在 eino 框架中不乘载其他功能。
-
-下面表格中给出各种编排模式下,Agent 执行时的具体 RunPath:
-
-
-
-三个 Agent 均使用 ChatModelAgent 实现:
+- 父 Agent 保持控制权,可基于子 Agent 结果继续推理
+- 子 Agent 接收独立的任务描述,不继承父 Agent 的完整对话历史
+- 多个子 Agent 可并行调用
```go
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
"github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/components/tool"
)
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-type GetWeatherInput struct {
- City string `json:"city"`
-}
-
-func NewWeatherAgent() adk.Agent {
- weatherTool, err := utils.InferTool(
- "get_weather",
- "Gets the current weather for a specific city.", // English description
- func(ctx context.Context, input *GetWeatherInput) (string, error) {
- return fmt.Sprintf(`the temperature in %s is 25°C`, input.City), nil
- },
- )
- if err != nil {
- log.Fatal(err)
- }
-
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "WeatherAgent",
- Description: "This agent can get the current weather for a given city.",
- Instruction: "Your sole purpose is to get the current weather for a given city by using the 'get_weather' tool. After calling the tool, report the result directly to the user.",
- Model: newChatModel(),
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{weatherTool},
- },
- },
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func NewChatAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ChatAgent",
- Description: "A general-purpose agent for handling conversational chat.", // English description
- Instruction: "You are a friendly conversational assistant. Your role is to handle general chit-chat and answer questions that are not related to any specific tool-based tasks.",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func NewRouterAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "RouterAgent",
- Description: "A manual router that transfers tasks to other expert agents.",
- Instruction: `You are an intelligent task router. Your responsibility is to analyze the user's request and delegate it to the most appropriate expert agent.If no Agent can handle the task, simply inform the user it cannot be processed.`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-```
-
-之后使用 Eino ADK 的 Transfer 能力搭建 Multi-Agent 并运行,ChatModelAgent 实现了 OnSubAgent 接口,在 adk.SetSubAgents 方法中会使用此接口向 ChatModelAgent 注册父/子 Agent,不需要用户处理 TransferAction 生成问题:
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino/adk"
-)
-
-func main() {
- weatherAgent := NewWeatherAgent()
- chatAgent := NewChatAgent()
- routerAgent := NewRouterAgent()
-
- ctx := context.Background()
- a, err := adk.SetSubAgents(ctx, routerAgent, []adk.Agent{chatAgent, weatherAgent})
- if err != nil {
- log.Fatal(err)
- }
-
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: a,
- })
-
- // query weather
- println("\n\n>>>>>>>>>query weather<<<<<<<<<")
- iter := runner.Query(ctx, "What's the weather in Beijing?")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- if event.Action != nil {
- fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
- } else {
- fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
- }
- }
-
- // failed to route
- println("\n\n>>>>>>>>>failed to route<<<<<<<<<")
- iter = runner.Query(ctx, "Book me a flight from New York to London tomorrow.")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- if event.Action != nil {
- fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
- } else {
- fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
- }
- }
-}
-```
-
-运行结果:
-
-```yaml
->>>>>>>>>query weather<<<<<<<<<
-Agent[RouterAgent]:
-assistant:
-tool_calls:
-{Index:| 选项 | 说明 |
WithFullChatHistoryAsInput() | 将父 Agent 的完整对话历史作为子 Agent 输入(默认只传模型生成的 request 参数) |
WithAgentInputSchema(schema) | 自定义子 Agent 的输入 schema |
+### 事件流透传
-```go
-// github.com/cloudwego/eino/adk/prebuilt/supervisor.go
+当 `ToolsConfig.EmitInternalEvents = true` 时,子 Agent 的事件会实时透传到父 Agent 的事件流,允许终端用户看到子 Agent 的中间过程。
-type SupervisorConfig struct {
- Supervisor adk.Agent
- SubAgents []adk.Agent
-}
+> 💡
+> 透传的事件不影响父 Agent 的状态或 checkpoint,仅用于用户展示。唯一例外是 Interrupted action,会通过 CompositeInterrupt 跨边界传播以支持中断恢复。
-func NewSupervisor(ctx context.Context, conf *SupervisorConfig) (adk.Agent, error) {
- subAgents := make([]adk.Agent, 0, len(conf.SubAgents))
- supervisorName := conf.Supervisor.Name(ctx)
- for _, subAgent := range conf.SubAgents {
- subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{
- Agent: subAgent,
- ToAgentNames: []string{supervisorName},
- }))
- }
+### 预构建示例:DeepAgents
- return adk.SetSubAgents(ctx, conf.Supervisor, subAgents)
-}
-```
+[DeepAgents](/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents) 是 AgentAsTool 模式的最佳实践:主 Agent 通过 **TaskTool** 将子任务委派给子 Agent 执行,配合 **WriteTodos** 进行任务规划和进度追踪。
## Workflow Agents
-WorkflowAgent 支持以代码中预设好的流程运行 Agents。Eino ADK 提供了三种基础 Workflow Agent:Sequential、Parallel、Loop,它们之间可以互相嵌套以完成更复杂的任务。
-
-默认情况下,Workflow 中每个 Agent 的输入由 History 章节中介绍的方式生成,可以通过 WithHistoryRewriter 自定 AgentInput 生成方式。
-
-当 Agent 产生 ExitAction Event 后,Workflow Agent 会立刻退出,无论之后有没有其他需要运行的 Agent。
-
-详解与用例参考请见:[Eino ADK: Workflow Agents](/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow)
-
-### SequentialAgent
-
-SequentialAgent 会按照你提供的顺序,依次执行一系列 Agent:
-
-
-
-```go
-type SequentialAgentConfig struct {
- Name string
- Description string
- SubAgents []Agent
-}
-
-func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
-```
-
-### LoopAgent
-
-LoopAgent 基于 SequentialAgent 实现,在 SequentialAgent 运行完成后,再次从头运行:
-
-
-
-```go
-type LoopAgentConfig struct {
- Name string
- Description string
- SubAgents []Agent
-
- MaxIterations int // 最大循环次数
-}
-
-func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
-```
-
-### ParallelAgent
-
-ParallelAgent 会并发运行若干 Agent:
+确定性编排,用于流程固定的多步任务:
-
+| 类型 | 说明 | 构造函数 |
| Sequential | 按数组顺序依次执行子 Agent | adk.NewSequentialAgent |
| Parallel | 并发执行所有子 Agent,全部完成后结束 | adk.NewParallelAgent |
| Loop | 循环执行子 Agent 序列,直到 BreakLoop 或超过 MaxIterations | adk.NewLoopAgent |
-
-可以通过 ToolsConfig 为 ChatModelAgent 配置 Tool:
-
-```go
-// github.com/cloudwego/eino/adk/chatmodel.go
-
-type ToolsConfig struct {
- compose.ToolsNodeConfig
-
- // Names of the tools that will make agent return directly when the tool is called.
- // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
- ReturnDirectly map[string]bool
-
- // EmitInternalEvents indicates whether internal events from agentTool should be emitted
- // to the parent generator via a tool option injection at run-time.
- EmitInternalEvents bool
-}
-```
-
-ToolsConfig 复用了 Eino Graph ToolsNodeConfig,详细参考:[Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。额外提供了 ReturnDirectly 配置,ChatModelAgent 调用配置在 ReturnDirectly 中的 Tool 后会直接退出。
-
-## ChatModelAgent 配置字段
-
-> 💡
-> 注意:GenModelInput 默认情况下,会通过 adk.GetSessionValues() 并以 F-String 的格式渲染 Instruction,如需关闭此行为,可定制 GenModelInput 方法。
-
-```go
-type ChatModelAgentConfig struct {
- // Name of the agent. Better be unique across all agents.
- Name string
- // Description of the agent's capabilities.
- // Helps other agents determine whether to transfer tasks to this agent.
- Description string
- // Instruction used as the system prompt for this agent.
- // Optional. If empty, no system prompt will be used.
- // Supports f-string placeholders for session values in default GenModelInput, for example:
- // "You are a helpful assistant. The current time is {Time}. The current user is {User}."
- // These placeholders will be replaced with session values for "Time" and "User".
- Instruction string
-
- Model model.ToolCallingChatModel
-
- ToolsConfig ToolsConfig
-
- // GenModelInput transforms instructions and input messages into the model's input format.
- // Optional. Defaults to defaultGenModelInput which combines instruction and messages.
- GenModelInput GenModelInput
-
- // Exit defines the tool used to terminate the agent process.
- // Optional. If nil, no Exit Action will be generated.
- // You can use the provided 'ExitTool' implementation directly.
- Exit tool.BaseTool
-
- // OutputKey stores the agent's response in the session.
- // Optional. When set, stores output via AddSessionValue(ctx, outputKey, msg.Content).
- OutputKey string
-
- // MaxIterations defines the upper limit of ChatModel generation cycles.
- // The agent will terminate with an error if this limit is exceeded.
- // Optional. Defaults to 20.
- MaxIterations int
-
- // ModelRetryConfig configures retry behavior for the ChatModel.
- // When set, the agent will automatically retry failed ChatModel calls
- // based on the configured policy.
- // Optional. If nil, no retry will be performed.
- ModelRetryConfig *ModelRetryConfig
-}
-
-type ToolsConfig struct {
- compose.ToolsNodeConfig
-
- // Names of the tools that will make agent return directly when the tool is called.
- // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
- ReturnDirectly map[string]bool
-
- // EmitInternalEvents indicates whether internal events from agentTool should be emitted
- // to the parent generator via a tool option injection at run-time.
- EmitInternalEvents bool
-}
-
-type GenModelInput func(ctx context.Context, instruction string, input *AgentInput) ([]Message, error)
-```
-
-- `Name`:Agent 名称
-- `Description`:Agent 描述
-- `Instruction`:调用 ChatModel 时的 System Prompt,支持 f-string 渲染
-- `Model`:运行所使用的 ChatModel,要求支持工具调用
-- `ToolsConfig`:工具配置
- - ToolsConfig 复用了 Eino Graph ToolsNodeConfig,详细参考:[Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。
- - ReturnDirectly:当 ChatModelAgent 调用配置在 ReturnDirectly 中的 Tool 后,将携带结果立刻退出,不会按照 react 模式返回 ChatModel。如果命中了多个 Tool,只有首个 Tool 会返回。Map key 为 Tool 名称。
- - EmitInternalEvents:当通过 adk.AgentTool() 将一个 Agent 通过 ToolCall 的形式当成 SubAgent 时,默认情况下,这个 SubAgent 不会发送 AgentEvent,只将最终结果作为 ToolResult 返回。
-- `GenModelInput`:Agent 被调用时会使用该方法将 `Instruction` 和 `AgentInput` 转换为调用 ChatModel 的 Messages。Agent 提供了默认的 GenModelInput 方法:
- 1. 将 `Instruction` 作为 `System Message` 加到 `AgentInput.Messages` 前
- 2. 将 `SessionValues` 为 variables 渲染到步骤 1 的 message list 中
-
-> 💡
-> 默认的 `GenModelInput` 使用 pyfmt 渲染,message list 中的文本会被作为 pyfmt 模板,这意味着文本中的 '{' 与 '}' 都会被视为关键字,如果希望直接输入这两个字符,需要进行转义 '{{'、'}}'
-
-- `OutputKey`:配置后,ChatModelAgent 运行产生的最后一条 Message 将会以 `OutputKey` 为 key 设置到 `SessionValues` 中
-- `MaxIterations`:react 模式下 ChatModel 最大生成次数,超过时 Agent 会报错退出,默认值为 20
-- `Exit`:Exit 是一个特殊的 Tool,当模型调用这个工具并执行后,ChatModelAgent 将直接退出,效果与 `ToolsConfig.ReturnDirectly` 类似。ADK 提供了一个默认 ExitTool 实现供用户使用:
-
-```go
-type ExitTool struct{}
-
-func (et ExitTool) Info(_ context.Context) (*schema.ToolInfo, error) {
- return ToolInfoExit, nil
-}
-
-func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ ...tool.Option) (string, error) {
- type exitParams struct {
- FinalResult string `json:"final_result"`
- }
-
- params := &exitParams{}
- err := sonic.UnmarshalString(argumentsInJSON, params)
- if err != nil {
- return "", err
- }
-
- err = SendToolGenAction(ctx, "exit", NewExitAction())
- if err != nil {
- return "", err
- }
-
- return params.FinalResult, nil
-}
-```
-
-- `ModelRetryConfig`: 配置后,ChatModel 请求过程中发生的各种错误(包括直接返回错误、流式响应过程中发生错误等),都会按照配置的策略选择是否以及何时进行重试。如果是流式响应过程中发生错误,则这一次流式响应依然会第一时间通过 AgentEvent 的形式返回出去。如果这次流式响应过程中的错误,按照配置的策略,会进行重试,则消费 AgentEvent 中的 message stream,会得到 `WillRetryError`。用户可以处理这个 error,做对应的上屏展示等处理,示例如下:
-
-```go
-iterator := agent.Run(ctx, input)
-for {
- event, ok := iterator.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- handleFinalError(event.Err)
- break
- }
-
- // Process streaming output
- if event.Output != nil && event.Output.MessageOutput.IsStreaming {
- stream := event.Output.MessageOutput.MessageStream
- for {
- msg, err := stream.Recv()
- if err == io.EOF {
- break // Stream completed successfully
- }
- if err != nil {
- // Check if this error will be retried (more streams coming)
- var willRetry *adk.WillRetryError
- if errors.As(err, &willRetry) {
- log.Printf("Attempt %d failed, retrying...", willRetry.RetryAttempt)
- break // Wait for next event with new stream
- }
- // Original error - won't retry, agent will stop and the next AgentEvent probably will be an error
- log.Printf("Final error (no retry): %v", err)
- break
- }
- // Display chunk to user
- displayChunk(msg)
- }
- }
-}
-```
-
-## ChatModelAgent Transfer
-
-`ChatModelAgent` 支持将其他 Agent 的元信息转为自身的 Tool ,经由 ChatModel 判断实现动态 Transfer:
-
-- `ChatModelAgent` 实现了 `OnSubAgents` 接口,使用 `SetSubAgents` 为 `ChatModelAgent` 设置子 Agents 后,`ChatModelAgent` 会增加一个 `Transfer Tool`,并且在 prompt 中指示 ChatModel 在需要 transfer 时调用这个 Tool 并以 transfer 目标 AgentName 作为 Tool 输入。
-
-```go
-const (
- TransferToAgentInstruction = `Available other agents: %s
-
-Decision rule:
-- If you're best suited for the question according to your description: ANSWER
-- If another agent is better according its description: CALL '%s' function with their agent name
-
-When transferring: OUTPUT ONLY THE FUNCTION CALL`
-)
-
-func genTransferToAgentInstruction(ctx context.Context, agents []Agent) string {
- var sb strings.Builder
- for _, agent := range agents {
- sb.WriteString(fmt.Sprintf("\n- Agent name: %s\n Agent description: %s",
- agent.Name(ctx), agent.Description(ctx)))
- }
-
- return fmt.Sprintf(TransferToAgentInstruction, sb.String(), TransferToAgentToolName)
-}
-```
-
-- `Transfer Tool` 运行会设置 Transfer Event,指定跳转到目标 Agent 上,完成后 ChatModelAgent 退出。
-- Agent Runner 接收到 Transfer Event 后,跳转到目标 Agent 上执行,完成 Transfer 操作
-
-## ChatModelAgent AgentAsTool
-
-当需要被调用的 Agent 不需要完整的运行上下文,仅需要明确清晰的入参即可正确运行时,该 Agent 可以转换为 Tool 交由 `ChatModelAgent` 判断调用:
-
-- ADK 中提供了工具方法,可以方便地将 Eino ADK Agent 转化为 Tool 供 ChatModelAgent 调用:
-
-```go
-// github.com/cloudwego/eino/adk/agent_tool.go
-
-func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
-```
-
-- 被转换为 Tool 后的 Agent 可以通过 `ToolsConfig` 直接注册在 ChatModelAgent 中
-
-```go
-bookRecommender := NewBookRecommendAgent()
-bookRecommendeTool := NewAgentTool(ctx, bookRecommender)
-
-a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- // ...
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{bookRecommendeTool},
- },
- },
-})
-```
-
-## ChatModelAgent Middleware
-
-`ChatModelAgentMiddleware` 是 `ChatModelAgent` 的扩展机制,允许开发者在 Agent 执行的各个阶段注入自定义逻辑:
-
-
-
-`ChatModelAgentMiddleware` 定义为 interface,开发者可以实现此 interface 并通过配置到 `ChatModelAgentConfig` 使其在 `ChatModelAgent` 中生效:
-
-```go
-type ChatModelAgentMiddleware interface {
- // ...
-}
-
-a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- // ...
- Handlers: []adk.ChatModelAgentMiddleware{
- &MyMiddleware{},
- },
-})
-```
-
-**使用 BaseChatModelAgentMiddleware**
-
-`BaseChatModelAgentMiddleware` 提供所有方法的默认空实现。通过嵌入它,可以只覆盖需要的方法:
-
-```go
-type MyMiddleware struct {
- *adk.BaseChatModelAgentMiddleware
- // 自定义字段
- logger *log.Logger
-}
-
-// 只需覆盖需要的方法
-func (m *MyMiddleware) BeforeModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- m.logger.Printf("Messages count: %d", len(state.Messages))
- return ctx, state, nil
-}
-```
-
-### BeforeAgent
-
-在每次 Agent 运行前调用,可用于修改指令和工具配置。ChatModelAgentContext 定义了 BeforeAgent 中可读写的内容:
-
-```go
-type ChatModelAgentContext struct {
- // InstructionAgent 是当前 Agent 的指令
- Instruction string
- // Tools 是当前配置的原始工具列表
- Tools []tool.BaseTool
- // ReturnDirectly 配置调用后直接返回的工具名称集合
- ReturnDirectly map[string]bool
-}
-
-type ChatModelAgentMiddleware interface {
- // ...
- BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error)
- // ...
-}
-```
-
-例子:
-
-```go
-func (m *MyMiddleware) BeforeAgent(
- ctx context.Context,
- runCtx *adk.ChatModelAgentContext,
-) (context.Context, *adk.ChatModelAgentContext, error) {
- // 拷贝 runCtx,避免修改输入
- nRunCtx := *runCtx
-
- // 修改指令
- nRunCtx.Instruction += "\n\n请始终使用中文回复。"
-
- // 添加工具
- nRunCtx.Tools = append(runCtx.Tools, myCustomTool)
-
- // 设置工具直接返回
- nRunCtx.ReturnDirectly["my_tool"] = true
-
- return ctx, &nRunCtx, nil
-}
-```
-
-### BeforeModelRewriteState / AfterModelRewriteState
-
-在每次模型调用前/后调用,可用于检查和修改消息历史。ModelContext 定义了只读内容,ChatModelAgentState 定义了可读写内容:
-
-```go
-type ModelContext struct {
- // Tools 包含当前配置给 Agent 的工具列表
- // 在请求时填充,包含将要发送给模型的工具信息
- Tools []*schema.ToolInfo
-
- // ModelRetryConfig 包含模型的重试配置
- // 从 Agent 的 ModelRetryConfig 填充
- ModelRetryConfig *ModelRetryConfig
-}
-
-type ChatModelAgentState struct {
- // Messages 包含当前会话中的所有消息
- Messages []Message
-}
-
-type ChatModelAgentMiddleware interface {
- BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
- AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
-}
-```
-
-例子:
-
-```go
-func (m *MyMiddleware) BeforeModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- // 拷贝 state,避免修改入参
- nState := *state
-
- // 检查消息历史
- if len(state.Messages) > 50 {
- // 截断过旧的消息
- nState.Messages = state.Messages[len(state.Messages)-50:]
- }
- return ctx, &nState, nil
-}
-
-func (m *MyMiddleware) AfterModelRewriteState(
- ctx context.Context,
- state *adk.ChatModelAgentState,
- mc *adk.ModelContext,
-) (context.Context, *adk.ChatModelAgentState, error) {
- // 模型响应是最后一条消息
- lastMsg := state.Messages[len(state.Messages)-1]
- m.logger.Printf("Model response: %s", lastMsg.Content)
- return ctx, state, nil
-}
-```
-
-### WrapModel
-
-包装模型调用,可用于拦截和修改模型的输入输出:
-
-```go
-type ChatModelAgentMiddleware interface {
- WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error)
-}
-```
-
-例子:
-
-```go
-func (m *MyMiddleware) WrapModel(
- ctx context.Context,
- chatModel model.BaseChatModel,
- mc *adk.ModelContext,
-) (model.BaseChatModel, error) {
- return &loggingModel{
- inner: chatModel,
- logger: m.logger,
- }, nil
-}
-
-type loggingModel struct {
- inner model.BaseChatModel
- logger *log.Logger
-}
-
-func (m *loggingModel) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) {
- m.logger.Printf("Input messages: %d", len(msgs))
- resp, err := m.inner.Generate(ctx, msgs, opts...)
- m.logger.Printf("Output: %v, error: %v", resp != nil, err)
- return resp, err
-}
-
-func (m *loggingModel) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) {
- return m.inner.Stream(ctx, msgs, opts...)
-}
-```
-
-### WrapInvokableToolCall / WrapStreamableToolCall
-
-包装工具调用,可用于拦截和修改工具的输入输出:
-
-```go
-// InvokableToolCallEndpoint 是工具调用的函数签名。
-// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。
-type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error)
-
-// StreamableToolCallEndpoint 是流式工具调用的函数签名。
-// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。
-type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error)
-
-type ToolContext struct {
- // Name 说明了本次调用工具的名称
- Name string
- // CallID 说明了本次调用工具的 ToolCallID
- CallID string
-}
-
-type ChatModelAgentMiddleware interface {
- WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error)
- WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error)
-}
-```
-
-例子:
-
-```go
-func (m *MyMiddleware) WrapInvokableToolCall(
- ctx context.Context,
- endpoint adk.InvokableToolCallEndpoint,
- tCtx *adk.ToolContext,
-) (adk.InvokableToolCallEndpoint, error) {
- return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
- m.logger.Printf("Calling tool: %s (ID: %s)", tCtx.Name, tCtx.CallID)
- start := time.Now()
-
- result, err := endpoint(ctx, argumentsInJSON, opts...)
-
- m.logger.Printf("Tool %s completed in %v", tCtx.Name, time.Since(start))
- return result, err
- }, nil
-}
-```
-
-# ChatModelAgent 使用示例
-
-## 场景说明
-
-创建一个图书推荐 Agent,Agent 将能够根据用户的输入推荐相关图书。
-
-## 代码实现
-
-### 步骤 1: 定义工具
-
-图书推荐 Agent 需要一个根据能够根据用户要求(题材、评分等)检索图书的工具 `book_search` 。
-
-利用 Eino 提供的工具方法可以方便地创建(可参考[如何创建一个 tool ?](/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool)):
-
-```go
-import (
- "context"
- "log"
-
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
-)
-
-type BookSearchInput struct {
- Genre string `json:"genre" jsonschema:"description=Preferred book genre,enum=fiction,enum=sci-fi,enum=mystery,enum=biography,enum=business"`
- MaxPages int `json:"max_pages" jsonschema:"description=Maximum page length (0 for no limit)"`
- MinRating int `json:"min_rating" jsonschema:"description=Minimum user rating (0-5 scale)"`
-}
-
-type BookSearchOutput struct {
- Books []string
-}
-
-func NewBookRecommender() tool.InvokableTool {
- bookSearchTool, err := utils.InferTool("search_book", "Search books based on user preferences", func(ctx context.Context, input *BookSearchInput) (output *BookSearchOutput, err error) {
- // search code
- // ...
- return &BookSearchOutput{Books: []string{"God's blessing on this wonderful world!"}}, nil
- })
- if err != nil {
- log.Fatalf("failed to create search book tool: %v", err)
- }
- return bookSearchTool
-}
-```
-
-### 步骤 2: 创建 ChatModel
-
-Eino 提供了多种 ChatModel 封装(如 openai、gemini、doubao 等,详见 [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)),这里以 openai ChatModel 为例:
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/components/model"
-)
-
-func NewChatModel() model.ToolCallingChatModel {
- ctx := context.Background()
- apiKey := os.Getenv("OPENAI_API_KEY")
- openaiModel := os.Getenv("OPENAI_MODEL")
-
- cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: apiKey,
- Model: openaiModel,
- })
- if err != nil {
- log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
- }
- return cm
-}
-```
-
-### 步骤 3: 创建 ChatModelAgent
-
-除了配置 ChatModel 和工具外,还需要配置描述 Agent 功能用途的 Name 和 Description,以及指示 ChatModel 的 Instruction,Instruction 最终会作为 system message 被传递给 ChatModel。
-
-```go
-import (
- "context"
- "fmt"
- "log"
-
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/compose"
-)
-
-func NewBookRecommendAgent() adk.Agent {
- ctx := context.Background()
-
- a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- Name: "BookRecommender",
- Description: "An agent that can recommend books",
- Instruction: `You are an expert book recommender. Based on the user's request, use the "search_book" tool to find relevant books. Finally, present the results to the user.`,
- Model: NewChatModel(),
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{NewBookRecommender()},
- },
- },
- })
- if err != nil {
- log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
- }
-
- return a
-}
-```
-
-###
-
-### 步骤 4: 通过 Runner 运行
-
-```go
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino/adk"
-
- "github.com/cloudwego/eino-examples/adk/intro/chatmodel/subagents"
-)
-
-func main() {
- ctx := context.Background()
- a := subagents.NewBookRecommendAgent()
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: a,
- })
- iter := runner.Query(ctx, "recommend a fiction book to me")
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Err != nil {
- log.Fatal(event.Err)
- }
- msg, err := event.Output.MessageOutput.GetMessage()
- if err != nil {
- log.Fatal(err)
- }
- fmt.Printf("\nmessage:\n%v\n======", msg)
- }
-}
-```
-
-## 运行结果
-
-```yaml
-message:
-assistant:
-tool_calls:
-{Index:| 字段 | 说明 |
Name | Agent 名称。用作 AgentTool 时必填 |
Description | Agent 能力描述。用作 AgentTool 时必填 |
Instruction | System Prompt。支持 {Key} 占位符,默认 GenModelInput会用 SessionValues 渲染 |
Model | 必填。model.BaseModel[M]类型,使用 Tools 时须支持 model.WithTools |
ToolsConfig | 工具配置,详见下文 |
GenModelInput | 自定义输入转换。默认将 Instruction 作为 System Message + f-string 渲染 |
MaxIterations | ReAct 最大循环次数,超过报错退出。默认 20 |
Handlers | 接口式 Middleware(TypedChatModelAgentMiddleware[M]),推荐使用 |
Middlewares | 结构体式 Middleware(AgentMiddleware),旧版兼容 |
ModelRetryConfig | 模型调用失败时的重试策略 |
ModelFailoverConfig | 模型调用失败时切换备用模型。需配置 GetFailoverModel和 ShouldFailover |
| 钩子 | 时机 | 可修改内容 |
BeforeAgent | Agent 运行前(仅一次) | Instruction、Tools、ReturnDirectly、ToolSearchTool |
AfterAgent | Agent 成功结束后 | 读取最终 state(不修改) |
BeforeModelRewriteState | 每次模型调用前 | Messages、ToolInfos、DeferredToolInfos(持久化到 state) |
AfterModelRewriteState | 每次模型调用后 | Messages(含模型响应)、ToolInfos(持久化到 state) |
WrapModel | 包装模型调用 | 重试、failover、事件发送(不要修改 Messages) |
WrapToolCall | 包装工具调用 | 权限检查、日志、输出改写 |
| 场景 | 行为 |
Stream()初始化失败 | 与 Generate 一致,直接触发 failover 判定 |
| 流中途出错 | 已接收 chunk 拼接为 LastOutputMessage传入 ShouldFailover;决定 failover 后关闭当前流,用新模型重启 |
| 客户端影响 | 失败尝试中已发送的事件不会被撤回。客户端应在收到新一轮流时重置部分结果或按元数据去重 |
+---
-上图展示了 DeepAgents 的核心组件与它们之间的调用关系:
+## Config 完整定义
-- 主 Agent: 系统的入口和总指挥,接收初始任务,以 ReAct 方式调用工具完成任务并负责最终结果的呈现。
-- ChatModel (ToolCallingChatModel): 通常是一个具备工具调用能力的大语言模型,负责理解任务、推理、选择并调用工具。
-- Tools: MainAgent 可用的一系列能力的集合,包括:
- - WriteTodos: 内置的规划工具,用于将复杂任务拆解为结构化的待办事项列表。
- - TaskTool: 一个特殊的工具,作为调用子 Agent 的统一入口。
- - BuiltinTools、CustomTools: DeepAgents 内置的通用工具以及用户根据业务需求自定义的各类工具。
-- SubAgents: 负责执行具体、独立的子任务,与 MainAgent 上下文独立。
- - GeneralPurpose: 通用子 Agent,具有与 MainAgent 相同的 Tools(除了 TaskTool),用于在“干净”的上下文中执行子任务。
- - CustomSubAgents: 用户根据业务需求自定义的各种子 Agent。
+```go
+type Config = TypedConfig[*schema.Message]
-### 内置能力
+type TypedConfig[M adk.MessageType] struct {
+ Name string // Agent 标识名
+ Description string // 用途描述
+ ChatModel model.BaseModel[M] // 必填;需支持 model.WithTools
+ Instruction string // 系统提示词;为空时使用内置默认 Prompt
-#### Filesystem
+ // 子 Agent(绑定到 TaskTool)
+ SubAgents []adk.TypedAgent[M]
-> 💡
-> 目前处于 alpha 状态
+ // 自定义工具
+ ToolsConfig adk.ToolsConfig
+ MaxIteration int // 最大推理迭代次数
-创建 DeepAgents 时配置相关 Backend,DeepAgents 会自动加载相应工具:
+ // 文件系统(三选一或组合)
+ Backend filesystem.Backend // 注册 ls/read_file/write_file/edit_file/glob/grep
+ Shell filesystem.Shell // 注册 execute(与 StreamingShell 互斥)
+ StreamingShell filesystem.StreamingShell // 注册 execute(流式,与 Shell 互斥)
-```
-type Config struct {
- // ...
- Backend filesystem.Backend
- Shell filesystem.Shell
- StreamingShell filesystem.StreamingShell
- // ...
-}
-```
+ // 内置功能开关
+ WithoutWriteTodos bool // true 时关闭 write_todos 工具
+ WithoutGeneralSubAgent bool // true 时关闭默认 general-purpose 子 Agent
-| 配置 | 功能 | 添加工具 |
| Backend | 提供文件系统访问能力,可选 | read_file, write_file, edit_file, glob, grep |
| Shell | 提供 Shell 能力,可选,与 StreamShell 互斥 | execute |
| StreamingShell | 提供可以流式返回结果的 Shell 能力,可选,与 Shell 互斥 | execute(streaming) |
+### 构造函数
-1. 模型接收用户输入。
-2. 模型调用 WriteTodos 工具,参数为依照 WriteTodos Description 产生的任务列表。这次工具调用被添加到上下文中,供后续参考。
-3. 模型依照上下文中的 todos,调用 TaskTool 完成第一个 todo。
-4. 再次调用 WriteTodos ,更新 Todos 执行进度。
+```go
+// 标准版(M = *schema.Message)
+func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error)
-> 💡
-> 对简单任务来说,每次都调用 WriteTodos 可能会起到反效果。WriteTodos Description 中添加了一些比较通用的正反例子来避免不调用或过度调用 WriteTodos。使用 DeepAgents 时,可以根据实际业务场景添加更多 prompt 来让 WriteTodos 在合适的时候被调用。
+// 泛型版(支持 *schema.AgenticMessage)
+func NewTyped[M adk.MessageType](ctx context.Context, cfg *TypedConfig[M]) (adk.TypedResumableAgent[M], error)
+```
> 💡
-> WriteTodos 会被默认添加到 Agent 中,配置 `WithoutWriteTodos=true` 可以关闭 WriteTodos。
+> 返回 ResumableAgent(包含 Resume 方法),可与 Runner 的 checkpoint/resume 机制配合使用。
-### 任务委派与 SubAgents 调用
-
-**TaskTool**
+---
-所有子 Agent 会被绑定到 TaskTool 上,当主 Agent 分配子任务给子 Agent 处理时,它会调用 TaskTool,并指明需要哪个子代理及执行的任务。TaskTool 随后将任务路由到指定的子代理,并在其执行完毕后,将结果返回给主 Agent。TaskTool 的默认 Description 会说明调用子 Agent 的通用规则并拼接每个子 Agent 的 Description,开发者可以通过配置 `TaskToolDescriptionGenerator` 来自定义 TaskTool 的 Description。
+## 架构
-> 当用户配置了 Config.SubAgents 时,这些 Agent 会基于 ChatModelAgent AgentAsTool 的能力绑定到 TaskTool 上
+
-**上下文隔离**
+- **主 Agent**:系统入口,以 ReAct 方式调用工具完成任务
+- **ChatModel**(`model.BaseModel[M]`):负责推理与工具选择
+- **Tools**:
+ - `write_todos`:内置规划工具,将任务拆解为结构化 TODO 列表
+ - `task`:子 Agent 调用入口(路由参数:`subagent_type`、`description`)
+ - 内置工具(文件系统/Shell)+ 用户自定义工具(`ToolsConfig`)
+- **SubAgents**:上下文隔离,独立执行子任务
+ - `general-purpose`:默认子 Agent,拥有与主 Agent 相同的工具(除 task)和配置
+ - 自定义子 Agent(`Config.SubAgents`)
-Agent 之间的上下文隔离:
+---
-- 信息传递: 主 Agent 与子 Agent 之间不共享上下文。子 Agent 仅接收主 Agent 分配的子任务目标,不会接收整个任务的处理过程;主 Agent 仅接收子 Agent 的处理结果,不会接受子 Agent 的处理过程。
-- 避免污染: 这种隔离确保了子 Agent 的执行过程(如大量的工具调用和中间步骤)不会“污染”主代理的上下文,主代理只接收简洁、明确的最终答案。
+## 内置文件系统
-**general-purpose**
+| 配置字段 | 注册工具 | 说明 |
Backend | ls, read_file, write_file, edit_file, glob, grep | 文件系统操作 |
Shell | execute | 非流式命令执行,与 StreamingShell 互斥 |
StreamingShell | execute (streaming) | 流式命令执行,与 Shell 互斥 |
- - 优势:DeepAgents 将 Plan/RePlan 作为工具供主 Agent 自由调用,可以在任务中跳过不必要的规划,整体上减少模型调用次数、降低耗时与成本。
- - 劣势:任务规划与委派由一次模型调用完成,对模型能力要求更高,提示词调优也相对更困难。
+`write_todos` 工具将结构化 TODO 列表写入会话(key: `deep_agent_session_key_todos`),供后续推理参考。
-## DeepAgents 使用示例
+**TODO 结构**:
-### 场景说明
+```go
+type TODO struct {
+ Content string `json:"content"`
+ ActiveForm string `json:"activeForm"`
+ Status string `json:"status"` // "pending" | "in_progress" | "completed"
+}
+```
-Excel Agent 是一个“看得懂 Excel 的智能助手”,它先把问题拆解成步骤,再一步步执行并校验结果。它能理解用户问题与上传的文件内容,提出可行的解决方案,并选择合适的工具(系统命令、生成并运行 Python 代码、网络查询等等)完成任务。
+**工作流程**:
-在真实业务里,你可以把 Excel Agent 当成一位“Excel 专家 + 自动化工程师”。当你交付一个原始表格和目标描述,它会给出方案并完成执行:
+1. 模型接收用户输入
+2. 调用 `write_todos` 拆解任务,写入上下文
+3. 按 TODO 逐项执行(调用 task 或直接工具)
+4. 再次调用 `write_todos` 更新进度
-- **数据清理与格式化**:从一个包含大量数据的 Excel 文件中完成去重、空值处理、日期格式标准化操作。
-- **数据分析与报告生成**:从销售数据中提取每月的销售总额,聚合统计、透视,最终生成并导出图表报告。
-- **自动化预算计算**:根据不同部门的预算申请,自动计算总预算并生成部门预算分配表。
-- **数据匹配与合并**:将多个不同来源的客户信息表进行匹配合并,生成完整的客户信息数据库。
+> 💡
+> 对简单任务,每次都调用 write_todos 可能适得其反。内置 Prompt 已包含正反例指导何时使用。可通过自定义 Instruction 进一步调优。配置 WithoutWriteTodos=true 可完全关闭。
-用 DeepAgents 搭建的 Excel Agent 结构如下:
+---
-
+## 子 Agent 委派:task 工具
-1. 在主 Agent 添加 ReadFile 工具,允许主 Agent 查看文件内容,辅助子任务制定
-2. 添加 Code 和 WebSearch 两个子 Agent:Code 可以编写 python 代码来操作 excel 表格;WebSearch 可以搜索信息并总结。
+**TaskTool** 是所有子 Agent 的统一调用入口:
-### 代码实现
+- 参数:`subagent_type`(目标子 Agent 名称)、`description`(任务描述)
+- 内部通过 `adk.NewTypedAgentTool` 将每个子 Agent 包装为工具
+- 默认 Description 包含所有可用子 Agent 的名称与说明;可通过 `TaskToolDescriptionGenerator` 自定义
-[https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
+**上下文隔离**:
-### 运行结果
+- 子 Agent 仅接收主 Agent 分配的任务描述,不共享对话历史
+- 主 Agent 仅接收子 Agent 的最终结果,中间步骤不回传
+- 避免大量工具调用和中间推理"污染"主 Agent 上下文
-```yaml
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool name: task
-arguments: {"subagent_type":"CodeAgent","description":"请帮我处理一个CSV文件操作任务。具体要求如下:1. 读取当前目录下的questions.csv文件;2. 提取该CSV文件中的第一列数据;3. 将提取出的第一列数据保存到一个新的CSV文件中,新文件命名为first_column.csv。请使用pandas库来完成这些操作,确保代码能够正确执行并处理可能出现的文件路径问题。"}
+**general-purpose 子 Agent**:
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool name: python_runner
-arguments: {"code":"```python\nimport pandas as pd\nimport os\n\n# 获取当前工作目录并构建文件路径\nbase_dir = os.getcwd()\ninput_path = os.path.join(base_dir, 'questions.csv')\noutput_path = os.path.join(base_dir, 'first_column.csv')\n\ntry:\n # 读取CSV文件\n df = pd.read_csv(input_path)\n # 提取第一列数据\n first_column = df.iloc[:, 0]\n # 保存为新CSV文件(不包含索引)\n first_column.to_csv(output_path, index=False)\n print(f\"成功将第一列数据保存至: {output_path}\")\nexcept FileNotFoundError:\n print(f\"错误: 未找到文件 {input_path}\")\nexcept Exception as e:\n print(f\"处理过程中发生错误: {str(e)}\")\n```"}
+- 默认创建,拥有与主 Agent 相同的工具(除 task)、Instruction 和 ModelFailoverConfig
+- 用于在隔离上下文中执行无专门子 Agent 的通用任务
+- 配置 `WithoutGeneralSubAgent=true` 可关闭
-name: ExcelAgent
-path: [{ExcelAgent}]
-tool response: 成功将第一列数据保存至: /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv
+---
+## 与其他方案对比
-name: ExcelAgent
-path: [{ExcelAgent}]
-answer: 任务已完成。已成功读取当前目录下的 `questions.csv` 文件,提取第一列数据,并将结果保存至 `first_column.csv`。具体输出路径如下:
+| 维度 | DeepAgents vs ReAct | DeepAgents vs Plan-and-Execute |
| 优势 | 内置规划 + 子 Agent 上下文隔离,多步任务效果更优 | Plan/RePlan 作为工具按需调用,减少不必要的规划开销 |
| 劣势 | 规划 + 子 Agent 调用增加模型请求、耗时与 token 成本 | 规划与委派在单次调用中完成,对模型能力要求更高 |
-代码已处理路径拼接和异常捕获(如文件不存在或格式错误),确保执行稳定性。
+- 主 Agent 配置 ReadFile 工具辅助任务制定
+- 添加 Code(Python 操作 Excel)和 WebSearch 两个子 Agent
-name: ExcelAgent
-path: [{ExcelAgent}]
-answer: 已成功将 `questions.csv` 表格中的第一列数据提取至新文件 `first_column.csv`,文件保存路径为
-:
+### 代码
-`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c4
-4b131973/first_column.csv`
+完整示例:[https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
-操作过程中已处理路径拼接和异常捕获(如文件不存在、格式错误等问题),确保数据
-提取完整性和文件生成稳定性。若需要调整文件路径或对数据格式有进一步要求,请随时告知
-。
+```go
+agent, err := deep.New(ctx, &deep.Config{
+ Name: "ExcelAgent",
+ ChatModel: myModel,
+ Backend: localBackend,
+ SubAgents: []adk.Agent{codeAgent, webSearchAgent},
+ ToolsConfig: adk.ToolsConfig{
+ InvokableTools: []tool.InvokableTool{readFileTool},
+ },
+})
```
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
index 1f3ba69429e..e90c0266c67 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
title: Plan-Execute Agent
-weight: 4
+weight: 2
---
## Plan-Execute Agent 概述
@@ -275,7 +275,7 @@ func newPlanExecuteAgent(ctx context.Context) adk.Agent {
replanner := newReplanner(ctx, model)
// 组合为 PlanExecuteAgent(固定 execute - replan 最大迭代 10 次)
- planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{
+ planExecuteAgent, err := planexecute.New(ctx, &planexecute.PlanExecuteConfig{
Planner: planner,
Executor: executor,
Replanner: replanner,
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
deleted file mode 100644
index f583790aa52..00000000000
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
+++ /dev/null
@@ -1,499 +0,0 @@
----
-Description: ""
-date: "2026-03-02"
-lastmod: ""
-tags: []
-title: Supervisor Agent
-weight: 3
----
-
-## Supervisor Agent 概述
-
-### Import Path
-
-`import ``github.com/cloudwego/eino/adk/prebuilt/supervisor`
-
-### 什么是 Supervisor Agent?
-
-Supervisor Agent 是一种中心化多 Agent 协作模式,由一个监督者(Supervisor Agent) 和多个子 Agent(SubAgents)组成。Supervisor 负责任务的分配、子 Agent 执行过程的监控,以及子 Agent 完成后的结果汇总与下一步决策;子 Agent 则专注于执行具体任务,并在完成后通过 WithDeterministicTransferTo 自动将任务控制权交回 Supervisor。
-
-
-
-该模式适用于需要动态协调多个专业 Agent 完成复杂任务的场景,例如:
-
-- 科研项目管理(Supervisor 分配调研、实验、报告撰写任务给不同子 Agent)。
-- 客户服务流程(Supervisor 根据用户问题类型,分配给技术支持、售后、销售等子 Agent)。
-
-### Supervisor Agent 结构
-
-Supervisor 模式的核心结构如下:
-
-- **Supervisor Agent**:作为协作核心,具备任务分配逻辑(如基于规则或 LLM 决策),可通过 `SetSubAgents` 将子 Agent 纳入管理。
-- **SubAgents**:每个子 Agent 被 WithDeterministicTransferTo 增强,预设 `ToAgentNames` 为 Supervisor 名称,确保任务完成后自动转让回 Supervisor。
-
-### Supervisor Agent 特点
-
-1. **确定性回调**:子 Agent 执行完毕(未中断)后,通过 WithDeterministicTransferTo 自动触发 Transfer 事件,将任务控制权交回 Supervisor,避免协作流程中断。
-2. **中心化控制**:Supervisor 统一管理子 Agent,可根据子 Agent 的执行结果动态调整任务分配(如分配给其他子 Agent 或直接生成最终结果)。
-3. **松耦合扩展**:子 Agent 可独立开发、测试和替换,只需确保实现 Agent 接口并绑定到 Supervisor,即可接入协作流程。
-4. **支持中断与恢复**:若子 Agent 或 Supervisor 支持 `ResumableAgent` 接口,协作流程可在中断后恢复,保持任务上下文连续性。
-
-### Supervisor Agent 运行流程
-
-Supervisor 模式的典型协作流程如下:
-
-1. **任务启动**:Runner 触发 Supervisor 运行,输入初始任务(如“完成一份 LLM 发展历史报告”)。
-2. **任务分配**:Supervisor 根据任务需求,通过 Transfer 事件将任务转让给指定子 Agent(如“调研 Agent”)。
-3. **子 Agent 执行**:子 Agent 执行具体任务(如调研 LLM 关键里程碑),并生成执行结果事件。
-4. **自动回调**:子 Agent 完成后,WithDeterministicTransferTo 触发 Transfer 事件,将任务转让回 Supervisor。
-5. **结果处理**:Supervisor 接收子 Agent 的结果,决定下一步(如分配给“报告撰写 Agent”继续处理,或直接输出最终结果)。
-
-## Supervisor Agent 使用示例
-
-### 场景说明
-
-创建一个科研报告生成系统:
-
-- **Supervisor**:基于用户输入的研究主题,分配任务给“调研 Agent”和“撰写 Agent”,并汇总最终报告。
-- **调研 Agent**:负责生成研究计划(如 LLM 发展的关键阶段)。
-- **撰写 Agent**:负责根据调研计划撰写完整报告。
-
-### 代码实现
-
-#### 步骤 1:实现子 Agent
-
-首先创建两个子 Agent,分别负责调研和撰写任务:
-
-```go
-// 调研 Agent:生成研究计划
-func NewResearchAgent(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ResearchAgent",
- Description: "Generates a detailed research plan for a given topic.",
- Instruction: `
-You are a research planner. Given a topic, output a step-by-step research plan with key stages and milestones.
-Output ONLY the plan, no extra text.`,
- Model: model,
- })
- return agent
-}
-
-// 撰写 Agent:根据研究计划撰写报告
-func NewWriterAgent(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "WriterAgent",
- Description: "Writes a report based on a research plan.",
- Instruction: `
-You are an academic writer. Given a research plan, expand it into a structured report with details and analysis.
-Output ONLY the report, no extra text.`,
- Model: model,
- })
- return agent
-}
-```
-
-#### 步骤 2:实现 Supervisor Agent
-
-创建 Supervisor Agent,定义任务分配逻辑(此处简化为基于规则:先分配给调研 Agent,再分配给撰写 Agent):
-
-```go
-// Supervisor Agent:协调调研和撰写任务
-func NewReportSupervisor(model model.ToolCallingChatModel) adk.Agent {
- agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ReportSupervisor",
- Description: "Coordinates research and writing to generate a report.",
- Instruction: `
-You are a project supervisor. Your task is to coordinate two sub-agents:
-- ResearchAgent: generates a research plan.
-- WriterAgent: writes a report based on the plan.
-
-Workflow:
-1. When receiving a topic, first transfer the task to ResearchAgent.
-2. After ResearchAgent finishes, transfer the task to WriterAgent with the plan as input.
-3. After WriterAgent finishes, output the final report.`,
- Model: model,
- })
- return agent
-}
-```
-
-#### 步骤 3:组合 Supervisor 与子 Agent
-
-使用 `NewSupervisor` 将 Supervisor 和子 Agent 组合:
-
-```go
-import (
- "context"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/adk/prebuilt/supervisor"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-func main() {
- ctx := context.Background()
-
- // 1. 创建 LLM 模型(如 GPT-4o)
- model, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: "YOUR_API_KEY",
- Model: "gpt-4o",
- })
-
- // 2. 创建子 Agent 和 Supervisor
- researchAgent := NewResearchAgent(model)
- writerAgent := NewWriterAgent(model)
- reportSupervisor := NewReportSupervisor(model)
-
- // 3. 组合 Supervisor 与子 Agent
- supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{
- Supervisor: reportSupervisor,
- SubAgents: []adk.Agent{researchAgent, writerAgent},
- })
-
- // 4. 运行 Supervisor 模式
- iter := supervisorAgent.Run(ctx, &adk.AgentInput{
- Messages: []adk.Message{
- schema.UserMessage("Write a report on the history of Large Language Models."),
- },
- EnableStreaming: true,
- })
-
- // 5. 消费事件流(打印结果)
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
- if event.Output != nil && event.Output.MessageOutput != nil {
- msg, _ := event.Output.MessageOutput.GetMessage()
- println("Agent[" + event.AgentName + "]:\n" + msg.Content + "\n===========")
- }
- }
-}
-```
-
-### 运行结果
-
-```markdown
-Agent[ReportSupervisor]:
-
-===========
-Agent[ReportSupervisor]:
-successfully transferred to agent [ResearchAgent]
-===========
-Agent[ResearchAgent]:
-1. **Scope Definition & Background Research**
- - Task: Define "Large Language Model" (LLM) for the report (e.g., size thresholds, key characteristics: transformer-based, large-scale pretraining, general-purpose).
- - Task: Identify foundational NLP/AI concepts pre-LLMs (statistical models, early neural networks, word embeddings) to contextualize origins.
- - Milestone: 3-day literature review of academic definitions, industry reports, and AI historiographies to finalize scope.
-
-2. **Chronological Periodization**
- - Task: Divide LLM history into distinct eras (e.g., Pre-2017: Pre-transformer foundations; 2017-2020: Transformer revolution & early LLMs; 2020-Present: Scaling & mainstream adoption).
- - Task: Map key events, models, and breakthroughs per era (e.g., 2017: "Attention Is All You Need"; 2018: GPT-1/BERT; 2020: GPT-3; 2022: ChatGPT; 2023: Llama 2).
- - Milestone: 10-day timeline draft with annotated model releases, research papers, and technological shifts.
-
-3. **Key Technical Milestones**
- - Task: Deep-dive into critical innovations (transformer architecture, pretraining-fine-tuning paradigm, scaling laws, in-context learning).
- - Task: Extract details from seminal papers (authors, institutions, methodologies, performance benchmarks).
- - Milestone: 1-week analysis of 5-7 foundational papers (e.g., Vaswani et al. 2017; Radford et al. 2018; Devlin et al. 2018) with technical summaries.
-
-4. **Stakeholder Mapping**
- - Task: Identify key organizations (OpenAI, Google DeepMind, Meta AI, Microsoft Research) and academic labs (Stanford, Berkeley) driving LLM development.
- - Task: Document institutional contributions (e.g., OpenAI’s GPT series, Google’s BERT/PaLM, Meta’s Llama) and research priorities (open vs. closed models).
- - Milestone: 5-day stakeholder profile draft with org-specific timelines and model lineages.
-
-5. **Technical Evolution & Innovation Trajectory**
- - Task: Analyze shifts in architecture (from RNNs/LSTMs to transformers), training paradigms (pretraining + fine-tuning → instruction tuning → RLHF), and compute scaling (parameters, data size, GPU usage over time).
- - Task: Link technical changes to performance improvements (e.g., GPT-1 (124M params) vs. GPT-4 (100B+ params): task generalization, emergent abilities).
- - Milestone: 1-week technical trajectory report with data visualizations (param scaling, benchmark scores over time).
-
-6. **Impact & Societal Context**
- - Task: Research LLM impact on NLP tasks (translation, summarization, QA) and beyond (education, content creation, policy).
- - Task: Document cultural/industry shifts (rise of prompt engineering, "AI-native" products, public perception post-ChatGPT).
- - Milestone: 5-day impact analysis integrating case studies (e.g., GitHub Copilot, healthcare LLMs) and media/scholarly discourse.
-
-7. **Challenges & Critiques (Historical Perspective)**
- - Task: Track historical limitations (pre-2020: data sparsity, task specificity; post-2020: bias, misinformation, energy use) and responses (e.g., 2019: BERT bias audits; 2023: EU AI Act).
- - Task: Cite key critiques (e.g., "On the Dangers of Stochastic Parrots," 2021) and industry/academic reactions.
- - Milestone: 5-day challenge timeline linking issues to their emergence and mitigation efforts.
-
-8. **Synthesis & Narrative Drafting**
- - Task: Integrate chronological, technical, and societal data into a coherent narrative (origins → revolution → scaling → mainstream impact).
- - Task: Outline report structure (Abstract, Introduction, Era-by-Era Analysis, Key Innovations, Stakeholders, Impact, Challenges, Conclusion).
- - Milestone: 1-week first draft of full report (8,000–10,000 words).
-
-9. **Validation & Fact-Checking**
- - Task: Verify model release dates, paper citations, parameter counts, and stakeholder claims via primary sources (original papers, official press releases, archived GitHub repos).
- - Task: Cross-check with secondary sources (AI history books, expert interviews, peer-reviewed historiographies).
- - Milestone: 3-day validation report flagging/correcting inaccuracies.
-
-10. **Finalization & Revision**
- - Task: Edit for clarity, narrative flow, and consistency; refine visuals (timelines, param scaling charts).
- - Task: Format references (APA/MLA) and appendices (model comparison table, key paper list).
- - Milestone: 2-day final report submission.
-===========
-Agent[ResearchAgent]:
-
-===========
-Agent[ResearchAgent]:
-successfully transferred to agent [ReportSupervisor]
-===========
-Agent[ReportSupervisor]:
-
-===========
-Agent[ReportSupervisor]:
-successfully transferred to agent [WriterAgent]
-===========
-Agent[WriterAgent]:
-# The History of Large Language Models: From Foundations to Mainstream Revolution
-
-
-## Abstract
-Large Language Models (LLMs) represent one of the most transformative technological innovations of the 21st century, enabling machines to understand, generate, and manipulate human language with unprecedented fluency. This report traces the historical trajectory of LLMs, from their conceptual roots in early natural language processing (NLP) to their current status as mainstream tools. It examines key technical milestones—including the invention of the transformer architecture, the rise of pretraining-fine-tuning paradigms, and the scaling of model parameters—and contextualizes these within the contributions of academic labs and tech giants. The report also analyzes societal impacts, from revolutionizing NLP tasks to sparking debates over bias, misinformation, and AI regulation. By synthesizing chronological, technical, and cultural data, this history reveals how LLMs evolved from niche research experiments to agents of global change.
-
-
-## 1. Introduction: Defining Large Language Models
-A **Large Language Model (LLM)** is a type of machine learning model designed to process and generate human language by learning patterns from massive text datasets. Key characteristics include: (1) a transformer-based architecture, enabling parallel processing of text sequences; (2) large-scale pretraining on diverse corpora (e.g., books, websites, articles); (3) general-purpose functionality, allowing adaptation to tasks like translation, summarization, or dialogue without task-specific engineering; and (4) scale, typically defined by billions (or tens of billions) of parameters (adjustable weights that capture linguistic patterns).
-
-LLMs emerged from decades of NLP research, building on foundational concepts like statistical models (e.g., n-grams), early neural networks (e.g., recurrent neural networks [RNNs]), and word embeddings (e.g., Word2Vec, GloVe). By the 2010s, these predecessors had laid groundwork for "language understanding," but were limited by task specificity (e.g., a model trained for translation could not summarize text) and data sparsity. LLMs addressed these gaps by prioritizing scale, generality, and architectural innovation—ultimately redefining the boundaries of machine language capability.
-
-
-## 2. Era-by-Era Analysis: The Evolution of LLMs
-
-### 2.1 Pre-2017: Pre-Transformer Foundations (1950s–2016)
-The roots of LLMs lie in mid-20th-century NLP, when researchers first sought to automate language tasks. Early efforts relied on rule-based systems (e.g., 1950s machine translation using syntax rules) and statistical methods (e.g., 1990s n-gram models for speech recognition). By the 2010s, neural networks gained traction: RNNs and long short-term memory (LSTM) models (Hochreiter & Schmidhuber, 1997) enabled sequence modeling, while word embeddings (Mikolov et al., 2013) represented words as dense vectors, capturing semantic relationships.
-
-Despite progress, pre-2017 models faced critical limitations: RNNs/LSTMs processed text sequentially, making them slow to train and unable to handle long-range dependencies (e.g., linking "it" in a sentence to a noun paragraphs earlier). Data was also constrained: models like Word2Vec trained on millions, not billions, of tokens. These bottlenecks set the stage for a paradigm shift.
-
-
-### 2.2 2017–2020: The Transformer Revolution and Early LLMs
-The year 2017 marked the dawn of the LLM era with the publication of *"Attention Is All You Need"* (Vaswani et al.), which introduced the **transformer architecture**. Unlike RNNs, transformers use "self-attention" mechanisms to weigh the importance of different words in a sequence simultaneously, enabling parallel computation and capturing long-range dependencies. This breakthrough reduced training time and improved performance on language tasks.
-
-#### Key Models and Breakthroughs:
-- **2018**: OpenAI released **GPT-1** (Radford et al.), the first transformer-based LLM. With 124 million parameters, it introduced the "pretraining-fine-tuning" paradigm: pretraining on a large unlabeled corpus (BooksCorpus) to learn general language patterns, then fine-tuning on task-specific labeled data (e.g., sentiment analysis).
-- **2018**: Google published **BERT** (Devlin et al.), a bidirectional transformer that processed text from left-to-right *and* right-to-left, outperforming GPT-1 on context-dependent tasks like question answering. BERT’s success popularized "contextual embeddings," where word meaning depends on surrounding text (e.g., "bank" as a financial institution vs. a riverbank).
-- **2019**: OpenAI scaled up with **GPT-2** (1.5 billion parameters), demonstrating improved text generation but sparking early concerns about misuse (OpenAI initially delayed full release over fears of disinformation).
-- **2020**: Google’s **T5** (Text-to-Text Transfer Transformer) unified NLP tasks under a single "text-to-text" framework (e.g., translating "translate English to French: Hello" to "Bonjour"), simplifying model adaptation.
-
-
-### 2.3 2020–Present: Scaling, Emergence, and Mainstream Adoption
-The 2020s saw LLMs transition from research curiosities to global phenomena, driven by exponential scaling of parameters, data, and compute.
-
-#### Key Developments:
-- **2020**: OpenAI’s **GPT-3** (175 billion parameters) marked a turning point. Trained on 45 terabytes of text, it exhibited "few-shot" and "zero-shot" learning—adapting to tasks with minimal examples (e.g., "Write a poem about AI" with no prior poetry training). GPT-3’s release via API (OpenAI Playground) introduced LLMs to developers, enabling early applications like chatbots and code generation.
-- **2022**: **ChatGPT** (based on GPT-3.5) brought LLMs to the public. Launched in November, its user-friendly interface and conversational ability sparked a viral explosion (100 million users by January 2023). ChatGPT refined training with **Reinforcement Learning from Human Feedback (RLHF)**, aligning outputs with human preferences (e.g., helpfulness, safety).
-- **2023**: Meta released **Llama 2** (7B–70B parameters), an open-source LLM that lowered barriers to entry, allowing researchers and startups to fine-tune models without proprietary access. Meanwhile, OpenAI’s **GPT-4** (100B+ parameters) expanded multimodality (text + images) and improved reasoning (e.g., solving math problems, coding).
-- **2023–2024**: The "race to scale" continued with models like Google’s **PaLM 2** (540B parameters), Anthropic’s **Claude 2** (200B+ parameters), and open-source alternatives (e.g., Mistral, Falcon). Compute usage skyrocketed: training GPT-3 required ~3.14e23 floating-point operations (FLOPs), equivalent to 355 years of a single GPU’s work.
-
-
-## 3. Key Technical Milestones
-### 3.1 The Transformer Architecture (2017)
-Vaswani et al.’s *"Attention Is All You Need"* (Google, University of Toronto) replaced RNNs with self-attention, a mechanism that computes "attention scores" between every pair of words in a sequence. For example, in "The cat sat on the mat; it purred," self-attention links "it" to "cat." This parallel processing reduced training time from weeks (for RNNs) to days, enabling larger models.
-
-### 3.2 Pretraining-Fine-Tuning Paradigm (2018)
-GPT-1 and BERT established the now-standard workflow: (1) Pretrain on a large, unlabeled corpus (e.g., Common Crawl, a web scrape of 1.1 trillion tokens) to learn syntax, semantics, and world knowledge; (2) Fine-tune on task-specific data (e.g., GLUE, a benchmark of 10 NLP tasks). This decoupled language learning from task engineering, enabling generalization.
-
-### 3.3 Scaling Laws and Emergent Abilities (2020s)
-In 2020, OpenAI researchers articulated **scaling laws**: model performance improves predictably with increased parameters, data, and compute. By 2022, this led to "emergent abilities"—skills not present in smaller models, such as GPT-3’s in-context learning or GPT-4’s multi-step reasoning.
-
-### 3.4 Instruction Tuning and RLHF (2022)
-Post-2020, training shifted from task-specific fine-tuning to **instruction tuning** (training on natural language instructions like "Summarize this article") and **RLHF** (rewarding models for human-preferred outputs). These methods made LLMs more usable: ChatGPT, for instance, follows prompts like "Explain quantum physics like I’m 5" without explicit fine-tuning.
-
-
-## 4. Stakeholders: The Ecosystem of LLM Development
-LLM evolution has been driven by a mix of tech giants, academic labs, and startups, each with distinct priorities:
-
-### 4.1 Tech Giants: Closed vs. Open Models
-- **OpenAI** (founded 2015, backed by Microsoft): Pioneered the GPT series, prioritizing commercialization via closed APIs (e.g., ChatGPT Plus, GPT-4 API). Focus: user-friendliness and safety (via RLHF).
-- **Google DeepMind**: Developed BERT, T5, and PaLM, integrating LLMs into products like Google Search (via BERT) and Bard. Balances closed (PaLM) and open (T5) models.
-- **Meta AI**: Advocated for open science with Llama 1/2 (2023), releasing weights for research and commercial use. Meta’s "open" approach aims to democratize LLM access and accelerate safety research.
-- **Microsoft**: Partnered with OpenAI (2019–present), providing Azure compute and integrating GPT into Bing (search), Office (Copilot), and GitHub (Copilot X for coding).
-
-### 4.2 Academic Labs
-- **Stanford NLP**: Contributed to BERT and T5 research; developed HELM (Holistic Evaluation of Language Models), a benchmark for LLM safety and fairness.
-- **UC Berkeley**: Studied LLM bias (e.g., 2021 paper "On the Dangers of Stochastic Parrots," critiquing LLMs as "statistical mimics" lacking true understanding).
-
-
-## 5. Impact & Societal Context
-### 5.1 Transforming NLP and Beyond
-LLMs have redefined NLP performance: By 2023, GPT-4 outperformed humans on the MMLU benchmark (a test of 57 subjects, including math, law, and biology), scoring 86.4% vs. 86.5% for humans. Beyond NLP, they have revolutionized:
-- **Content Creation**: Tools like Jasper and Copy.ai automate marketing copy; artists use DALL-E (paired with LLMs) for text-to-image generation.
-- **Education**: Khan Academy’s Khanmigo tutors students; Coursera uses LLMs for personalized feedback.
-- **Coding**: GitHub Copilot (2021) generates code from comments, boosting developer productivity by 55% (Microsoft, 2023).
-
-### 5.2 Cultural Shifts
-- **Prompt Engineering**: The rise of "prompt engineers"—professionals skilled in crafting text inputs to elicit desired LLM outputs—became a new career path.
-- **AI-Native Products**: Startups like Character.AI (chatbots with distinct personalities) and Perplexity (AI-powered search) emerged as "LLM-first" services.
-- **Public Perception**: Post-ChatGPT, LLMs shifted from "AI hype" to tangible utility, though skepticism persists (e.g., 62% of U.S. adults worry about job displacement, Pew Research, 2023).
-
-
-## 6. Challenges & Critiques: A Historical Perspective
-### 6.1 Technical Limitations
-- **Pre-2020**: Data sparsity (small corpora limited generalization); task specificity (models like BERT required retraining for new tasks).
-- **Post-2020**: **Hallucinations** (fabricating facts, e.g., GPT-3 citing fake research papers); **energy use** (training GPT-3 emitted ~500 tons of CO₂, equivalent to 125 round-trip flights from NYC to London); **computational inequality** (only tech giants can afford 100B+ parameter models).
-
-### 6.2 Societal Risks
-- **Bias**: Early LLMs mirrored training data biases (e.g., BERT associated "doctor" with "male" in 2019 audits). Responses included bias mitigation datasets (e.g., WinoBias) and audits (e.g., Stanford’s Gender Shades).
-- **Misinformation**: GPT-2’s realistic text generation prompted calls for regulation; by 2023, deepfakes (e.g., AI-generated political speeches) became a policy focus.
-- **Regulation**: The EU AI Act (2024) classified LLMs as "high-risk," requiring transparency (e.g., disclosing AI-generated content) and safety testing.
-
-
-## 7. Conclusion: A Revolution in Five Years
-The history of LLMs is a story of exponential progress: from the transformer’s 2017 invention to ChatGPT’s 2022 viral explosion, a mere five years. What began as an academic breakthrough—parallelizing text processing with self-attention—evolved into a technology that writes code, tutors students, and shapes global policy.
-
-Yet challenges persist: scaling has outpaced our understanding of how LLMs "think," and debates over bias, energy use, and access (closed vs. open models) intensify. As we look to the future, this history reminds us that LLMs are not just technical achievements, but mirrors of society—reflecting both our ingenuity and our flaws. Their next chapter will depend on balancing innovation with responsibility, ensuring these models serve as tools for collective progress.
-
-
-## References
-- Devlin, J., et al. (2018). *BERT: Pre-training of deep bidirectional transformers for language understanding*. NAACL.
-- Hochreiter, S., & Schmidhuber, J. (1997). *Long short-term memory*. Neural Computation.
-- Mikolov, T., et al. (2013). *Efficient estimation of word representations in vector space*. ICLR.
-- Radford, A., et al. (2018). *Improving language understanding by generative pre-training*. OpenAI.
-- Vaswani, A., et al. (2017). *Attention is all you need*. NeurIPS.
-- Weidinger, L., et al. (2021). *On the dangers of stochastic parrots: Can language models be too big?*. ACM FAccT.
-===========
-Agent[WriterAgent]:
-
-===========
-Agent[WriterAgent]:
-successfully transferred to agent [ReportSupervisor]
-===========
-```
-
-## WithDeterministicTransferTo
-
-### 什么是 WithDeterministicTransferTo?
-
-`WithDeterministicTransferTo` 是 Eino ADK 提供的 Agent 增强工具,用于为 Agent 注入任务转让(Transfer)能力 。它允许开发者为目标 Agent 预设固定的任务转让路径,当该 Agent 完成任务(未被中断)时,会自动生成 Transfer 事件,将任务流转到预设的目标 Agent。
-
-这一能力是构建 Supervisor Agent 协作模式的基础,确保子 Agent 在执行完毕后能可靠地将任务控制权交回监督者(Supervisor),形成“分配-执行-反馈”的闭环协作流程。
-
-### WithDeterministicTransferTo 核心实现
-
-#### 配置结构
-
-通过 `DeterministicTransferConfig` 定义任务转让的核心参数:
-
-```go
-// 包装方法
-func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent
-
-// 配置详情
-type DeterministicTransferConfig struct {
- Agent Agent // 被增强的目标 Agent
- ToAgentNames []string // 任务完成后转让的目标 Agent 名称列表
-}
-```
-
-- `Agent`:需要添加转让能力的原始 Agent。
-- `ToAgentNames`:当 `Agent` 完成任务且未中断时,自动转让任务的目标 Agent 名称列表(按顺序转让)。
-
-#### Agent 包装
-
-WithDeterministicTransferTo 会对原始 Agent 进行包装,根据其是否实现 `ResumableAgent` 接口(支持中断与恢复),分别返回 `agentWithDeterministicTransferTo` 或 `resumableAgentWithDeterministicTransferTo` 实例,确保增强能力与 Agent 原有功能(如 `Resume` 方法)兼容。
-
-包装后的 Agent 会覆盖 `Run` 方法(对 `ResumableAgent` 还会覆盖 `Resume` 方法),在原始 Agent 的事件流基础上追加 Transfer 事件:
-
-```go
-// 对普通 Agent 的包装
-type agentWithDeterministicTransferTo struct {
- agent Agent // 原始 Agent
- toAgentNames []string // 目标 Agent 名称列表
-}
-
-// Run 方法:执行原始 Agent 任务,并在任务完成后追加 Transfer 事件
-func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] {
- aIter := a.agent.Run(ctx, input, options...)
-
- iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
-
- // 异步处理原始事件流,并追加 Transfer 事件
- go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
-
- return iterator
-}
-```
-
-对于 `ResumableAgent`,额外实现 `Resume` 方法,确保恢复执行后仍能触发确定性转让:
-
-```go
-type resumableAgentWithDeterministicTransferTo struct {
- agent ResumableAgent // 支持恢复的原始 Agent
- toAgentNames []string // 目标 Agent 名称列表
-}
-
-// Resume 方法:恢复执行原始 Agent 任务,并在完成后追加 Transfer 事件
-func (a *resumableAgentWithDeterministicTransferTo) Resume(ctx context.Context, info *ResumeInfo, opts ...AgentRunOption) *AsyncIterator[*AgentEvent] {
- aIter := a.agent.Resume(ctx, info, opts...)
- iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
- go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
- return iterator
-}
-```
-
-#### 事件流追加 Transfer 事件
-
-`appendTransferAction` 是实现确定性转让的核心逻辑,它会消费原始 Agent 的事件流,在 Agent 任务正常结束(未中断)后,自动生成并发送 Transfer 事件到目标 Agent:
-
-```go
-func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent], generator *AsyncGenerator[*AgentEvent], toAgentNames []string) {
- defer func() {
- // 异常处理:捕获 panic 并通过事件传递错误
- if panicErr := recover(); panicErr != nil {
- generator.Send(&AgentEvent{Err: safe.NewPanicErr(panicErr, debug.Stack())})
- }
- generator.Close() // 事件流结束,关闭生成器
- }()
-
- interrupted := false
-
- // 1. 转发原始 Agent 的所有事件
- for {
- event, ok := aIter.Next()
- if !ok { // 原始事件流结束
- break
- }
- generator.Send(event) // 转发事件给调用方
-
- // 检查是否发生中断(如 InterruptAction)
- if event.Action != nil && event.Action.Interrupted != nil {
- interrupted = true
- } else {
- interrupted = false
- }
- }
-
- // 2. 若未中断且存在目标 Agent,生成 Transfer 事件
- if !interrupted && len(toAgentNames) > 0 {
- for _, toAgentName := range toAgentNames {
- // 生成转让消息(系统提示 + Transfer 动作)
- aMsg, tMsg := GenTransferMessages(ctx, toAgentName)
- // 发送系统提示事件(告知用户任务转让)
- aEvent := EventFromMessage(aMsg, nil, schema.Assistant, "")
- generator.Send(aEvent)
- // 发送 Transfer 动作事件(触发任务转让)
- tEvent := EventFromMessage(tMsg, nil, schema.Tool, tMsg.ToolName)
- tEvent.Action = &AgentAction{
- TransferToAgent: &TransferToAgentAction{
- DestAgentName: toAgentName, // 目标 Agent 名称
- },
- }
- generator.Send(tEvent)
- }
- }
-}
-```
-
-**关键逻辑**:
-
-- **事件转发**:原始 Agent 产生的所有事件(如思考、工具调用、输出结果)会被完整转发,确保业务逻辑不受影响。
-- **中断检查**:若 Agent 执行过程中被中断(如 `InterruptAction`),则不触发 Transfer(中断视为任务未正常完成)。
-- **Transfer 事件生成**:任务正常结束后,为每个 `ToAgentNames` 生成两条事件:
- 1. 系统提示事件(`schema.Assistant` 角色):告知用户任务将转让给目标 Agent。
- 2. Transfer 动作事件(`schema.Tool` 角色):携带 `TransferToAgentAction`,触发 ADK 运行时将任务转让给 `DestAgentName` 对应的 Agent。
-
-## 总结
-
-WithDeterministicTransferTo 为 Agent 提供了可靠的任务转让能力,是构建 Supervisor 模式的核心基石;而 Supervisor 模式通过中心化协调与确定性回调,实现了多 Agent 之间的高效协作,显著降低了复杂任务的开发与维护成本。结合两者,开发者可快速搭建灵活、可扩展的多 Agent 系统。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
deleted file mode 100644
index f03c7c390c9..00000000000
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
+++ /dev/null
@@ -1,1265 +0,0 @@
----
-Description: ""
-date: "2026-03-09"
-lastmod: ""
-tags: []
-title: Workflow Agents
-weight: 2
----
-
-# Workflow Agents 概述
-
-## 导入路径
-
-`import ``github.com/cloudwego/eino/adk`
-
-## 什么是 Workflow Agents
-
-Workflow Agents 是 eino ADK 中的一种特殊 Agent 类型,它允许开发者以预设的流程来组织和执行多个子 Agent。
-
-与基于 LLM 自主决策的 Transfer 模式不同,Workflow Agents 采用**预设决策**的方式,按照代码中定义好的执行流程来运行子 Agent,提供了更可预测和可控的多 Agent 协作方式。
-
-Eino ADK 提供了三种基础的 Workflow Agent 类型:
-
-- **SequentialAgent**:按顺序依次执行子 Agent
-- **LoopAgent**:循环执行子 Agent 序列
-- **ParallelAgent**:并发执行多个子 Agent
-
-这些 Workflow Agent 可以相互嵌套,构建更复杂的执行流程,满足各种业务场景需求。
-
-# SequentialAgent
-
-## 功能
-
-SequentialAgent 是最基础的 Workflow Agent,它按照配置中提供的顺序,依次执行一系列子 Agent。每个子 Agent 执行完成后,其输出会通过 History 机制传递给下一个子 Agent,形成一个线性的执行链。
-
-
-
-```go
-type SequentialAgentConfig struct {
- Name string // Agent 名称
- Description string // Agent 描述
- SubAgents []Agent // 子 Agent 列表,按执行顺序排列
-}
-
-func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
-```
-
-SequentialAgent 的执行遵循以下设定:
-
-1. **线性执行**:严格按照 SubAgents 数组的顺序执行
-2. **History 传递**:每个 Agent 的执行结果都会被添加到 History 中,后续 Agent 可以访问前面 Agent 的执行历史
-3. **提前退出**:如果任何一个子 Agent 产生 ExitAction / Interrupt,整个 Sequential 流程会立即终止
-
-SequentialAgent 适用于以下场景:
-
-- **多步骤处理流程**:如数据预处理 -> 分析 -> 生成报告
-- **管道式处理**:每个步骤的输出作为下个步骤的输入
-- **有依赖关系的任务序列**:后续任务依赖前面任务的结果
-
-## 示例
-
-示例展示了如何使用 SequentialAgent 创建一个三步骤的文档处理流水线:
-
-1. **DocumentAnalyzer**:分析文档内容
-2. **ContentSummarizer**:总结分析结果
-3. **ReportGenerator**:生成最终报告
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-// 创建 ChatModel 实例
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// 文档分析 Agent
-func NewDocumentAnalyzerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "DocumentAnalyzer",
- Description: "分析文档内容并提取关键信息",
- Instruction: "你是一个文档分析专家。请仔细分析用户提供的文档内容,提取其中的关键信息、主要观点和重要数据。",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 内容总结 Agent
-func NewContentSummarizerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ContentSummarizer",
- Description: "对分析结果进行总结",
- Instruction: "基于前面的文档分析结果,生成一个简洁明了的总结,突出最重要的发现和结论。",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 报告生成 Agent
-func NewReportGeneratorAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ReportGenerator",
- Description: "生成最终的分析报告",
- Instruction: "基于前面的分析和总结,生成一份结构化的分析报告,包含执行摘要、详细分析和建议。",
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // 创建三个处理步骤的 Agent
- analyzer := NewDocumentAnalyzerAgent()
- summarizer := NewContentSummarizerAgent()
- generator := NewReportGeneratorAgent()
-
- // 创建 SequentialAgent
- sequentialAgent, err := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
- Name: "DocumentProcessingPipeline",
- Description: "文档处理流水线:分析 → 总结 → 报告生成",
- SubAgents: []adk.Agent{analyzer, summarizer, generator},
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // 创建 Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: sequentialAgent,
- })
-
- // 执行文档处理流程
- input := "请分析以下市场报告:2024年第三季度,公司营收增长15%,主要得益于新产品线的成功推出。但运营成本也上升了8%,需要优化效率。"
-
- fmt.Println("开始执行文档处理流水线...")
- iter := runner.Query(ctx, input)
-
- stepCount := 1
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatal(event.Err)
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- fmt.Printf("\n=== 步骤 %d: %s ===\n", stepCount, event.AgentName)
- fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
- stepCount++
- }
- }
-
- fmt.Println("\n文档处理流水线执行完成!")
-}
-```
-
-运行结果为:
-
-```markdown
-开始执行文档处理流水线...
-
-=== 步骤 1: DocumentAnalyzer ===
-市场报告关键信息分析:
-
-1. 营收增长情况:
- - 2024年第三季度,公司营收同比增长15%。
- - 营收增长的主要驱动力是新产品线的成功推出。
-
-2. 成本情况:
- - 运营成本上涨了8%。
- - 成本上升提醒公司需要进行效率优化。
-
-主要观点总结:
-- 新产品线推出显著推动了营收增长,显示公司在产品创新方面取得良好成果。
-- 虽然营收提升,但运营成本的增加在一定程度上影响了盈利能力,指出了提升运营效率的重要性。
-
-重要数据:
-- 营收增长率:15%
-- 运营成本增长率:8%
-
-=== 步骤 2: ContentSummarizer ===
-总结:2024年第三季度,公司实现了15%的营收增长,主要归功于新产品线的成功推出,体现了公司产品创新能力的显著提升。然而,运营成本同时上涨了8%,对盈利能力构成一定压力,强调了优化运营效率的迫切需求。整体来看,公司在增长与成本控制之间需寻求更好的平衡以保障持续健康发展。
-
-=== 步骤 3: ReportGenerator ===
-分析报告
-
-一、执行摘要
-2024年第三季度,公司实现营收同比增长15%,主要得益于新产品线的成功推出,展现了强劲的产品创新能力。然而,运营成本也同比提升了8%,对利润空间形成一定压力。为确保持续的盈利增长,需重点关注运营效率的优化,推动成本控制与收入增长的平衡发展。
-
-二、详细分析
-1. 营收增长分析
-- 公司营收增长15%,反映出新产品线市场接受度良好,有效拓展了收入来源。
-- 新产品线的推出体现了公司研发及市场响应能力的提升,为未来持续增长奠定基础。
-
-2. 运营成本情况
-- 运营成本上升8%,可能来自原材料价格上涨、生产效率下降或销售推广费用增加等多个方面。
-- 该成本提升在一定程度上抵消了收入增长带来的利润增益,影响整体盈利能力。
-
-3. 盈利能力及效率考量
-- 营收与成本增长的不匹配显示出当前运营效率存在改进空间。
-- 优化供应链管理、提升生产自动化及加强成本控制将成为关键措施。
-
-三、建议
-1. 加强新产品线后续支持,包括市场推广和客户反馈机制,持续推动营收增长。
-2. 深入分析运营成本构成,识别主要成本驱动因素,制定针对性降低成本的策略。
-3. 推动内部流程优化与技术升级,提升生产及运营效率,缓解成本压力。
-4. 建立动态的财务监控体系,实现对营收与成本的实时跟踪与调整,确保公司财务健康。
-
-四、结论
-公司在2024年第三季度展现出了良好的增长动力,但同时面临成本上升带来的挑战。通过持续的产品创新结合有效的成本管理,未来有望实现盈利能力和市场竞争力的双重提升,推动公司稳健发展。
-
-文档处理流水线执行完成!
-```
-
-# LoopAgent
-
-## 功能
-
-LoopAgent 基于 SequentialAgent 实现,它会重复执行配置的子 Agent 序列,直到达到最大迭代次数或某个子 Agent 产生 ExitAction。LoopAgent 特别适用于需要迭代优化、反复处理或持续监控的场景。
-
-
-
-```go
-type LoopAgentConfig struct {
- Name string // Agent 名称
- Description string // Agent 描述
- SubAgents []Agent // 子 Agent 列表
- MaxIterations int // 最大迭代次数,0 表示无限循环
-}
-
-func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
-```
-
-LoopAgent 的执行遵循以下设定:
-
-1. **循环执行**:重复执行 SubAgents 序列,每次循环都是一个完整的 Sequential 执行过程
-2. **History 累积**:每次迭代的结果都会累积到 History 中,后续迭代可以访问所有历史信息
-3. **条件退出**:支持通过 ExitAction 或达到最大迭代次数来终止循环,配置 `MaxIterations=0` 时表示无限循环
-
-LoopAgent 适用于以下场景:
-
-- **迭代优化**:如代码优化、参数调优等需要反复改进的任务
-- **持续监控**:定期检查状态并执行相应操作
-- **反复处理**:需要多轮处理才能达到满意结果的任务
-- **自我改进**:Agent 根据前面的执行结果不断改进自己的输出
-
-## 示例
-
-示例展示了如何使用 LoopAgent 创建一个代码优化循环:
-
-1. **CodeAnalyzer**:分析代码问题
-2. **CodeOptimizer**:根据分析结果优化代码
-3. **ExitController**:判断是否需要退出循环
-
-循环会持续执行直到代码质量达到标准或达到最大迭代次数。
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
-
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// 代码分析 Agent
-func NewCodeAnalyzerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "CodeAnalyzer",
- Description: "分析代码质量和性能问题",
- Instruction: `你是一个代码分析专家。请分析提供的代码,识别以下问题:
-1. 性能瓶颈
-2. 代码重复
-3. 可读性问题
-4. 潜在的 bug
-5. 不符合最佳实践的地方
-
-如果代码已经足够优秀,请输出 "EXIT: 代码质量已达到标准" 来结束优化流程。`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 代码优化 Agent
-func NewCodeOptimizerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "CodeOptimizer",
- Description: "根据分析结果优化代码",
- Instruction: `基于前面的代码分析结果,对代码进行优化改进:
-1. 修复识别出的性能问题
-2. 消除代码重复
-3. 提高代码可读性
-4. 修复潜在 bug
-5. 应用最佳实践
-
-请提供优化后的完整代码。`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 创建一个特殊的 Agent 来处理退出逻辑
-func NewExitControllerAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "ExitController",
- Description: "控制优化循环的退出",
- Instruction: `检查前面的分析结果,如果代码分析师认为代码质量已达到标准(包含"EXIT"关键词),
-则输出 "TERMINATE" 并生成退出动作来结束循环。否则继续下一轮优化。`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // 创建优化流程的 Agent
- analyzer := NewCodeAnalyzerAgent()
- optimizer := NewCodeOptimizerAgent()
- controller := NewExitControllerAgent()
-
- // 创建 LoopAgent,最多执行 5 轮优化
- loopAgent, err := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
- Name: "CodeOptimizationLoop",
- Description: "代码优化循环:分析 → 优化 → 检查退出条件",
- SubAgents: []adk.Agent{analyzer, optimizer, controller},
- MaxIterations: 5, // 最多 5 轮优化
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // 创建 Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: loopAgent,
- })
-
- // 待优化的代码示例
- codeToOptimize := `
-func processData(data []int) []int {
- result := []int{}
- for i := 0; i < len(data); i++ {
- for j := 0; j < len(data); j++ {
- if data[i] > data[j] {
- result = append(result, data[i])
- break
- }
- }
- }
- return result
-}
-`
-
- fmt.Println("开始代码优化循环...")
- iter := runner.Query(ctx, "请优化以下 Go 代码:\n"+codeToOptimize)
-
- iteration := 1
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatal(event.Err)
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- fmt.Printf("\n=== 第 %d 轮 - %s ===\n", iteration, event.AgentName)
- fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
-
- // 检查是否需要退出
- if event.AgentName == "ExitController" {
- if event.Action != nil && event.Action.Exit {
- fmt.Println("\n优化循环提前结束!")
- break
- }
- iteration++
- }
- }
- }
-
- fmt.Println("\n代码优化循环执行完成!")
-}
-```
-
-运行结果为:
-
-```java
-开始代码优化循环...
-
-=== 第 1 轮 - CodeAnalyzer ===
-分析提供的代码:
-
-```go
-func processData(data []int) []int {
- result := []int{}
- for i := 0; i < len(data); i++ {
- for j := 0; j < len(data); j++ {
- if data[i] > data[j] {
- result = append(result, data[i])
- break
- }
- }
- }
- return result
-}
-```
-
-### 1. 性能瓶颈
-
-- 双层循环,时间复杂度为 O(n²),对于较大的数据量,性能不佳。
-- 内层循环当条件满足时立即 break,减少了部分不必要的比较,但整体仍然是二次复杂度。
-
-### 2. 代码重复
-
-- 代码没有明显重复,但逻辑可以简化。
-
-### 3. 可读性问题
-
-- 代码的意图不太明确。根据代码逻辑,函数筛选出那些在数组里至少有一个元素比它小的元素。换句话说,剔除了数组里所有等于或最小的数字。
-- 可以通过注释或重命名来增加可读性。
-
-### 4. 潜在的 bug
-
-- 目前没有明显的 bug,但输入为空数组时,返回空数组符合预期。
-
-### 5. 不符合最佳实践的地方
-
-- 过程较为低效,未使用 Go 语言的特性优化性能。
-- 缺少注释或文档说明。
-
----
-
-## 优化建议
-
-如果目的确实是要返回所有比数组中某些元素大的元素(排除数组中的最小元素),可以改为:
-
-- 找出数组中的最小元素 minVal。
-- 遍历数组,将大于 minVal 的元素加入结果。
-- 这样时间复杂度降为 O(n)。
-
-优化代码:
-
-```go
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-### 优化点说明
-
-- 使用一次遍历确定最小值,O(n)。
-- 再次遍历构造结果,O(n)。
-- 适当使用切片容量初始化,减少内存分配。
-- 代码意图明确且性能提升明显。
-
----
-
-如果你期望的是其他逻辑,请告知我进一步调整。
-
-=== 第 1 轮 - CodeOptimizer ===
-
-```go
-// processData 返回输入切片中所有大于最小元素的值。
-// 如果输入为空,则返回 nil。
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- // 找出切片中的最小值
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- // 收集所有大于最小值的元素
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== 第 1 轮 - ExitController ===
-TERMINATE
-
-=== 第 2 轮 - CodeAnalyzer ===
-EXIT: 代码质量已达到标准
-
-=== 第 2 轮 - CodeOptimizer ===
-
-```go
-// processData 返回输入切片中所有大于最小元素的值。
-// 如果输入为空,则返回 nil。
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- // 找出切片中的最小值
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- // 收集所有大于最小值的元素
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== 第 2 轮 - ExitController ===
-TERMINATE
-
-=== 第 3 轮 - CodeAnalyzer ===
-EXIT: 代码质量已达到标准
-
-=== 第 3 轮 - CodeOptimizer ===
-
-```go
-// processData 返回输入切片中所有大于最小元素的值。
-// 如果输入为空,则返回 nil。
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== 第 3 轮 - ExitController ===
-TERMINATE
-
-=== 第 4 轮 - CodeAnalyzer ===
-EXIT: 代码质量已达到标准
-
-=== 第 4 轮 - CodeOptimizer ===
-
-```go
-// processData 返回切片中所有大于最小元素的元素。
-// 如果输入为空,则返回 nil。
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== 第 4 轮 - ExitController ===
-TERMINATE
-
-=== 第 5 轮 - CodeAnalyzer ===
-EXIT: 代码质量已达到标准
-
-=== 第 5 轮 - CodeOptimizer ===
-
-```go
-// processData 返回输入切片中所有大于最小元素的值。
-// 如果输入为空,则返回 nil。
-func processData(data []int) []int {
- if len(data) == 0 {
- return nil
- }
-
- minVal := data[0]
- for _, v := range data[1:] {
- if v < minVal {
- minVal = v
- }
- }
-
- result := make([]int, 0, len(data))
- for _, v := range data {
- if v > minVal {
- result = append(result, v)
- }
- }
-
- return result
-}
-```
-
-=== 第 5 轮 - ExitController ===
-TERMINATE
-
-代码优化循环执行完成!
-
-```
-
-
-
-
-## BreakLoop
-
-
-在 Loop Agent 中,当某个 Agent 需要中断循环运行时,您可以使用 ADK 提供的对应 Break Action。
-
-```go
-// BreakLoopAction is a programmatic-only agent action used to prematurely
-// terminate the execution of a loop workflow agent.
-// When a loop workflow agent receives this action from a sub-agent, it will stop its
-// current iteration and will not proceed to the next one.
-// It will mark the BreakLoopAction as Done, signalling to any 'upper level' loop agent
-// that this action has been processed and should be ignored further up.
-// This action is not intended to be used by LLMs.
-type BreakLoopAction struct {
- // From records the name of the agent that initiated the break loop action.
- From string
- // Done is a state flag that can be used by the framework to mark when the
- // action has been handled.
- Done bool
- // CurrentIterations is populated by the framework to record at which
- // iteration the loop was broken.
- CurrentIterations int
-}
-
-// NewBreakLoopAction creates a new BreakLoopAction, signaling a request
-// to terminate the current loop.
-func NewBreakLoopAction(agentName string) *AgentAction {
- return &AgentAction{BreakLoop: &BreakLoopAction{
- From: agentName,
- }}
-}
-```
-
-Break Action 在达到中断目的的同时不影响 Loop Agent 外的其他 Agent 运行,而 Exit Action 会立刻中断所有后续的 Agent 运行。
-
-以下图为例:
-
-
-
-- 当 Agent1 发出 BreakAction 时,Loop Agent 将中断,Sequential 继续运行 Agent3
-- 当 Agent1 发出 ExitAction 时,Sequential 运行流程整体终止,Agent2 / Agent3 均不会运行
-
-# ParallelAgent
-
-## 功能
-
-ParallelAgent 允许多个子 Agent 基于相同的输入上下文并发执行,所有子 Agent 同时开始执行,并等待全部完成后结束。这种模式特别适用于可以独立并行处理的任务,能够显著提高执行效率。
-
-
-
-```go
-type ParallelAgentConfig struct {
- Name string // Agent 名称
- Description string // Agent 描述
- SubAgents []Agent // 并发执行的子 Agent 列表
-}
-
-func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
-```
-
-ParallelAgent 的执行遵循以下设定:
-
-1. **并发执行**:所有子 Agent 同时启动,在独立的 goroutine 中并行执行
-2. **共享输入**:所有子 Agent 接收相同的初始输入和上下文
-3. **等待与结果聚合**:内部使用 sync.WaitGroup 等待所有子 Agent 执行完成,收集所有子 Agent 的执行结果并按接收顺序输出
-
-另外 Parallel 内部默认包含异常处理机制:
-
-- **Panic 恢复**:每个 goroutine 都有独立的 panic 恢复机制
-- **错误隔离**:单个子 Agent 的错误不会影响其他子 Agent 的执行
-- **中断处理**:支持子 Agent 的中断和恢复机制
-
-ParallelAgent 适用于以下场景:
-
-- **独立任务并行处理**:多个不相关的任务可以同时执行
-- **多角度分析**:从不同角度同时分析同一个问题
-- **性能优化**:通过并行执行减少总体执行时间
-- **多专家咨询**:同时咨询多个专业领域的 Agent
-
-## 示例
-
-示例展示了如何使用 ParallelAgent 同时从四个不同角度分析产品方案:
-
-1. **TechnicalAnalyst**:技术可行性分析
-2. **BusinessAnalyst**:商业价值分析
-3. **UXAnalyst**:用户体验分析
-4. **SecurityAnalyst**:安全风险分析
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
- "os"
- "sync"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/model"
-)
-
-func newChatModel() model.ToolCallingChatModel {
- cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- })
- if err != nil {
- log.Fatal(err)
- }
- return cm
-}
-
-// 技术分析 Agent
-func NewTechnicalAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "TechnicalAnalyst",
- Description: "从技术角度分析内容",
- Instruction: `你是一个技术专家。请从技术实现、架构设计、性能优化等技术角度分析提供的内容。
-重点关注:
-1. 技术可行性
-2. 架构合理性
-3. 性能考量
-4. 技术风险
-5. 实现复杂度`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 商业分析 Agent
-func NewBusinessAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "BusinessAnalyst",
- Description: "从商业角度分析内容",
- Instruction: `你是一个商业分析专家。请从商业价值、市场前景、成本效益等商业角度分析提供的内容。
-重点关注:
-1. 商业价值
-2. 市场需求
-3. 竞争优势
-4. 成本分析
-5. 盈利模式`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 用户体验分析 Agent
-func NewUXAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "UXAnalyst",
- Description: "从用户体验角度分析内容",
- Instruction: `你是一个用户体验专家。请从用户体验、易用性、用户满意度等角度分析提供的内容。
-重点关注:
-1. 用户友好性
-2. 操作便利性
-3. 学习成本
-4. 用户满意度
-5. 可访问性`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-// 安全分析 Agent
-func NewSecurityAnalystAgent() adk.Agent {
- a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
- Name: "SecurityAnalyst",
- Description: "从安全角度分析内容",
- Instruction: `你是一个安全专家。请从信息安全、数据保护、隐私合规等安全角度分析提供的内容。
-重点关注:
-1. 数据安全
-2. 隐私保护
-3. 访问控制
-4. 安全漏洞
-5. 合规要求`,
- Model: newChatModel(),
- })
- if err != nil {
- log.Fatal(err)
- }
- return a
-}
-
-func main() {
- ctx := context.Background()
-
- // 创建四个不同角度的分析 Agent
- techAnalyst := NewTechnicalAnalystAgent()
- bizAnalyst := NewBusinessAnalystAgent()
- uxAnalyst := NewUXAnalystAgent()
- secAnalyst := NewSecurityAnalystAgent()
-
- // 创建 ParallelAgent,同时进行多角度分析
- parallelAgent, err := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
- Name: "MultiPerspectiveAnalyzer",
- Description: "多角度并行分析:技术 + 商业 + 用户体验 + 安全",
- SubAgents: []adk.Agent{techAnalyst, bizAnalyst, uxAnalyst, secAnalyst},
- })
- if err != nil {
- log.Fatal(err)
- }
-
- // 创建 Runner
- runner := adk.NewRunner(ctx, adk.RunnerConfig{
- Agent: parallelAgent,
- })
-
- // 要分析的产品方案
- productProposal := `
-产品方案:智能客服系统
-
-概述:开发一个基于大语言模型的智能客服系统,能够自动回答用户问题,处理常见业务咨询,并在必要时转接人工客服。
-
-主要功能:
-1. 自然语言理解和回复
-2. 多轮对话管理
-3. 知识库集成
-4. 情感分析
-5. 人工客服转接
-6. 对话历史记录
-7. 多渠道接入(网页、微信、APP)
-
-技术架构:
-- 前端:React + TypeScript
-- 后端:Go + Gin 框架
-- 数据库:PostgreSQL + Redis
-- AI模型:GPT-4 API
-- 部署:Docker + Kubernetes
-`
-
- fmt.Println("开始多角度并行分析...")
- iter := runner.Query(ctx, "请分析以下产品方案:\n"+productProposal)
-
- // 使用 map 来收集不同分析师的结果
- results := make(map[string]string)
- var mu sync.Mutex
-
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Printf("分析过程中出现错误: %v", event.Err)
- continue
- }
-
- if event.Output != nil && event.Output.MessageOutput != nil {
- mu.Lock()
- results[event.AgentName] = event.Output.MessageOutput.Message.Content
- mu.Unlock()
-
- fmt.Printf("\n=== %s 分析完成 ===\n", event.AgentName)
- }
- }
-
- // 输出所有分析结果
- fmt.Println("\n" + "============================================================")
- fmt.Println("多角度分析结果汇总")
- fmt.Println("============================================================")
-
- analysisOrder := []string{"TechnicalAnalyst", "BusinessAnalyst", "UXAnalyst", "SecurityAnalyst"}
- analysisNames := map[string]string{
- "TechnicalAnalyst": "技术分析",
- "BusinessAnalyst": "商业分析",
- "UXAnalyst": "用户体验分析",
- "SecurityAnalyst": "安全分析",
- }
-
- for _, agentName := range analysisOrder {
- if result, exists := results[agentName]; exists {
- fmt.Printf("\n【%s】\n", analysisNames[agentName])
- fmt.Printf("%s\n", result)
- fmt.Println("----------------------------------------")
- }
- }
-
- fmt.Println("\n多角度并行分析完成!")
- fmt.Printf("共收到 %d 个分析结果\n", len(results))
-}
-```
-
-运行结果为:
-
-```markdown
-开始多角度并行分析...
-
-=== BusinessAnalyst 分析完成 ===
-
-=== UXAnalyst 分析完成 ===
-
-=== SecurityAnalyst 分析完成 ===
-
-=== TechnicalAnalyst 分析完成 ===
-
-============================================================
-多角度分析结果汇总
-============================================================
-
-【技术分析】
-针对该智能客服系统方案,下面从技术实现、架构设计及性能优化等角度进行详细分析:
-
----
-
-### 一、技术可行性
-
-1. **自然语言理解和回复**
- - 利用 GPT-4 API 实现自然语言理解和自动回复是当前成熟且可行的方案。GPT-4具备强大的语言理解和生成能力,适合处理复杂、多样的问题。
-
-2. **多轮对话管理**
- - 依赖后端维护上下文状态,结合GPT-4模型能够较好处理多轮交互。需要设计合理的上下文管理机制(例如对话历史维护、关键槽位抽取等),确保上下文信息完整性。
-
-3. **知识库集成**
- - 可通过向GPT-4 API添加特定的知识库检索结果(检索增强生成),或者通过本地检索接口集成知识库。技术上可行,但对于实时性和准确性有较高要求。
-
-4. **情感分析**
- - 情感分析功能可以用独立的轻量模型实现(例如基于BERT微调),也可尝试利用GPT-4输出,但成本较高。情感分析能力帮助智能客服更好地理解用户情绪,提升用户体验。
-
-5. **人工客服转接**
- - 技术上通过建立事件触发规则(如轮次数、情绪阈值、关键词检测)实现自动转人工。系统需支持工单或会话传递机制,并保障会话无缝切换。
-
-6. **多渠道接入**
- - 网页、微信、App等多渠道接入均可通过统一API网关实现,技术成熟,同时需要处理渠道差异性(消息格式、认证、推送机制等)。
-
----
-
-### 二、架构合理性
-
-- **前端 React + TypeScript**
- 非常适合搭建响应式客服界面,生态成熟,方便多渠道共享组件。
-
-- **后端 Go + Gin**
- Go语言性能优异,Gin框架轻量且性能高,适合高并发场景。后端承担对接 GPT-4 API、管理状态、多渠道消息转发等职责,选择合理。
-
-- **数据库 PostgreSQL + Redis**
- - PostgreSQL 负责存储结构化数据,如用户信息、对话历史、知识库元数据。
- - Redis 负责缓存会话状态、热点知识库、限流等,提升访问性能。
- 架构设计符合常见大型互联网产品模式,组件分工明确。
-
-- **AI模型 GPT-4 API**
- 使用成熟API降低开发难度和模型维护成本;缺点是对网络和API调用依赖度高。
-
-- **部署 Docker + Kubernetes**
- 容器化和K8s编排能保证系统弹性伸缩、高可用和灰度发布,适合生产环境,符合现代微服务架构趋势。
-
----
-
-### 三、性能考量
-
-1. **响应时间**
- - GPT-4 API调用本身有一定延迟(通常几百毫秒到1秒不等),对响应时间影响较大。需要做好接口异步处理与前端体验设计(如加载动画、部分渐进响应)。
-
-2. **并发处理能力**
- - 后端Go具有高并发处理优势,配合Redis缓存热点数据,能大幅提升整体吞吐能力。
- - 但GPT-4 API调用受限于OpenAI服务的QPS限制与调用成本,需合理设计调用频率与降级策略。
-
-3. **缓存策略**
- - 对用户对话上下文和常见问题答案进行缓存,减少重复API调用。
- - 如关键问题先做本地匹配,失败后才调用GPT-4,提升效率。
-
-4. **多渠道负载均衡**
- - 需要设计统一消息总线和可靠的异步队列,防止某渠道流量突增影响整体系统稳定。
-
----
-
-### 四、技术风险
-
-1. **GPT-4 API依赖**
- - 高度依赖第三方API,风险包括服务中断、接口变更及成本波动。
- - 建议设计本地缓存和有限的替代回答逻辑以应对API异常。
-
-2. **多轮对话上下文管理难度**
- - 上下文过长或复杂会导致回答质量降低,需要设计限制上下文长度、选择性保留重要信息机制。
-
-3. **知识库集成复杂度**
- - 如何做到知识库与
-----------------------------------------
-
-【商业分析】
-以下是对智能客服系统产品方案的商业角度分析:
-
-1. 商业价值
-- 提升客户服务效率:自动解答用户问题和常见咨询,减少人工客服压力,降低用人成本。
-- 提升用户体验:多轮对话和情感分析使交互更自然,增强客户满意度和粘性。
-- 数据驱动决策支持:对话历史与知识库集成为企业提供宝贵的用户反馈和行为数据,优化产品和服务。
-- 支持业务扩展:多渠道接入(网页、微信、APP)满足不同客户接入习惯,提升覆盖率。
-
-2. 市场需求
-- 市场对智能客服的需求持续增长,特别是在电商、金融、医疗、教育等行业,客户服务自动化是企业数字化转型的重要方向。
-- 随着AI技术的成熟,企业期望借助大语言模型提升客服智能化水平。
-- 用户对即时响应、全天候服务的需求增加,推动智能客服系统的广泛采用。
-
-3. 竞争优势
-- 采用先进的GPT-4大语言模型,拥有较强的自然语言理解与生成能力,提升问答准确率和对话自然度。
-- 情感分析功能有助于精准识别用户情绪,动态调整回复策略,提高客户满意度。
-- 多渠道接入设计满足企业多元化客户触达需求,增强产品适用性。
-- 技术架构采用微服务、容器化部署,便于弹性扩展和维护,提升系统稳定性和扩展能力。
-
-4. 成本分析
-- AI模型调用成本较高,依赖GPT-4 API,需根据调用量和响应速度调整预算。
-- 技术研发投入较大,涉及前后端、多渠道融合、AI和知识库管理。
-- 运维和服务器成本需考虑多渠道并发访问。
-- 长期来看,人工客服人数可显著减少,节省人力成本。
-- 可通过云服务降低硬件初期投入,但云资源使用需精细管理以控制费用。
-
-5. 盈利模式
-- SaaS订阅服务:按月/年向企业客户收取服务费,基于接入渠道数、并发量和功能级别分层定价。
-- 按调用次数或对话数收费,适合业务波动较大的客户。
-- 增值服务:数据分析报告定制、行业知识库集成、人工客服协同工具等收费。
-- 中大型客户可提供定制开发和技术支持,收取项目费用。
-- 通过持续优化模型和服务,增加客户留存和续费率。
-
-综上,该智能客服系统基于成熟技术与AI优势,具备良好的商业价值和市场潜力。其多渠道接入和情感分析等功能增强竞争力,但需合理控制AI调用成本和运营费用。建议重点推进SaaS订阅和增值服务,结合市场推广,快速占领客户资源,提升盈利能力。
-----------------------------------------
-
-【用户体验分析】
-针对该智能客服系统方案,我将从用户体验、易用性、用户满意度及可访问性等角度进行分析:
-
-1. 用户友好性
-- 自然语言理解和回复能力提升了用户与系统的沟通体验,使用户能够用自然话语表达需求,降低交流障碍。
-- 多轮对话管理允许系统理解上下文,减少重复解释,增强对话连贯性,进一步提升用户体验。
-- 情感分析功能有助于系统识别用户情绪,做出更贴心的回应,提高互动的个性化和人性化。
-- 多渠道接入覆盖用户常用的访问途径,方便用户随时随地获取服务,提升友好度。
-
-2. 操作便利性
-- 自动回答常见业务咨询能够减轻用户等待时间和操作负担,提高响应速度。
-- 人工客服转接机制确保复杂问题可被及时处理,保障服务连续性和操作的无缝衔接。
-- 对话历史记录方便用户回顾咨询内容,避免重复查询,提升操作便利。
-- 使用现代技术栈(React、TypeScript)为前端交互提供良好性能和响应速度,间接增强操作流畅性。
-
-3. 学习成本
-- 基于自然语言处理,用户无需学习特殊指令,降低使用门槛。
-- 多轮对话自然衔接,让用户更易理解系统响应逻辑,减少迷惑和挫败感。
-- 不同渠道的一致性界面(如在网页和微信中保持类似体验)有助于用户迅速上手。
-- 通过情感分析提供的更精准反馈,减少用户因误解而频繁尝试的时间成本。
-
-4. 用户满意度
-- 快速准确的自动回复和多轮对话减少用户等待和重复输入,提升满意度。
-- 情感分析让系统更懂用户情绪,带来更温暖的交互体验,增加用户粘性。
-- 人工客服介入保障复杂问题得到妥善处理,提高服务质量感知。
-- 多渠道覆盖满足不同用户的使用场景,增强整体满意度。
-
-5. 可访问性
-- 多渠道接入覆盖网页、微信、APP,适应不同用户的设备和环境,提升可访问性。
-- 方案未明确提及无障碍设计(如屏幕阅读器兼容、高对比度模式等),这可能是未来需要补充的部分。
-- 前端采用React和TypeScript,有利于实现响应式设计和无障碍功能,但需确保开发规范落地。
-- 后端架构和部署方案保证系统的稳定性和扩展性,间接提升用户持续可访问性。
-
-总结:
-该智能客服系统方案在用户体验和易用性方面考虑较为充分,利用大语言模型实现自然多轮对话、情感分析和知识库集成,满足用户多样化需求。同时,多渠道接入增强了系统的覆盖能力。建议在具体落地时,强化无障碍设计,实现更全面的可访问性保障,同时继续优化对话策略以提升用户满意度。
-----------------------------------------
-
-【安全分析】
-针对该智能客服系统方案,结合信息安全、数据保护及隐私合规等方面,展开如下分析:
-
-一、数据安全
-
-1. 数据传输安全
-- 建议系统所有客户端与服务器间通信均采用TLS/SSL加密,保障数据在传输过程中的机密性与完整性。
-- 由于支持多渠道接入(网页、微信、APP),需确保每个入口均严格实施加密传输。
-
-2. 数据存储安全
-- PostgreSQL存储对话历史、用户资料等敏感信息,需启用数据库加密(如透明数据加密TDE或字段级加密),防止数据泄露。
-- Redis作为缓存,可能存储临时会话数据,也需开启访问认证与加密传输。
-- 对用户敏感数据实行最小存储原则,避免无关数据超范围保存。
-- 数据备份过程中需加密保存,且备份访问同样受控。
-
-3. API调用安全
-- GPT-4 API调用产生大量用户数据交互,应评估其数据处理及存储政策,确保符合数据安全要求。
-- 增加调用权限管理,限制API密钥访问范围和权限,避免被滥用。
-
-4. 日志安全
-- 系统日志中避免存储明文敏感信息,尤其是个人身份信息、对话内容。日志访问需严格控制。
-
-二、隐私保护
-
-1. 个人数据处理
-- 采集和存储用户个人数据(姓名、联系方式、账务信息等)必须明确告知用户,并征得用户同意。
-- 实施数据匿名化/去标识化技术,尤其是对话历史中的身份信息处理。
-
-2. 用户隐私权利
-- 满足相关法律法规(例如《个人信息保护法》、《GDPR》)中用户的访问、更正、删除数据的权利。
-- 提供隐私政策明确披露数据收集、使用和共享情况。
-
-3. 交互隐私
-- 多轮对话和情感分析等功能应考虑避免过度侵犯用户隐私,例如敏感情绪数据的使用透明告知和限制。
-
-4. 第三方合规
-- GPT-4 API由第三方提供,需确保其服务符合相关隐私合规要求及数据保护标准。
-
-三、访问控制
-
-1. 用户身份验证
-- 系统中涉及用户身份信息查询和管理时,需建立可靠的身份认证机制。
-- 支持多因素认证增强安全性。
-
-2. 权限管理
-- 后端管理接口及人工客服转接模块需采用基于角色的访问控制(RBAC),确保操作权限最小化。
-- 对访问敏感数据的操作需有详细审计和监控。
-
-3. 会话管理
-- 对多渠道的会话要有有效的会话管理机制,防止会话劫持。
-- 对话历史访问权限应限制仅允许相关用户或授权人员访问。
-
-四、安全漏洞
-
-1. 应用安全
-- 前端React+TypeScript应防止XSS、CSRF攻击,合理使用Content Security Policy(CSP)。
-- 后端Go应用需防止SQL注入、请求伪造和权限缺失。Gin框架提供中间件支持,建议充分利用安全模块。
-
-2. AI模型风险
-- GPT-4 API本身输入输出可能存在敏感信息泄露或模型误用风险,需限制输入内容、过滤敏感信息。
-- 防止生成恶意回答或信息泄露,建立内容审核机制。
-
-3. 容器和部署安全
-- Docker容器须采用安全镜像,及时打补丁。Kubernetes集群网络策略和访问控制需完善。
-- 容器运行权限最小化,避免容器逃逸风险。
-
-五、合规要求
-
-1. 数据保护法规
-- 根据运营地域,需符合《个人信息保护法》(PIPL)、《欧盟通用数据保护条例》(GDPR)或其他相关法律要求。
-- 明确用户数据的采集、处理、传输和存储流程符合法规。
-
-2. 用户隐私告知及同意
-- 应提供清晰的隐私政策和使用条款,说明数据用途及处理方式。
-- 实现用户同意管理(Consent Management)机制。
-
-3. 数据跨境传输合规
-- 若系统涉及跨境数据流,需评估合规风险和采取相应技术
-----------------------------------------
-
-多角度并行分析完成!
-共收到 4 个分析结果
-```
-
-# 总结
-
-Workflow Agents 为 Eino ADK 提供了强大的多 Agent 协作能力,通过合理选择和组合这些 Workflow Agent,开发者可以构建出高效、可靠的多 Agent 协作系统,满足各种复杂的业务需求。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md b/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
index a3629db244d..67f85bcf201 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
@@ -1,390 +1,198 @@
---
Description: ""
-date: "2026-03-02"
+date: "2026-05-17"
lastmod: ""
tags: []
title: Agent 抽象
weight: 3
---
-# Agent 定义
+# Agent 接口
-Eino 定义了 Agent 的基础接口,实现此接口的 Struct 可被视为一个 Agent:
+ADK 的所有功能围绕 `Agent` 接口展开:
```go
-// github.com/cloudwego/eino/adk/interface.go
+// github.com/cloudwego/eino/adk
-type Agent interface {
+type TypedAgent[M MessageType] interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
- Run(ctx context.Context, input *AgentInput, opts ...AgentRunOption) *AsyncIterator[*AgentEvent]
+ Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]
}
+
+// 默认类型别名(使用 *schema.Message)
+type Agent = TypedAgent[*schema.Message]
```
| Method | 说明 |
| Name | Agent 的名称,作为 Agent 的标识 |
| Description | Agent 的职能描述信息,主要用于让其他的 Agent 了解和判断该 Agent 的职责或功能 |
| Run | Agent 的核心执行方法,返回一个迭代器,调用者可以通过这个迭代器持续接收 Agent 产生的事件 |
| 方法 | 说明 |
Name | Agent 名称标识 |
Description | 职能描述,供其他 Agent 或框架了解能力 |
Run | 核心执行方法,异步返回事件流(Future 模式) |
-
-## AgentRunOption
-
-`AgentRunOption` 由 Agent 实现定义,可以在请求维度修改 Agent 配置或者控制 Agent 行为。
-
-Eino ADK 提供了一些通用定义的 Option,供用户使用:
-
-- `WithSessionValues`:设置跨 Agent 读写数据
-- `WithSkipTransferMessages`:配置后,当 Event 为 Transfer SubAgent 时,Event 中的消息不会追加到 History 中
-
-Eino ADK 提供了 `WrapImplSpecificOptFn` 和 `GetImplSpecificOptions` 两个方法,供 Agent 包装与读取自定义的 `AgentRunOption`。
-
-当使用 `GetImplSpecificOptions` 方法读取 `AgentRunOptions` 时,与所需类型(如例子中的 options)不符的 AgentRunOption 会被忽略。
+| 泛型类型 | 默认别名 |
TypedAgent[*schema.Message] | Agent |
TypedAgentInput[*schema.Message] | AgentInput |
TypedAgentEvent[*schema.Message] | AgentEvent |
TypedAgentOutput[*schema.Message] | AgentOutput |
TypedMessageVariant[*schema.Message] | MessageVariant |
| 组件/中间件 | 影响的提示词 |
| FileSystem Middleware | 文件系统工具描述、系统提示词、执行工具提示词 |
| Reduction Middleware | 工具结果截断/清理的提示文字 |
| Skill Middleware | 技能系统提示词、技能工具描述 |
| ChatModelAgent | 内置系统提示词 |
+# ADK 架构
## Agent Interface
-Eino ADK 的核心是 Agent 抽象(Agent Interface),ADK 的所有功能设计均围绕 Agent 抽象展开。详解请见 [Eino ADK: Agent 抽象 [New]](/zh/docs/eino/core_modules/eino_adk/agent_interface)
+ADK 的所有功能围绕 `Agent` 接口展开:
```go
type Agent interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
-
- // Run runs the agent.
- // The returned AgentEvent within the AsyncIterator must be safe to modify.
- // If the returned AgentEvent within the AsyncIterator contains MessageStream,
- // the MessageStream MUST be exclusive and safe to be received directly.
- // NOTE: it's recommended to use SetAutomaticClose() on the MessageStream of AgentEvents emitted by AsyncIterator,
- // so that even the events are not processed, the MessageStream can still be closed.
Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
}
```
-`Agent.Run` 的定义为:
-
-1. 从入参 AgentInput、AgentRunOption 和可选的 Context Session 中获取任务详情及相关数据
-2. 执行任务,并将执行过程、执行结果写入到 AgentEvent Iterator
-
-`Agent.Run` 要求 Agent 的实现以 Future 模式异步执行,核心分成三步,具体可参考 ChatModelAgent 中 Run 方法的实现:
+`Run` 的语义:
-1. 创建一对 Iterator、Generator
-2. 启动 Agent 的异步任务,并传入 Generator,处理 AgentInput。Agent 在这个异步任务执行核心逻辑(例如 ChatModelAgent 调用 LLM),并在产生新的事件时写入到 Generator 中,供 Agent 调用方在 Iterator 中消费
-3. 启动 2 中的任务后立即返回 Iterator
+1. 从 `AgentInput` 和 Context 中获取任务信息
+2. 异步执行任务,产出的事件写入 `AsyncIterator`
+3. 启动异步任务后立即返回 Iterator(Future 模式)
-## 多 Agent 协作
+## ChatModelAgent
-围绕 Agent 抽象,Eino ADK 提供多种简单易用、场景丰富的组合原语,可支撑开发丰富多样的 Multi-Agent 协同策略,比如 Supervisor、Plan-Execute、Group-Chat 等 Multi-Agent 场景。从而实现不同的 Agent 分工合作模式,处理更复杂的任务。详解请见 [Eino ADK: Agent 组合](/zh/docs/eino/core_modules/eino_adk/agent_collaboration)
+ADK 的核心实现。以 ChatModel 为决策器,通过 ReAct Loop 自主推进问题求解。
-Eino ADK 定义的 Agent 协作过程中的协作原语如下:
+**ChatModelAgent = ChatModel + Tools + ReAct Loop + Middleware**
-- Agent 间协作方式
+详细介绍见:[Eino ADK: ChatModelAgent 介绍](/zh/docs/eino/overview/eino_adk_quickstart)
-| 协助方式 | 描述 |
| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
| 上下文策略 | 描述 | |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 | |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 | |
| 协作方式 | 机制 | 适用场景 |
| AgentAsTool(推荐) | 子 Agent 包装为 Tool,父 Agent 自主决定是否调用 | 委派子任务、能力组合 |
| Workflow | Sequential / Loop / Parallel 确定性编排 | 流程固定的多步任务 |
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
| 类别 | ChatModel Agent | Workflow Agents | Custom Logic | EinoBuiltInAgent(supervisor, plan-execute) |
| 功能 | 思考,生成,工具调用 | 控制 Agent 之间的执行流程 | 运行自定义逻辑 | 开箱即用的 Multi-agent 模式封装 |
| 核心 | LLM | 预确定的执行流程(顺序,并发,循环) | 自定义代码 | 基于 Eino 实践积累的经验,对前三者的高度封装 |
| 用途 | 生成,动态决策 | 结构化处理,编排 | 定制需求 | 特定场景内的开箱即用 |
| 组件 | 职责 | 文档 | ||
| ChatModelAgent | ReAct Loop:推理 → 行动 → 反馈,自主决策 | ChatModelAgent 介绍 | ||
| Middleware | 在 ReAct Loop 的生命周期点位注入行为(压缩、搜索、重试等) | ChatModelAgentMiddleware | ||
| Runner | 单次 Agent 运行入口:Query / Run → 事件流 | Agent Runner 与扩展 | ||
| TurnLoop | 多轮运行时:Push / Preempt / Stop + 声明式 checkpoint/resume | Agent Cancel 与 TurnLoop | ||
| DeepAgents | 预构建 Agent:任务规划(PlanTask)+ 子任务委派(TaskTool) | DeepAgents |
+## 其他 Agent 类型
-# ADK Examples
+除 ChatModelAgent 外,ADK 还提供确定性编排原语:
-[Eino-examples](https://github.com/cloudwego/eino-examples/tree/main/adk) 项目中提供了多种 ADK 的实施样例,您可以参考样例代码与简介,对 adk 能力构建初步的认知:
+- **Workflow Agents**:Sequential / Loop / Parallel Agent,用于预定义流程的结构化编排。
+- **Custom Agent**:实现 `Agent` 接口即可接入框架。
-| 项目路径 | 简介 | 结构图 |
| 顺序工作流案例 | 该示例代码展示了基于 eino adk 的 Workflow 模式构建的一个顺序执行的多智能体工作流。 | ![]() |
| 循环工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 LoopAgent,构建了一个反思迭代型智能体框架。 | ![]() |
| 并行工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 ParallelAgent,构建了一个并发信息搜集框架: | ![]() |
| supervisor | 该用例采用单层 Supervisor 管理两个功能较为综合的子 Agent:Research Agent 负责检索任务,Math Agent 负责多种数学运算(加、乘、除),但所有数学运算均由同一个 Math Agent 内部统一处理,而非拆分为多个子 Agent。此设计简化了代理层级,适合任务较为集中且不需要过度拆解的场景,便于快速部署和维护。 | ![]() |
| layered-supervisor | 该用例实现了多层级智能体监督体系,顶层 Supervisor 管理 Research Agent 和 Math Agent,Math Agent 又进一步细分为 Subtract、Multiply、Divide 三个子 Agent。顶层 Supervisor 负责将研究任务和数学任务分配给下级 Agent,Math Agent 作为中层监督者再将具体数学运算任务分派给其子 Agent。 | ![]() |
| plan-execute 案例 | 本示例基于 eino adk 实现 plan-execute-replan 模式的多 Agent 旅行规划系统,核心功能是处理用户复杂旅行请求(如 “3 天北京游,需从纽约出发的航班、酒店推荐、必去景点”),通过 “计划 - 执行 - 重新计划” 循环完成任务:1. 计划(Plan):Planner Agent基于大模型生成分步执行计划(如 “第一步查北京天气,第二步搜纽约到北京航班”);2. 执行(Execute): Executor Agent调用 ** 天气(get_weather)、航班(search_flights)、酒店(search_hotels)、景点(search_attractions)** 等 Mock 工具执行每一步,若用户输入信息缺失(如未说明预算),则调用 ask_for_clarification工具追问;3. 重新计划(Replan): Replanner Agent根据工具执行结果评估是否需要调整计划(如航班无票则重新选日期)。Execute 和 Replan 不断循环运行,直至完成计划中的所有步骤;4. 支持会话轨迹跟踪(CozeLoop 回调)和状态管理,最终输出完整旅行方案。从结构上看,plan-execute-replan 分为两层: | ![]() |
| 书籍推荐 agent(运行中断与恢复) | 该代码展示了基于 eino adk 框架构建的一个书籍推荐聊天智能体实现,体现了 Agent 运行中断与恢复功能。 | ![]() |
+# What's Next
diff --git a/content/zh/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md b/content/zh/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md
new file mode 100644
index 00000000000..04a64e15dac
--- /dev/null
+++ b/content/zh/docs/eino/core_modules/eino_adk/eino_adk_agent_cancel_and_turnloop_quickstart/_index.md
@@ -0,0 +1,540 @@
+---
+Description: ""
+date: "2026-05-17"
+lastmod: ""
+tags: []
+title: Agent Cancel 与 TurnLoop 快速入门
+weight: 10
+---
+
+Eino ADK 中 **Agent 取消** 和 **TurnLoop** 两项核心特性的快速入门指南。自 [v0.9.0-alpha.9](https://github.com/cloudwego/eino/releases/tag/v0.9.0-alpha.9) 版本引入。
+
+## 类型约定
+
+本文示例统一使用以下泛型实例化:
+
+- `T = string`(推送给 TurnLoop 的业务项类型)
+- `M = *schema.Message`(Agent 消息类型,即标准 `Message`)
+
+ADK 中相关类型别名:
+
+```go
+type Agent = TypedAgent[*schema.Message]
+type AgentInput = TypedAgentInput[*schema.Message]
+type AgentEvent = TypedAgentEvent[*schema.Message]
+```
+
+当需要使用 `*schema.AgenticMessage` 时,将 `M` 替换为对应类型即可,所有 API 签名完全对称。
+
+---
+
+## 第一部分:Agent 取消
+
+### 场景
+
+用户向 agent 发送请求后,因等待过长或需求变更,希望取消当前执行。
+
+### 核心 API
+
+```go
+// 创建取消选项和取消函数
+cancelOpt, cancelFunc := adk.WithCancel()
+
+// 启动 agent,传入取消选项
+iter := runner.Run(ctx, []*schema.Message{schema.UserMessage("你好")}, cancelOpt)
+
+// 发起取消(可在任意 goroutine 调用)
+handle, contributed := cancelFunc(adk.WithAgentCancelMode(adk.CancelImmediate))
+// contributed == true: 本次调用影响了执行结果
+// contributed == false: agent 已结束或取消已完成,本次调用无实际效果
+
+err := handle.Wait()
+```
+
+`CancelHandle.Wait()` 的三种返回值:
+
+```go
+switch {
+case err == nil:
+ // 取消成功
+case errors.Is(err, adk.ErrCancelTimeout):
+ // 安全点超时,已自动升级为立即取消
+case errors.Is(err, adk.ErrExecutionEnded):
+ // agent 在取消生效前已自然结束
+}
+```
+
+### 三种取消模式
+
+| 模式 | 行为 | 适用场景 |
CancelImmediate | 立即中断,不等待安全点 | 紧急停止、超时兜底 |
CancelAfterChatModel | 等当前 ChatModel 调用完成后取消 | 需要完整模型回答 |
CancelAfterToolCalls | 等当前 ToolCalls 全部完成后取消 | 确保 tool 副作用完整 |
+
+### 基本用法
+
+```go
+loop := adk.NewTurnLoop(adk.TurnLoopConfig[string, *schema.Message]{
+ // GenInput:接收缓冲区所有项目,决定本轮消费哪些
+ GenInput: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], items []string) (*adk.GenInputResult[string, *schema.Message], error) {
+ return &adk.GenInputResult[string, *schema.Message]{
+ Input: &adk.AgentInput{Messages: []*schema.Message{schema.UserMessage(strings.Join(items, "\n"))}},
+ Consumed: items,
+ }, nil
+ },
+
+ // PrepareAgent:根据本轮消费项构建 Agent
+ PrepareAgent: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], consumed []string) (adk.Agent, error) {
+ return myAgent, nil
+ },
+
+ // OnAgentEvents:处理 agent 事件流(可选)
+ OnAgentEvents: func(ctx context.Context, tc *adk.TurnContext[string, *schema.Message], events *adk.AsyncIterator[*adk.AgentEvent]) error {
+ for {
+ event, ok := events.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ return event.Err
+ }
+ log.Printf("收到事件: agent=%s", event.AgentName)
+ }
+ return nil
+ },
+})
+
+loop.Push("消息 1")
+loop.Push("消息 2")
+loop.Run(ctx) // 非阻塞,启动后台处理
+loop.Push("消息 3") // 运行中仍可推入
+loop.Stop()
+result := loop.Wait() // 阻塞至退出
+```
+
+### 核心回调
+
+| 回调 | 必填 | 职责 |
GenInput | ✅ | 接收缓冲区所有项目,返回 Consumed(本轮处理)和 Remaining(留给后续轮次)。不在两者中的项目会被丢弃。 |
PrepareAgent | ✅ | 根据 Consumed 项目构建 Agent(设置 prompt、tools、middleware 等) |
OnAgentEvents | ❌ | 处理 agent 事件流。未设置时默认 drain 事件并返回首个错误 |
GenResume | ❌ | 从 checkpoint 恢复时调用,决定如何合并 interrupted/unhandled/new items |
| Checkpoint 状态 | 行为 |
| 存在 mid-turn checkpoint(agent 执行中被中断) | 调用 GenResume,将 interrupted/unhandled/new items 交给应用层决策后恢复执行 |
| 存在 between-turns checkpoint(轮次间被停止) | 将已缓冲项目加入 buffer,通过 GenInput正常处理 |
| 不存在 checkpoint | 从头开始 |
| 组件名称 | 中文文档 | English Docs |
| AgenticARK | README.zh_CN.md | README.md |
| AgenticDeepSeek | README_zh.md | README.md |
| AgenticOpenAI | README.zh_CN.md | README.md |
| AgenticGemini | README.zh_CN.md | README.md |
| AgenticQwen | README_zh.md | README.md |
+
+关键特征:**累积上下文驱动的渐进式决策**。每一轮循环不是从零开始,而是在此前所有推理与行动的完整轨迹之上继续推进。模型的每一次决策都基于不断增长的问题求解上下文做出,这让 Agent 能处理需要多步推理、试错、修正的复杂任务。
+
+## 什么让你的 ChatModelAgent 不同
+
+ReAct Loop 的结构是固定的。那什么让**你的** ChatModelAgent 有别于其他人的,能针对你的具体问题?
+
+四个维度:
+
+1. **ChatModel** — 选择哪个模型做决策。
+2. **Instruction** — 系统指令:角色定义、行为约束、少样本示例。
+3. **Tools** — 工具集合:决定 Agent 可以做什么。
+4. **Middleware(ChatModelAgentMiddleware)** — 在 ReAct Loop 的特定生命周期点位上注入行为:拦截、修改、增强循环中的输入和输出。
+
+前三者定义了 Agent "是什么"——决策能力、角色约束、行动范围。
+
+Middleware 定义了 Agent "怎么跑"——它不改变 Loop 的结构(推理 → 行动 → 反馈始终不变),而是控制循环运行时的具体行为。例如:模型调用前压缩上下文、运行前动态注入工具、工具调用时做权限检查、模型失败时重试或切换备用模型。这些都是在 Loop 的特定点位上做的运行时增强。
+
+## Middleware:在 ReAct Loop 中注入行为
+
+构建 ChatModelAgent 时,你会遇到这些典型问题:
+
+- **Agent 需要读写文件、执行命令?** → 需要在运行前注入一组通用工具。
+- **Agent 需要复用一组预定义的指令和知识?** → 需要把可复用能力打包成 Skill,按需加载。
+- **上下文越来越长,超出模型窗口怎么办?** → 需要在每次模型调用前自动压缩历史。
+- **工具太多,全部塞进 prompt 会稀释注意力?** → 需要按需搜索和加载工具。
+- **模型偶尔调用失败或返回垃圾?** → 需要自动重试或切换备用模型。
+
+这些需求的共同点:它们不需要改变 ReAct Loop 的结构,只需要在循环的特定点位上拦截和增强。这就是 Middleware 做的事。
+
+对应的内置 Middleware:
+
+| 场景 | Middleware | 做了什么 |
| 需要文件系统能力 | FileSystem | 运行前注入 ls/read/write/edit/grep/execute 等工具 |
| 复用预定义能力 | Skill | 将指令、知识、工具打包为可按需加载的技能单元 |
| 上下文超窗口 | Reduction / Summarization | 模型调用前压缩消息和工具结果 |
| 工具过多 | ToolSearch | 按需搜索并加载 Tools,而非一次性暴露全部 |
| 模型调用不稳定 | ModelRetry / ModelFailover | 单次模型调用维度做重试 / 故障切换 |
+
+对应的钩子点位总结:
+
+| 钩子点位 | 时机 | 典型用途 |
BeforeAgent | Agent 运行前(仅一次) | 增强 Instruction,注入 Tools |
BeforeModelRewriteState | 每次模型调用前 | 修改 Messages / ToolInfos |
AfterModelRewriteState | 每次模型调用后 | 修改模型响应或修补状态 |
WrapModel | 单次模型调用维度 | 重试、故障切换、改写模型返回 |
WrapToolCall | 单次工具调用维度 | 权限、安全、输出改写 |
AfterAgent | Agent 成功结束后 | 后处理、状态清理 |
+
+## 附录:Middleware 速查
+
+### 实例一览
+
+| Middleware | 描述 |
| Reduction | 超长工具输出截断 / 写入文件系统,防止 token 超限 |
| Summarization | 历史消息摘要压缩 |
| Skill | 可复用指令/知识以 Tool 形式暴露,Agent 按需加载 |
| FileSystem | ls/read/write/edit/glob/grep/execute 文件操作工具集 |
| ToolSearch | tool_search元工具,按需搜索加载工具(减少常驻工具列表占用) |
| PatchToolCall | 修补消息历史中的悬空工具调用(缺失工具结果) |
| SafeTool | WrapToolCall 维度拦截工具执行错误,转为可读文本返回模型,使 Agent 可自行修正而非中断 |
| ModelRetry | 模型调用失败时按策略重试 [内置配置] |
| ModelFailover | 模型调用失败时切换备用模型 [内置配置] |
| AgentsMD | 将 Agents.md 知识文件注入模型上下文,提升上下文质量 |
| PlanTask | 持久化的任务管理工具集(create/get/update/list),支持依赖关系追踪 |
| WriteTodos | 轻量级 TODO 列表工具,Agent 可创建和追踪结构化待办事项 [DeepAgent 内置] |
| TaskTool | 子 Agent 委派工具,主 Agent 通过它把子任务交给子 Agent 独立执行 [DeepAgent 内置] |
| Permission | 工具调用权限控制 [WIP] |
| 类别 | 解决什么问题 | 包含 |
| 扩展通用 Tool | 给 Agent 更多能力 | FileSystem, Skill, ToolSearch, PlanTask, WriteTodos, TaskTool |
| 处理 ReAct 过程中的错误 | 提高可靠性 | ModelRetry, ModelFailover, SafeTool, PatchToolCall |
| 保证上下文窗口在上限内 | 防 token 超限 | Reduction, Summarization, ToolSearch |
| 安全与权限 | 约束 Agent 行为 | Permission |
| 提高上下文内容质量 | 让模型看到更好的上下文 | Skill, AgentsMD |
| 章节 | 主题 | 入口 |
| 第一章 | ChatModel 与 Message(Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch01_chatmodel_agent_console.md |
| 第一章 | ChatModel 与 AgenticMessage(Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch01_chatmodel_agent_console.md |
| 第二章 | Agent 与 Runner(Console 多轮) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch02_chatmodel_agent_runner_console.md |
| 第三章 | Memory 与 Session(持久化对话) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch03_memory_session_jsonl.md |
| 第四章 | Tool 与文件系统访问 | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch04_tool_backend_filesystem.md |
| 第七章 | Interrupt/Resume(中断与恢复) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch07_interrupt_resume.md |
| 第八章 | Graph Tool(复杂工作流) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch08_graph_tool.md |
| 第九章 | Skill(Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch09_skill.md |
| 最终章 | A2UI(Web) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md |
| 第十章 | A2UI(Web) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md |
| 第十一章 | TurnLoop | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch11_turnloop.md | |
| 章节 | 主题 | 核心内容 | 能力提升 |
| 第一章 | ChatModel 与 Message | 理解 Component 抽象,实现单次对话 | 基础对话能力 |
| 第二章 | Agent 与 Runner | 引入执行抽象,实现多轮对话 | 会话管理能力 |
| 第三章 | Memory 与 Session | 持久化对话历史,支持会话恢复 | 持久化能力 |
| 第四章 | Tool 与文件系统 | 添加文件访问能力,读取源码 | 工具调用能力 |
| 第五章 | Middleware | 中间件机制,统一处理横切关注点 | 扩展性增强 |
| 第六章 | Callback | 回调机制,监控 Agent 执行过程 | 可观测性 |
| 第七章 | Interrupt 与 Resume | 中断与恢复,支持长时间任务 | 可靠性增强 |
| 第八章 | Graph 与 Tool | 使用 Graph 编排复杂工作流 | 复杂编排能力 |
| 第九章 | A2UI | Agent 到 UI 的集成方案 | 生产级应用 |
| 第一章 | ChatModel 与 AgenticMessage | 理解 Component 抽象,实现单次对话 | 基础对话能力 |
| 第二章 | Agent 与 Runner | 引入执行抽象,实现多轮对话 | 会话管理能力 |
| 第三章 | Memory 与 Session | 持久化对话历史,支持会话恢复 | 持久化能力 |
| 第四章 | Tool 与文件系统 | 添加文件访问能力,读取源码 | 工具调用能力 |
| 第五章 | Middleware | 中间件机制,统一处理横切关注点 | 扩展性增强 |
| 第六章 | Callback | 回调机制,监控 Agent 执行过程 | 可观测性 |
| 第七章 | Interrupt 与 Resume | 中断与恢复,支持长时间任务 | 可靠性增强 |
| 第八章 | Graph 与 Tool | 使用 Graph 编排复杂工作流 | 复杂编排能力 |
| 第九章 | Skill | 使用 Skill 中间件加载并复用技能文档 | 知识复用能力 |
| 最终章 | A2UI | Agent 到 UI 的集成方案 | 生产级应用 |
| 维度 | ChatModel | ChatModelAgent |
| 维度 | ChatModel | ChatModelAgent |
| 定位 | Component(组件) | Agent(智能体) |
| 接口 | Generate() / Stream() | Run() -> AsyncIterator[*AgentEvent] |
| 输出 | 直接返回消息内容 | 返回事件流(包含消息、控制动作等) |
| 能力 | 单纯的模型调用 | 可扩展 tools、middleware、interrupt 等 |
| 适用场景 | 简单的对话场景 | 复杂的智能体应用 |
| 核心接口 | Generate()/ Stream() | Run() -> AsyncIterator[*AgentEvent] |
| 输出形态 | 直接返回消息内容 | 返回事件流(包含消息、控制动作等) |
| 核心能力 | 单纯的大语言模型调用 | 支持扩展 tools、middleware、interrupt 等能力 |
| 适用场景 | 简单对话交互场景 | 复杂智能体应用开发 |
write_todos工具
| Middleware | 功能说明 |
| reduction | 工具输出缩减,当工具返回内容过长时自动截断并卸载到文件系统,防止上下文溢出 |
| summarization | 对话历史自动摘要,当 token 数量超过阈值时自动生成摘要压缩历史 |
| skill | 技能加载中间件,让 Agent 能够动态加载和执行预定义的技能 |
| reduction | 工具输出缩减,当工具返回内容过长时自动截断并卸载到文件系统,防止上下文溢出 |
| summarization | 对话历史自动摘要,当 token 数量超过阈值时自动生成摘要压缩历史 |
| skill | 技能加载中间件,让 Agent 能够动态加载和执行预定义的技能 |
| 时机常量 | 对应 Handler 方法 | 触发点 | 输入/输出 |
TimingOnStart | OnStart | 组件开始处理前 | CallbackInput |
TimingOnEnd | OnEnd | 组件成功返回后 | CallbackOutput |
TimingOnError | OnError | 组件返回错误时 | error |
TimingOnStartWithStreamInput | OnStartWithStreamInput | 组件接收流式输入时 | StreamReader[CallbackInput] |
TimingOnEndWithStreamOutput | OnEndWithStreamOutput | 组件返回流式输出时 | StreamReader[CallbackOutput] |
| 时机常量 | 对应 Handler 方法 | 触发点 | 输入 / 输出 |
| TimingOnStart | OnStart | 组件开始处理前 | CallbackInput |
| TimingOnEnd | OnEnd | 组件成功返回后 | CallbackOutput |
| TimingOnError | OnError | 组件返回错误时 | error |
| TimingOnStartWithStreamInput | OnStartWithStreamInput | 组件接收流式输入时 | StreamReader[CallbackInput] |
| TimingOnEndWithStreamOutput | OnEndWithStreamOutput | 组件返回流式输出时 | StreamReader[CallbackOutput] |
| 能力 | Ch10(Runner,单轮) | Ch11(TurnLoop,多轮) |
| 流式输出 | ✅ | ✅ |
| 审批 / 中断 | ✅ | ✅ |
| 跨轮次持久运行、实时响应新输入 | ❌ 每次 Run () 独立 | ✅ Push () 随时送入 |
| 抢占正在进行的回答 | ❌ | ✅ Push(item, WithPreempt(...)) |
| 中止 Agent | ❌ | ✅ loop.Stop(WithImmediate()) |
| 灵活的 per-turn 输入构建 | ❌ 业务层手动拼装 | ✅ GenInput 回调 |
| 模式 | 具体行为 |
| AfterToolCalls | 等待当前正在执行的工具调用完成后,再取消当前轮次并启动新一轮执行 |
| AfterChatModel | 等待当前大模型调用完成后,再取消当前轮次并启动新一轮执行 |
| AnySafePoint | 在任一安全点(如工具调用间隙、模型调用间隙)立即取消当前轮次并启动新一轮执行 |
| 模式 | 具体行为 |
| loop.Stop() | 轮次边界退出:等待当前轮次完成后退出 |
| loop.Stop(WithImmediate()) | 立即退出:取消当前轮次的 context |
| loop.Stop(WithGraceful()) | 安全点退出:在下一个安全点(如 tool call 之间)退出 |
| 回调 | 调用时机 | 职责 |
| GenInput | 队列中有 items 时 | 选择消费哪些 items,构建 Agent 输入(可决定哪些 items 保留给下一轮) |
| PrepareAgent | GenInput 之后 | 返回本轮使用的 Agent 实例,支持动态调整 Agent 配置 |
| OnAgentEvents | Agent 产出事件流时 | 消费事件、渲染输出、持久化结果,是业务层处理 Agent 输出的核心入口 |
| GenResume | 从 checkpoint 恢复时 | 从新 Push 进来的 items 中提取审批结果,构建ResumeParams,实现审批恢复的自动化 |
| Store + CheckpointID | — | 启用声明式 checkpoint,TurnLoop 自动处理执行状态的保存与恢复 |