cloudwego/eino-ext

eino-agent

Eino ADK agent construction, middleware, and runner.

ソースを見る
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

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.

go
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

TypeDescriptionDecision
ChatModelAgentReAct pattern: LLM reasons, calls tools, loops until doneDynamic (LLM)
DeepAgentPre-built agent with planning, filesystem, sub-agentsDynamic (LLM)
TurnLoopPush-based event loop for multi-turn execution with preemption and lifecycle managementRuntime
Custom AgentImplement the TypedAgent interface directlyCustom

ChatModelAgent Quick Start

go
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.

go
// 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):

ModeBehavior
CancelImmediate (0)Abort immediately, stream terminated
CancelAfterChatModelWait for current model call to finish
CancelAfterToolCallsWait 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.

go
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.

go
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.

go
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:

go
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:

go
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    // ...
    Handlers: []adk.ChatModelAgentMiddleware{fsMiddleware, summarizationMW},
})

Eight built-in middleware types (see reference/middleware.md for details):

MiddlewarePackagePurpose
FileSystemadk/middlewares/filesystemFile ops (read/write/edit/glob/grep) + shell
ToolSearchadk/middlewares/dynamictool/toolsearchDynamic tool selection via regex search
ToolReductionadk/middlewares/reductionTruncate/clear large tool results
Summarizationadk/middlewares/summarizationCompress long conversation history
PlanTaskadk/middlewares/plantaskTask creation and progress tracking
Skilladk/middlewares/skillSkill-based progressive disclosure
PatchToolCallsadk/middlewares/patchtoolcallsFix dangling tool calls in history
Agents.mdadk/middlewares/agentsmdInject Agents.md instructions into model input

AgentAsTool

Wrap any Agent as a Tool for use by another agent:

go
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:

同じリポジトリから

関連する Skills

すべての Skills
cloudwego
コミュニティ

eino-component

Eino component selection, configuration, and usage. Use when a user needs to choose or configure a ChatModel, AgenticModel, Embedding, Retriever, Indexer, Tool, Document loader/parser/transformer, Prompt template, or Callback handler. Covers all component interfaces and their implementations in eino-ext including OpenAI, Claude, Gemini, Ark, Ollama, Milvus, Elasticsearch, Redis, MCP tools, and more.

導入数
2
GitHub Stars
818
更新日
9月24日
cloudwego
コミュニティ

eino-compose

Eino orchestration with Graph, Chain, and Workflow. Use when a user needs to build multi-step pipelines, compose components into executable graphs, handle streaming between nodes, use branching or parallel execution, manage state with checkpoints, or understand the Runnable abstraction. Covers Graph (directed graph with cycles), Chain (linear sequential), and Workflow (DAG with field mapping).

導入数
2
GitHub Stars
818
更新日
9月24日
cloudwego
コミュニティ

eino-guide

Eino framework overview, concepts, and navigation. Use when a user asks general questions about Eino, needs help getting started, wants to understand the architecture, or is unsure which Eino skill to use. Eino is a Go framework for building LLM applications with components, orchestration graphs, and an agent development kit.

導入数
2
GitHub Stars
818
更新日
9月24日