Get Started with Eino ADK in 5 Minutes

This article 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.

Understanding ChatModelAgent

When we talk about “Agent,” we almost always mean: an entity powered by a large model as 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 the decision-maker, Tools as the action space, and tool feedback plus history as 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 feed back into the model context, becoming the basis for the next decision.
  4. History: Complete preservation of the reasoning trajectory, tool calls, and tool results throughout the problem-solving process.

Therefore, ChatModelAgent is not a single model call, but a sustained problem-solving process.

ChatModelAgent’s Execution Structure: ReAct Loop

ChatModelAgent’s core capability is autonomous decision-making — within 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 round, based on current context, determines what to do next.
  2. Action space (Tools): Defines the concrete actions the Agent can take.
  3. Feedback signal (Tool Feedback): Action results are injected into the context, becoming the basis for subsequent decisions — this enables the Agent to correct course based on actual execution results rather than guessing everything at once.
  4. Accumulated context (History): Complete preservation of reasoning trajectory, tool calls, and tool results. Each round, the model doesn’t see an isolated single query but the complete problem-solving process from start to current state.

All four are indispensable: without a decision-maker there’s no reasoning, without action space there’s no execution, without feedback there’s no correction, without accumulated context there’s no informed judgment based on history.

Key characteristic: Accumulated context-driven progressive decision-making. Each loop iteration doesn’t start from scratch but continues on top of the complete trajectory 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 ReAct Loop structure is fixed. So what makes your ChatModelAgent different from others, tailored to your specific problem?

Four dimensions:

  1. ChatModel — Which model makes decisions.
  2. Instruction — System instructions: role definition, behavioral constraints, few-shot examples.
  3. Tools — Tool collection: determines what the Agent can do.
  4. Middleware (ChatModelAgentMiddleware) — Inject behavior at specific lifecycle points of the ReAct Loop: intercept, modify, and enhance 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’s structure (reason → act → feedback always remains), but controls the specific runtime behavior of the loop. For example: compressing context before model calls, dynamically injecting tools before running, performing permission checks during tool calls, retrying or switching to backup models on failure. These are all runtime enhancements at specific loop points.

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 running.
  • Agent needs to reuse predefined instructions and knowledge? → Need to package reusable capabilities as Skills, loaded on demand.
  • Context growing too long, exceeding model window? → Need to automatically compress history before each model call.
  • Too many tools, stuffing all into prompt dilutes attention? → Need to search and load tools on demand.
  • Model occasionally fails or returns garbage? → Need automatic retry or backup model switching.

The common thread: they don’t need to change the ReAct Loop’s structure, only intercept and enhance at specific points in the loop. This is what Middleware does.

Corresponding built-in Middleware:

ScenarioMiddlewareWhat It Does
Need filesystem capabilitiesFileSystemInjects ls/read/write/edit/grep/execute tools before running
Reuse predefined capabilitiesSkillPackages instructions, knowledge, tools as skill units loadable on demand
Context exceeds windowReduction / SummarizationCompresses messages and tool results before model calls
Too many toolsToolSearchSearches and loads Tools on demand rather than exposing all at once
Unstable model callsModelRetry / ModelFailoverPer-model-call retry / failover switching

Each Middleware implementation injects at a specific hook point in the ReAct Loop. The diagram below shows where ChatModelAgentMiddleware hooks are positioned in the loop:

Hook point summary:

Hook PointTimingTypical Use
BeforeAgent
Before Agent runs (once only)Enhance Instruction, inject Tools
BeforeModelRewriteState
Before each model callModify Messages / ToolInfos
AfterModelRewriteState
After each model callModify model response or patch state
WrapModel
Per-model-call levelRetry, failover, rewrite model returns
WrapToolCall
Per-tool-call levelPermissions, security, output rewriting
AfterAgent
After Agent completes successfullyPost-processing, state cleanup

See the appendix at the end for a complete Middleware quick reference.

Quick Start: Create and Run a ChatModelAgent

Runner is the entry point for executing an Agent. It transforms a user request into a single Agent run, handling per-run configuration, event stream output, streaming toggles, and runtime capabilities like checkpoint/resume. The minimal usage is: put a ChatModelAgent into RunnerConfig, then call Query or Run.

The following example shows how to create a minimal ChatModelAgent and execute it via Runner:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/cloudwego/eino-ext/components/model/ark"
    "github.com/cloudwego/eino/adk"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/components/tool"
)

func main() {
    ctx := context.Background()

    // 1. Create ChatModel
    chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
        Model:  "doubao-seed-1-8-251228",
        APIKey: "your_api_key", // Replace with your API Key
    })
    if err != nil {
        log.Fatal(err)
    }

    // 2. Create ChatModelAgent
    agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
        Name:        "my-assistant",
        Description: "An assistant that can answer questions using tools.",
        Instruction: "You are a helpful assistant. Please answer user questions using available tools.",
        Model:       chatModel,
        ToolsConfig: adk.ToolsConfig{
            ToolsNodeConfig: compose.ToolsNodeConfig{
                Tools: []tool.BaseTool{
                    // Register your tools, e.g., webSearchTool
                },
            },
        },
        // Handlers: []adk.ChatModelAgentMiddleware{...}, // Register Middleware
    })
    if err != nil {
        log.Fatal(err)
    }

    // 3. Execute Agent via Runner
    runner := adk.NewRunner(ctx, adk.RunnerConfig{
        Agent:           agent,
        EnableStreaming: true,
    })

    // 4. Send user request and consume event stream
    iter := runner.Query(ctx, "Help me search for today's news")
    for {
        event, ok := iter.Next()
        if !ok {
            break
        }
        fmt.Println(event)
    }
}

