Eino ADK Overview
Import: github.com/cloudwego/eino/adk
The Agent Development Kit (ADK) provides a framework for building agents in Go. The ADK is generically parameterized by MessageType to support both classic schema.Message and the new schema.AgenticMessage. Prefer *schema.AgenticMessage for new usage.
type MessageType interface {
*schema.Message | *schema.AgenticMessage
}
type TypedAgent[M MessageType] interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]
}
// Convenience aliases for classic message type
type Agent = TypedAgent[*schema.Message]
Agent Types
| Type |
Description |
Decision |
| ChatModelAgent |
ReAct pattern: LLM reasons, calls tools, loops until done |
Dynamic (LLM) |
| DeepAgent |
Pre-built agent with planning, filesystem, sub-agents |
Dynamic (LLM) |
| TurnLoop |
Push-based event loop for multi-turn execution with preemption and lifecycle management |
Runtime |
| Custom Agent |
Implement the TypedAgent interface directly |
Custom |
ChatModelAgent Quick Start
import (
"context"
"fmt"
"log"
"github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/components/tool/utils"
"github.com/cloudwego/eino/compose"
)
func main() {
ctx := context.Background()
// 1. Create a tool
searchTool, _ := utils.InferTool("search_book", "Search books by genre",
func(ctx context.Context, input *struct {
Genre string `json:"genre" jsonschema_description:"Book genre"`
}) (string, error) {
return `{"books": ["The Great Gatsby"]}`, nil
})
// 2. Create model (BaseModel[M], not ToolCallingChatModel)
cm, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
APIKey: "your-key", Model: "gpt-4o",
})
// 3. Create agent
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "BookRecommender",
Description: "Recommends books",
Instruction: "You recommend books using the search_book tool.",
Model: cm,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{searchTool},
},
},
})
// 4. Run with Runner
runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent})
iter := runner.Query(ctx, "recommend a fiction book")
for {
event, ok := iter.Next()
if !ok {
break
}
if event.Err != nil {
log.Fatal(event.Err)
}
if event.Output != nil && event.Output.MessageOutput != nil {
msg, _ := event.Output.MessageOutput.GetMessage()
fmt.Printf("Agent[%s]: %v\n", event.AgentName, msg)
}
}
}
Cancel Mechanism
Cancel provides safe, controllable termination of agent execution.
// Create a cancel function alongside the run
cancelOpt, cancelFn := adk.WithCancel()
iter := runner.Query(ctx, "do something", cancelOpt)
// ... iterate events ...
// Cancel at a safe point (cancelFn is non-blocking, Wait blocks until complete)
handle, ok := cancelFn(adk.WithAgentCancelMode(adk.CancelAfterChatModel))
if ok {
handle.Wait()
}
CancelMode (bitmask):
| Mode |
Behavior |
CancelImmediate (0) |
Abort immediately, stream terminated |
CancelAfterChatModel |
Wait for current model call to finish |
CancelAfterToolCalls |
Wait for current tool calls to finish |
Cancel options:
WithAgentCancelMode(mode) -- set safe point
WithAgentCancelTimeout(d) -- escalate to immediate if safe point not reached in time
WithRecursive() -- propagate cancel into nested AgentTool agents
Cancel produces a CancelError on the event stream with checkpoint data for later resumption.
Model Retry
Output-based retry with full control over retry decisions.
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ...
ModelRetryConfig: &adk.ModelRetryConfig{
MaxRetries: 3,
ShouldRetry: func(ctx context.Context, retryCtx *adk.RetryContext) *adk.RetryDecision {
// Retry based on output content (e.g., empty response, bad finish reason)
if retryCtx.Err != nil {
return &adk.RetryDecision{Retry: true, Backoff: time.Second}
}
if retryCtx.OutputMessage == nil || retryCtx.OutputMessage.Content == "" {
return &adk.RetryDecision{Retry: true, Backoff: time.Second}
}
return &adk.RetryDecision{Retry: false}
},
},
})
RetryContext provides: RetryAttempt, InputMessages, OutputMessage (full concatenated response), Err.
RetryDecision controls: Retry, ModifiedInputMessages, AdditionalOptions, Backoff, RejectReason.
When streaming, a WillRetryError is emitted on the stream to signal retry is occurring.
Model Failover
Dynamic model switching when primary model fails or produces unsatisfactory output.
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ...
ModelFailoverConfig: &adk.ModelFailoverConfig[*schema.Message]{
MaxRetries: 2,
ShouldFailover: func(ctx context.Context, outputMessage *schema.Message, outputErr error) bool {
return outputErr != nil // failover on any error
},
GetFailoverModel: func(ctx context.Context, failoverCtx *adk.FailoverContext[*schema.Message]) (
model.BaseModel[*schema.Message], []*schema.Message, error) {
// Return a different model and optionally modified input
return backupModel, failoverCtx.InputMessages, nil
},
},
})
Failover interacts with Retry: when both are configured, FailoverContext.LastErr will be a *RetryExhaustedError if retry was also exhausted.
TurnLoop
Push-based event loop for multi-turn agent execution with preemption, idle timeout, and graceful shutdown.
import "github.com/cloudwego/eino/adk"
loop := adk.NewTurnLoop(adk.TurnLoopConfig[string, *schema.Message]{
GenInput: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], items []string) (*adk.GenInputResult[string, *schema.Message], error) {
// Convert pushed items into agent input
combined := strings.Join(items, "\n")
return &adk.GenInputResult[string, *schema.Message]{
RunCtx: ctx,
Input: &adk.TypedAgentInput[*schema.Message]{Messages: []*schema.Message{schema.UserMessage(combined)}},
Consumed: items,
}, nil
},
PrepareAgent: func(ctx context.Context, loop *adk.TurnLoop[string, *schema.Message], consumed []string) (adk.Agent, error) {
return myAgent, nil
},
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
}
// Process events (e.g., send to client)
}
return nil
},
})
// Start the loop
loop.Run(ctx)
// Push items (non-blocking, returns (ok bool, resolved <-chan struct{}))
loop.Push("user message 1")
// Preempt current turn with new input
loop.Push("urgent message", adk.WithPreempt[string, *schema.Message](adk.AfterChatModel))
// Stop gracefully
loop.Stop(adk.WithGraceful())
// Wait for exit and get final state
exitState := loop.Wait()
Key concepts:
Push() queues items; the loop batches and processes them via GenInput
- Preemption: cancel current turn at a safe point and start new turn with pending items
- Idle timeout:
UntilIdleFor(d) auto-stops after no items for duration d
- Graceful shutdown:
WithGraceful(), WithGracefulTimeout(d), WithImmediate()
- Exit state:
TurnLoopExitState contains ExitReason, UnhandledItems, InterruptedItems
Runner
The Runner manages agent lifecycle, context passing, and interrupt/resume:
runner := adk.NewRunner(ctx, adk.RunnerConfig{
Agent: myAgent,
EnableStreaming: true,
CheckPointStore: myStore, // for interrupt/resume
})
// Query (convenience for single user message)
iter := runner.Query(ctx, "hello")
// Run (full control over input messages)
iter := runner.Run(ctx, []*schema.Message{schema.UserMessage("hello")})
Middleware System
Middleware extends ChatModelAgent behavior. Configure via Handlers field:
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ...
Handlers: []adk.ChatModelAgentMiddleware{fsMiddleware, summarizationMW},
})
Eight built-in middleware types (see reference/middleware.md for details):
| Middleware |
Package |
Purpose |
| FileSystem |
adk/middlewares/filesystem |
File ops (read/write/edit/glob/grep) + shell |
| ToolSearch |
adk/middlewares/dynamictool/toolsearch |
Dynamic tool selection via regex search |
| ToolReduction |
adk/middlewares/reduction |
Truncate/clear large tool results |
| Summarization |
adk/middlewares/summarization |
Compress long conversation history |
| PlanTask |
adk/middlewares/plantask |
Task creation and progress tracking |
| Skill |
adk/middlewares/skill |
Skill-based progressive disclosure |
| PatchToolCalls |
adk/middlewares/patchtoolcalls |
Fix dangling tool calls in history |
| Agents.md |
adk/middlewares/agentsmd |
Inject Agents.md instructions into model input |
AgentAsTool
Wrap any Agent as a Tool for use by another agent:
subAgent := createMySubAgent()
agentTool := adk.NewAgentTool(ctx, subAgent)
parentAgent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{agentTool},
},
},
})
Human-in-the-Loop
ChatModelAgent supports interrupt and resume for human approval, clarification, and feedback. See reference/human-in-the-loop.md for details.
Key pattern: tool returns compose.Interrupt(ctx, info) to pause, then runner.ResumeWithParams(ctx, checkpointID, params) to continue.
Instructions to Agent
- Default to
ChatModelAgent for most use cases (single agent with tools)
- Use
Runner to execute agents -- never call agent.Run() directly in production
- Middleware order matters: PatchToolCalls first, then Reduction, then Summarization
- Use
DeepAgent (adk/prebuilt/deep) when you need built-in planning + filesystem + sub-agents
- Use
AgentAsTool or DeepAgents' SubAgents when a sub-agent needs isolated context (no shared history)
- Use
TurnLoop when building interactive applications that need preemption, idle management, or push-based input
Model field accepts model.BaseModel[M] -- for classic path use any BaseChatModel, for agentic path use AgenticModel
- Cancel, Retry, and Failover can all be combined; failover wraps retry
WithCancel() is a run option, not a config option -- create fresh per-run
Reference Files
Read these files on-demand for detailed API, examples, and advanced usage:
- [reference/chat-model-agent.md](reference/chat-model-agent.md) -- ChatModelAgentConfig reference, ReAct pattern, ToolsConfig, streaming, Cancel/Retry/Failover details
- [reference/deep-agents.md](reference/deep-agents.md) -- DeepAgent concept, config, architecture, comparison with ChatModelAgent
- [reference/middleware.md](reference/middleware.md) -- All 8 middleware types with interface, config, and examples
- [reference/runner-and-events.md](reference/runner-and-events.md) -- Runner creation, AgentEvent/AgentOutput, event iteration patterns
- [reference/agent-as-tool.md](reference/agent-as-tool.md) -- Wrapping an Agent as a Tool for use by another agent
- [reference/human-in-the-loop.md](reference/human-in-the-loop.md) -- Interrupt APIs, ResumableAgent, CheckPointStore, resume patterns
- [reference/filesystem.md](reference/filesystem.md) -- Filesystem Backend interface, Local and AgentKit implementations, usage with DeepAgent