关于 eino-agent
eino-agent 是面向 Go 开发者的 Eino Agent 开发套件,提供多种 Agent 类型、运行器和中间件。它还支持可控取消、模型重试与故障切换,以及人工介入后的暂停和恢复。
主要功能
- 01ChatModelAgent、DeepAgent 与自定义 Agent
- 02TurnLoop 多轮事件循环
- 03Runner 检查点与事件流管理
- 04安全取消、重试和模型故障切换
- 05中间件扩展与 Agent 转工具
适用场景
- 01构建可调用工具的多轮 Agent
- 02处理需要规划、文件操作或子 Agent 的任务
- 03在交互应用中排队输入并抢占当前轮次
- 04在审批或澄清后恢复 Agent 执行
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cloudwego/ eino-ext --skill eino-agent安装后在 Claude Code 中输入/eino-agent确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
reference/agent-as-tool.mdchat-model-agent.mddeep-agents.mdfilesystem.mdhuman-in-the-loop.mdmiddleware.mdrunner-and-events.md详细介绍
Eino ADK 概览
导入:github.com/cloudwego/eino/adk
Agent Development Kit(ADK)为在 Go 中构建 Agent 提供了一个框架。ADK 使用 MessageType 进行泛型参数化,以同时支持经典的 *schema.Message 和新的 *schema.AgenticMessage。新用法建议优先使用 *schema.AgenticMessage。
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 类型
| 类型 | 描述 | 决策方式 |
|---|---|---|
| ChatModelAgent | ReAct 模式:大模型推理、调用工具,并循环执行直至完成 | 动态(大模型) |
| DeepAgent | 预构建 Agent,具备规划、文件系统和子 Agent 功能 | 动态(大模型) |
| TurnLoop | 基于推送的事件循环,用于多轮执行,支持抢占和生命周期管理 | 运行时 |
| 自定义 Agent | 直接实现 TypedAgent 接口 | 自定义 |
ChatModelAgent 快速入门
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)
}
}
}
取消机制
取消机制可安全、可控地终止 Agent 执行。
// 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(位掩码):
| 模式 | 行为 |
|---|---|
CancelImmediate (0) | 立即中止,流随即终止 |
CancelAfterChatModel | 等待当前模型调用完成 |
CancelAfterToolCalls | 等待当前工具调用完成 |
取消选项:
WithAgentCancelMode(mode)-- 设置安全点WithAgentCancelTimeout(d)-- 若未能在时限内到达安全点,则升级为立即取消WithRecursive()-- 将取消操作传播至嵌套的 AgentTool Agent
取消会在事件流中产生一个 CancelError,其中包含可供后续恢复的检查点数据。
模型重试
基于输出的重试,可全面控制重试决策。
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 提供:RetryAttempt、InputMessages、OutputMessage(完整拼接后的响应)、Err。
RetryDecision 控制:Retry、ModifiedInputMessages、AdditionalOptions、Backoff、RejectReason。
在流式处理中,流中会发出 WillRetryError,以表示正在进行重试。
模型故障转移
当主模型失败或生成不满意的输出时,动态切换模型。
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
},
},
})
故障转移与重试会相互作用:如果同时配置两者,且重试也已耗尽,则 FailoverContext.LastErr 将是一个 *RetryExhaustedError。
TurnLoop
基于推送的事件循环,用于多轮 Agent 执行,支持抢占、空闲超时和优雅关闭。
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()
关键概念:
Push()将条目加入队列;循环会对条目进行批处理,并通过GenInput处理- 抢占:在安全点取消当前轮次,并使用待处理条目启动新一轮
- 空闲超时:
UntilIdleFor(d)在持续d时间没有条目后自动停止 - 优雅关闭:
WithGraceful()、WithGracefulTimeout(d)、WithImmediate() - 退出状态:
TurnLoopExitState包含ExitReason、UnhandledItems、InterruptedItems
Runner
Runner 管理 Agent 生命周期、上下文传递以及中断和恢复:
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")})
中间件系统
中间件扩展 ChatModelAgent 的行为。通过 Handlers 字段进行配置:
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ...
Handlers: []adk.ChatModelAgentMiddleware{fsMiddleware, summarizationMW},
})
八种内置中间件类型(详情请参阅 reference/middleware.md):
| 中间件 | 包 | 用途 |
|---|---|---|
| FileSystem | adk/middlewares/filesystem | 文件操作(读取/写入/编辑/文件匹配/文本搜索)和 Shell |
| ToolSearch | adk/middlewares/dynamictool/toolsearch | 通过正则搜索动态选择工具 |
| ToolReduction | adk/middlewares/reduction | 截断/清除大型工具结果 |
| Summarization | adk/middlewares/summarization | 压缩冗长的对话历史 |
| PlanTask | adk/middlewares/plantask | 创建任务并跟踪进度 |
| Skill | adk/middlewares/skill | 基于技能的渐进式信息披露 |
| PatchToolCalls | adk/middlewares/patchtoolcalls | 修复历史记录中未完成的工具调用 |
| Agents.md | adk/middlewares/agentsmd | 将 Agents.md 指令注入模型输入 |
AgentAsTool
将任意 Agent 封装为工具,供其他 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 支持为人工审批、澄清和反馈进行中断和恢复。详情请参阅 reference/human-in-the-loop.md。
关键模式:工具返回 compose.Interrupt(ctx, info) 以暂停,然后调用 runner.ResumeWithParams(ctx, checkpointID, params) 继续执行。
给 Agent 的说明
- 大多数情况下,默认使用
ChatModelAgent(包含工具的单个 Agent) - 使用
Runner执行 Agent——在生产环境中切勿直接调用agent.Run() - 中间件顺序很重要:先使用 PatchToolCalls,然后是 Reduction,最后是 Summarization
- 需要内置规划、文件系统和子 Agent 时,使用
DeepAgent(adk/prebuilt/deep) - 子 Agent 需要隔离上下文(不共享历史记录)时,使用
AgentAsTool或 DeepAgents 的 SubAgents - 构建需要抢占、空闲管理或基于推送的输入的交互式应用时,使用
TurnLoop Model字段接受model.BaseModel[M]——对于经典路径,可使用任意BaseChatModel;对于 Agentic 路径,使用AgenticModel- Cancel、Retry 和 Failover 可以组合使用;Failover 会封装 Retry
WithCancel()是运行选项,而不是配置选项——每次运行都应创建一个新的实例
参考文件
需要详细的 API、示例和高级用法时,按需阅读以下文件:
- reference/chat-model-agent.md -- ChatModelAgentConfig 参考、ReAct 模式、ToolsConfig、流式处理、Cancel/Retry/Failover 详情
- reference/deep-agents.md -- DeepAgent 概念、配置、架构,以及与 ChatModelAgent 的比较
- reference/middleware.md -- 全部 8 种中间件类型及其接口、配置和示例
- reference/runner-and-events.md -- Runner 创建、AgentEvent/AgentOutput、事件迭代模式
- reference/agent-as-tool.md -- 将 Agent 封装为供其他 Agent 使用的工具
- reference/human-in-the-loop.md -- 中断 API、ResumableAgent、CheckPointStore、恢复模式
- reference/filesystem.md -- 文件系统 Backend 接口、Local 和 AgentKit 实现,以及与 DeepAgent 搭配使用的方法