Core flow: NewChatModelAgentNewRunnerRunner.Query/Run → consume AsyncIterator event stream.

For more basic examples, see: Eino: Quick Start.

Further Reading: DeepAgents

DeepAgents is a pre-built ChatModelAgent whose core value lies in two preset Middleware:

  • WriteTodos (PlanTask): Enables the main Agent to explicitly plan a task list before execution and continuously track progress during execution. Complex problems no longer rely on the model “thinking through everything at once” but instead decompose first, then progress step by step.
  • TaskTool: Enables the main Agent to delegate subtasks to sub-Agents for independent execution, with results summarized back to the main loop. This allows a single Agent’s capability boundary to be extended through composition.

Additionally, DeepAgents comes with preset system prompts and optional FileSystem Middleware, ready out-of-the-box for scenarios requiring task planning and multi-Agent collaboration.

DeepAgents = ChatModelAgent
           + WriteTodos (task planning and tracking)
           + TaskTool (subtask delegation)
           + Optional FileSystem
           + Preset system prompts

Further reading:

Further Reading: Why Not Continue Using flow/react?

Back to first principles: Graph and Agent are two fundamentally different AI application paradigms.

  • Graph’s core is determinism: Developers predefine the topology, and node transitions are determined at compile time. Input is structured, output is predictable.
  • Agent’s core is autonomy: The LLM dynamically decides the next action at runtime, execution paths are unpredictable, and output is a full-process event stream.

flow/react is essentially using Graph’s approach to “simulate” an Agent — unrolling the ReAct reasoning loop into static nodes and edges. This works, but is fundamentally a mismatch: using deterministic orchestration to carry dynamic decision-making. As Agent complexity grows, this mismatch creates systemic problems:

  1. Deliverable mismatch: Graph targets “final results,” while Agent’s deliverable is the full process (reasoning trajectory, intermediate tool calls, state changes). Using Graph for Agent means intermediate process can only be extracted through side channels like Callbacks — feasible, but a patch.
  2. Execution model mismatch: Graph is a synchronous execution model, while Agents are naturally asynchronous long-running processes. Event stream output, checkpoint/resume, interrupt recovery, and other runtime capabilities need framework-level unified management at the Agent dimension, not scattered across Graph node callbacks.
  3. Extension point mismatch: Agent runtime enhancements (context compression, dynamic tool loading, model retry, security control) are fundamentally interception and injection into the decision loop. In Graph, these capabilities have no unified mount point and are scattered across various nodes or edges; in ChatModelAgent, they have clear lifecycle hooks (Middleware).

Therefore, flow/react isn’t deprecated but returns to its best-fit position: deterministic process orchestration. When the core problem is “autonomous decision-making + runtime enhancement,” the correct abstraction is ChatModelAgent + ChatModelAgentMiddleware.

Further reading:

Appendix: Middleware Quick Reference

Instance Overview

MiddlewareDescription
ReductionTruncates overly long tool output / writes to filesystem, preventing token limit exceeded
SummarizationHistorical message summary compression
SkillReusable instructions/knowledge exposed as Tools, Agent loads on demand
FileSystemls/read/write/edit/glob/grep/execute file operation tool set
ToolSearch
tool_search
meta-tool, searches and loads Tools on demand (reduces resident tool list footprint)
PatchToolCallPatches dangling tool calls in message history (missing tool results)
SafeToolWrapToolCall-level interception of tool execution errors, converting to readable text returned to the model, allowing Agent to self-correct rather than abort
ModelRetryRetries on model call failure per configured strategy [built-in config]
ModelFailoverSwitches to backup model on model call failure [built-in config]
AgentsMDInjects Agents.md knowledge file into model context, improving context quality
PlanTaskPersistent task management tool set (create/get/update/list), supports dependency tracking
WriteTodosLightweight TODO list tool, Agent can create and track structured to-do items [DeepAgent built-in]
TaskToolSub-Agent delegation tool, main Agent uses it to dispatch subtasks to sub-Agents for independent execution [DeepAgent built-in]
PermissionTool call permission control [WIP]

Note: ModelRetry / ModelFailover are built-in fields of ChatModelAgentConfig (ModelRetryConfig / ModelFailoverConfig) in code, conceptually corresponding to the WrapModel hook. SafeTool is an example pattern (see ChatWithEino ch05), implemented as user-defined Middleware. WriteTodos / TaskTool are DeepAgent built-ins, not exported separately. Permission is a planned capability.

Categories

CategoryProblem SolvedIncludes
Extend general ToolsGive Agent more capabilitiesFileSystem, Skill, ToolSearch, PlanTask, WriteTodos, TaskTool
Handle errors during ReAct processImprove reliabilityModelRetry, ModelFailover, SafeTool, PatchToolCall
Keep context window within limitsPrevent token overflowReduction, Summarization, ToolSearch
Security and permissionsConstrain Agent behaviorPermission
Improve context content qualityHelp model see better contextSkill, AgentsMD

ToolSearch spans two categories: it is both “extend Tools” (providing on-demand tool discovery) and “keep context window within limits” (avoiding loading too many tool descriptions at once).

Further reading: