From 26e2e9eb85c7f39591e6715c98702fa7484465b8 Mon Sep 17 00:00:00 2001 From: "shentong.martin" Date: Tue, 24 Mar 2026 17:50:41 +0800 Subject: [PATCH 1/3] docs(eino): sync chinese docs from feishu --- content/zh/docs/eino/FAQ.md | 4 +- .../checkpoint_interrupt.md | 4 +- .../stream_programming_essentials.md | 2 +- .../components/chat_model_guide.md | 2 +- .../Middleware_FileSystem/_index.md | 237 --------------- .../Middleware_PatchToolCalls.md | 2 +- .../Middleware_PlanTask.md | 2 +- .../Middleware_Skill.md | 108 ++----- .../Middleware_Summarization.md | 2 +- .../Middleware_ToolReduction.md | 2 +- .../Middleware_ToolSearch.md | 2 +- .../filesystem_backend/_index.md | 175 +++++++++++ .../backend_ark_agentkit_sandbox.md} | 2 +- ...07\344\273\266\347\263\273\347\273\237.md" | 2 +- .../middleware_agentsmd.md | 280 ++++++++++++++++++ .../middleware_filesystem.md | 187 ++++++++++++ .../eino_adk/adk_agent_callback.md | 2 +- .../agent_implementation/chat_model.md | 2 +- .../agent_implementation/deepagents.md | 2 +- .../core_modules/eino_adk/agent_preview.md | 2 +- .../docs/eino/ecosystem_integration/_index.md | 6 +- content/zh/docs/eino/overview/eino_adk0_1.md | 2 +- .../zh/docs/eino/overview/graph_or_agent.md | 2 +- .../chapter_01_chatmodel_and_message.md | 2 +- .../quick_start/chapter_09_a2ui_protocol.md | 2 +- .../quick_start/chapter_09_skill_console.md | 4 +- .../Eino_v0.8._-adk_middlewares/_index.md | 4 +- .../img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png | Bin 162840 -> 0 bytes 28 files changed, 689 insertions(+), 354 deletions(-) delete mode 100644 content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md create mode 100644 content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md rename content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/{Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md => filesystem_backend/backend_ark_agentkit_sandbox.md} (99%) rename "content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" => "content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" (99%) create mode 100644 content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md create mode 100644 content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md delete mode 100644 static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png diff --git a/content/zh/docs/eino/FAQ.md b/content/zh/docs/eino/FAQ.md index 5f4b47f5f00..dc39656ace1 100644 --- a/content/zh/docs/eino/FAQ.md +++ b/content/zh/docs/eino/FAQ.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-02" +date: "2026-03-24" lastmod: "" tags: [] title: FAQ @@ -142,7 +142,7 @@ eino-ext 支持的多模态输入输出场景,可以查阅 [https://www.cloudw eino-ext 部分 module 报错 undefined: schema.NewParamsOneOfByOpenAPIV3 等问题,升级报错的 eino-ext module 到最新版本即可。 -如果 schema 改造比较复杂,可以参考上面的 Discussion #397,使用其中的 JSONSchema 转换工具方法辅助转换。 +如果 schema 改造比较复杂,可以使用 JSONSchema 转换工具方法辅助转换。 # Q: Eino-ext 提供的 ChatModel 有哪些模型是支持 Response API 形式调用嘛? diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md index e32e898bc9b..01ef03745c3 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-02" +date: "2026-03-24" lastmod: "" tags: [] title: Interrupt & CheckPoint使用手册 @@ -109,7 +109,7 @@ type CheckpointStore interface { ### 注册序列化方法 -CheckPoint 的保存和读取涉及对 Graph 节点输入输出以及 State 的序列化和反序列化,在仅使用简单类型或 eino 内置类型(比如 Message 或 Document)时,用户无需额外操作;当引入自定义 struct 时,需要提前注册类型,Eino 提供了注册方法 `schema.RegisterName`: +CheckPoint 的保存和读取涉及对 Graph 节点输入输出以及 State 的序列化和反序列化,在仅使用简单类型或 eino 内置类型(比如 Message 或 Document)时,用户无需额外操作;当引入自定义 struct 时,需要提前注册类型,Eino 提供了注册方法 `schema.``RegisterName`: ```go package main diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md index 9c7025c0804..39ef62359a2 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md @@ -101,7 +101,7 @@ Collect 和 Transform 两种流式范式,目前只在编排场景有用到。 上面的 Concat message stream 是 Eino 框架自动提供的能力,即使不是 message,是任意的 T,只要满足特定的条件,Eino 框架都会自动去做这个 StreamReader[T] 到 T 的转化,这个条件是:**在编排中,当一个组件的上游输出是 StreamReader[T],但是组件只提供了 T 作为输入的业务接口时,框架会自动将 StreamReader[T] concat 成 T,再输入给这个组件。** > 💡 -> 框架自动将 StreamReader[T] concat 成 T 的过程,可能需要用户提供一个 Concat function。详见 [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles#share-FaVnd9E2foy4fAxtbTqcsgq3n5f) 中关于“合并帧”的章节。 +> 框架自动将 StreamReader[T] concat 成 T 的过程,可能需要用户提供一个 Concat function。详见 [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) 中关于“合并帧”的章节。 另一方面,考虑一个相反的例子。还是 React Agent,这次是一个更完整的编排示意图: diff --git a/content/zh/docs/eino/core_modules/components/chat_model_guide.md b/content/zh/docs/eino/core_modules/components/chat_model_guide.md index 83df1c2b9fb..6f73bc5ae4d 100644 --- a/content/zh/docs/eino/core_modules/components/chat_model_guide.md +++ b/content/zh/docs/eino/core_modules/components/chat_model_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-09" +date: "2026-03-24" lastmod: "" tags: [] title: ChatModel 使用说明 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md deleted file mode 100644 index 498bbcfd721..00000000000 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -Description: "" -date: "2026-03-12" -lastmod: "" -tags: [] -title: FileSystem -weight: 1 ---- - -> 💡 -> Package: [https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) - -# 概述 - -FileSystem Middleware 为 Agent 提供文件系统访问与大体积工具结果卸载能力。 -它定义了一套统一的文件系统 Backend 接口,用户可直接使用默认的内存实现,或根据自身需求扩展自定义后端。 - -核心功能包括: - -- 访问虚拟文件系统(ls/read/write/edit/glob/grep) -- 将超大工具结果自动卸载到文件系统,并在 Agent 上下文中保留摘要,按需加载内容 - -# Backend 接口 - -FileSystem Middleware 通过 `github.com/cloudwego/eino/adk/filesystem` 中的 Backend 接口操作文件系统: - -```go -type Backend interface { - // 列出指定路径下的文件(结构化信息) - LsInfo(path string) ([]FileInfo, error) - // 按 offset 和 limit 读取文件内容(返回适合 LLM 的文本) - Read(ctx context.Context, filePath string, offset, limit int) (string, error) - // 在指定路径中 grep pattern,返回匹配列表 - GrepRaw(ctx context.Context, pattern string, path, glob *string) ([]GrepMatch, error) - // 根据 pattern 和 path 进行 glob 匹配 - GlobInfo(ctx context.Context, pattern, path string) ([]FileInfo, error) - // 写入或更新文件 - Write(ctx context.Context, filePath, content string) error - // 替换文件中的字符串 - Edit(ctx context.Context, filePath, oldString, newString string, replaceAll bool) error -} -``` - -### **扩展接口** - -```go -type Shell interface { - Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) -} - -type StreamingShell interface { - ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) -} -``` - -`InMemoryBackend` 是 `Backend` 接口的内存实现,将文件存储在 map 中,支持并发安全访问。 - -```go -import "github.com/cloudwego/eino/adk/filesystem" - -ctx := context.Background() -backend := filesystem.NewInMemoryBackend() - -// 写入文件 -err := backend.Write(ctx, &filesystem.WriteRequest{ - FilePath: "/example/test.txt", - Content: "Hello, World!\nLine 2\nLine 3", -}) - -// 读取文件 -content, err := backend.Read(ctx, &filesystem.ReadRequest{ - FilePath: "/example/test.txt", - Offset: 1, - Limit: 10, -}) - -// 列出目录 -files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ - Path: "/example", -}) - -// 搜索内容 -matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ - Pattern: "Hello", - Path: "/example", -}) - -// 编辑文件 -err = backend.Edit(ctx, &filesystem.EditRequest{ - FilePath: "/example/test.txt", - OldString: "Hello", - NewString: "Hi", - ReplaceAll: false, -}) -``` - -其他 Backend 实现: - -# Filesystem Middleware - -Middleware 会自动向 Agent 注入一组工具及对应的 system prompt,使其能够直接操作文件系统。 - -### **创建中间件** - -推荐使用 `New` 函数创建中间件(返回 `ChatModelAgentMiddleware`): - -```go -import "github.com/cloudwego/eino/adk/middlewares/filesystem" - -middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ - Backend: myBackend, - // 如果需要 shell 命令执行能力,设置 Shell 或 StreamingShell - Shell: myShell, -}) -if err != nil { - // handle error -} - -agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ - // ... - Middlewares: []adk.ChatModelAgentMiddleware{middleware}, -}) -``` - -### **Config 配置项** - -```go -type MiddlewareConfig struct { - // Backend 提供文件系统操作,必填 - Backend filesystem.Backend - - // Shell 提供 shell 命令执行能力 - // 如果设置,会注册 execute 工具 - // 可选,与 StreamingShell 互斥 - Shell filesystem.Shell - - // StreamingShell 提供流式 shell 命令执行能力 - // 如果设置,会注册流式 execute 工具 - // 可选,与 Shell 互斥 - StreamingShell filesystem.StreamingShell - - // CustomSystemPrompt 覆盖默认的系统提示词 - // 可选,默认 ToolsSystemPrompt - CustomSystemPrompt *string - - // 以下为各工具的自定义名称,均为可选 - CustomLsToolName *string // 默认 "ls" - CustomReadFileToolName *string // 默认 "read_file" - CustomWriteFileToolName *string // 默认 "write_file" - CustomEditFileToolName *string // 默认 "edit_file" - CustomGlobToolName *string // 默认 "glob" - CustomGrepToolName *string // 默认 "grep" - CustomExecuteToolName *string // 默认 "execute" - - // 以下为各工具的自定义描述,均为可选 - CustomLsToolDesc *string - CustomReadFileToolDesc *string - CustomGrepToolDesc *string - CustomGlobToolDesc *string - CustomWriteFileToolDesc *string - CustomEditToolDesc *string - CustomExecuteToolDesc *string -} -``` - -> 💡 -> `New` 函数返回 `ChatModelAgentMiddleware`,提供更好的上下文传播能力。对于需要大型工具结果卸载的场景,请使用 `NewMiddleware` 函数或配合 ToolReduction middleware 使用。 - -注入的工具: - -这些工具均附带默认的英文描述(description)与内置提示词(system prompt)。如需切换为中文,可通过 `adk.SetLanguage()` 设置: - -``` -import "github.com/cloudwego/eino/adk" - -adk.SetLanguage(adk.LanguageChinese) // 切换为中文 -adk.SetLanguage(adk.LanguageEnglish) // 切换为英文(默认) -``` - -你也可以通过 `Config` 自定义说明文本和工具名称: - -```go -type MiddlewareConfig struct { - // 覆盖默认 System Prompt(可选) - CustomSystemPrompt *string - - // 覆盖各工具名称(可选) - CustomLsToolName *string - CustomReadFileToolName *string - // ... - - // 覆盖各工具 description(可选) - CustomLsToolDesc *string - CustomReadFileToolDesc *string - // ... -} -``` - -# [deprecated]工具结果卸载 - -> 💡 -> 该功能即将在 0.8.0 中 deprecate。迁移到 [Middleware: ToolReduction](/zh/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_toolreduction) - -当工具调用结果过大(例如读取大文件、grep 命中大量内容),如果继续将完整结果放入对话上下文,会导致: - -- token 急剧增加 -- Agent 历史上下文污染 -- 推理效率变差 - -为此,Middleware 提供了 自动卸载机制: - -- 当结果大小超过阈值(默认 20,000 tokens)时 - → 不直接返回全部内容给 LLM -- 实际结果会保存到文件系统 -- 上下文中仅包含: - - 摘要 - - 文件路径(agent 可再次调用工具读取) - - - -该功能默认启用,可通过配置调整行为: - -```go -type Config struct { - // other config... - - // 关闭自动卸载 - WithoutLargeToolResultOffloading bool - - // 自定义触发阈值(默认 20000 tokens) - LargeToolResultOffloadingTokenLimit int - - // 自定义卸载文件生成路径 - // 默认路径格式: /large_tool_result/{ToolCallID} - LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) -} -``` diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md index bdabd3ef26c..bd3e960647d 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md @@ -4,7 +4,7 @@ date: "2026-03-09" lastmod: "" tags: [] title: PatchToolCalls -weight: 7 +weight: 8 --- adk/middlewares/patchtoolcalls 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 2ffde35c6d9..daf5662c286 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 @@ -4,7 +4,7 @@ date: "2026-03-09" lastmod: "" tags: [] title: PlanTask -weight: 4 +weight: 6 --- # PlanTask 中间件 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md index 2ea323889da..2753ec9ae44 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md @@ -1,10 +1,10 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] title: Skill -weight: 2 +weight: 3 --- Skill Middleware 为 Eino ADK Agent 提供了 Skill 支持,使 Agent 能够动态发现和使用预定义的技能来更准确、高效地完成任务。 @@ -111,25 +111,6 @@ type Backend interface {
Get
根据名称获取完整的技能内容。当 Agent 决定使用某个技能时调用,返回包含详细指令的完整 Skill 结构 -## AgentHub 和 ModelHub - -当 Skill 使用 Context 模式(fork/isolate)时,需要配置 AgentHub 和 ModelHub: - -```go -// AgentFactory 用于创建 Agent 实例 -type AgentFactory func(ctx context.Context, m model.ToolCallingChatModel) (adk.Agent, error) - -// AgentHub 提供 Agent 工厂函数 -type AgentHub interface { - Get(ctx context.Context, name string) (AgentFactory, error) -} - -// ModelHub 提供模型实例 -type ModelHub interface { - Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) -} -``` - ### **NewBackendFromFilesystem** 基于 `filesystem.Backend` 接口的后端实现,在指定的目录下读取技能: @@ -158,85 +139,34 @@ func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystem ### **filesystem.Backend 实现** -`filesystem.Backend` 接口有以下两种实现可供选择: +`filesystem.Backend` 接口有以下两种实现可供选择,详见 [Middleware: FileSystem](/zh/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_filesystem) -#### **Local Backend(本地文件系统)** +## AgentHub 和 ModelHub -基于本地文件系统的实现,适用于 Unix/MacOS 环境: +当 Skill 使用 Context 模式(fork/isolate)时,需要配置 AgentHub 和 ModelHub: ```go -import "github.com/cloudwego/eino-ext/adk/backend/local" - -type Config struct { - ValidateCommand func(string) error // optional +// AgentHubOptions contains options passed to AgentHub.Get when creating an agent for skill execution. +type AgentHubOptions struct { + // Model is the resolved model instance when a skill specifies a "model" field in frontmatter. + // nil means the skill did not specify a model override; implementations should use their default. + Model model.ToolCallingChatModel } -func NewBackend(ctx context.Context, cfg *Config) (filesystem.Backend, error) -``` - - - - -
字段类型必需说明
ValidateCommand
func(string) error
命令验证函数,用于在执行命令前进行安全校验
- -> **注意**:仅支持 Unix/MacOS,不支持 Windows。 - -#### **Sandbox Backend(沙箱环境)** - -基于火山引擎 AgentKit 沙箱工具的实现,适用于需要隔离执行环境的场景: - -```go -import "github.com/cloudwego/eino-ext/adk/backend/agentkit" - -type Config struct { - AccessKeyID string - SecretAccessKey string - Region Region // 可选,默认 cn-beijing - ToolID string - SessionID string // 与 UserSessionID 至少提供一个 - UserSessionID string // 与 SessionID 至少提供一个 - SessionTTL int // 可选,默认 1800 秒 - ExecutionTimeout int // 可选 - HTTPClient *http.Client // 可选 +// AgentHub provides agent instances for context mode (fork/fork_with_context) execution. +type AgentHub interface { + // Get returns an Agent by name. When name is empty, implementations should return a default agent. + // The opts parameter carries skill-level overrides (e.g., model) resolved by the framework. + Get(ctx context.Context, name string, opts *AgentHubOptions) (adk.Agent, error) } -func NewSandboxToolBackend(config *Config) (filesystem.Backend, error) -``` - - - - - - - - - - - -
字段类型必需说明
AccessKeyID
string
火山引擎 Access Key ID
SecretAccessKey
string
火山引擎 Secret Access Key
Region
Region
区域,支持
cn-beijing
(默认)和
cn-shanghai
ToolID
string
沙箱工具 ID
SessionID
string
会话 ID,与 UserSessionID 至少提供一个
UserSessionID
string
用户会话 ID,与 SessionID 至少提供一个
SessionTTL
int
会话存活时间(秒),范围 60-86400,默认 1800
ExecutionTimeout
int
代码执行超时时间(秒)
- -> 更多信息请参考:[火山引擎 AgentKit 文档](https://www.volcengine.com/docs/86681/1847934) - -**LocalBackend** 内置的本地文件系统后端实现,在指定的目录下读取技能: - -```go -type LocalBackendConfig struct { - BaseDir string +// ModelHub 提供模型实例 +type ModelHub interface { + Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) } - -func NewLocalBackend(config *LocalBackendConfig) (*LocalBackend, error) ``` - - - -
字段类型必需说明
BaseDir
string
技能根目录的路径。LocalBackend 会扫描此目录下的所有子目录,查找包含
SKILL.md
文件的目录作为技能
- -工作方式: - -- 扫描 `BaseDir` 下的一级子目录 -- 查找每个子目录中的 `SKILL.md` 文件 -- 解析 YAML frontmatter 获取元数据 +### ## 初始化 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 94604afcf30..f59fe4dbc17 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 @@ -4,7 +4,7 @@ date: "2026-03-09" lastmod: "" tags: [] title: Summarization -weight: 3 +weight: 4 --- ## 概述 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md index fae42a03c9a..db4d192d2e6 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md @@ -4,7 +4,7 @@ date: "2026-03-12" lastmod: "" tags: [] title: Reduction -weight: 6 +weight: 5 --- # Reduction 中间件 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md index a220a419db4..de9bf1ac22a 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md @@ -4,7 +4,7 @@ date: "2026-03-09" lastmod: "" tags: [] title: ToolSearch -weight: 5 +weight: 7 --- # ToolSearch 中间件 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md new file mode 100644 index 00000000000..5291c0c17df --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md @@ -0,0 +1,175 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: FileSystem Backend +weight: 1 +--- + +> 💡 +> Package: [github.com/cloudwego/eino/adk/filesystem](https://github.com/cloudwego/eino/tree/main/adk/filesystem) + +## 背景与目的 + +在 AI Agent 场景中,Agent 往往需要与文件系统交互——读取文件内容、搜索代码、编辑配置、执行命令等。然而,不同的运行环境对文件系统的访问方式差异很大: + +- **本地开发环境**:直接操作本机文件系统,零配置即可使用 +- **云端沙箱环境**:通过远程 API 操作隔离的沙箱文件系统,需要认证和网络通信 +- **测试环境**:需要内存级别的模拟文件系统,无需真实磁盘 I/O +- **自定义存储**:可能需要对接 OSS、数据库等非传统文件系统 + +如果每种环境都各自实现一套文件操作逻辑,会导致 Middleware 和 Agent 代码与底层存储实现耦合,难以复用和测试。 + +为了解决这一问题,Eino ADK 抽象出 `filesystem.Backend` 接口,作为**统一的文件系统操作协议**。它的设计目标是: + +1. **解耦存储与业务**:Middleware 只依赖 Backend 接口,不关心底层是本地磁盘、远程沙箱还是内存模拟 +2. **可插拔替换**:通过切换 Backend 实现,同一个 Agent 可以在不同环境中运行,无需修改任何业务代码 +3. **易于测试**:内置 `InMemoryBackend` 实现,方便在单元测试中模拟文件系统行为 +4. **可扩展性**:所有方法使用结构体参数,未来新增字段不会破坏已有实现的兼容性 + +## Backend 接口 + +```go +type Backend interface { + // 列出指定路径下的文件和目录信息 + LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error) + // 读取文件内容,支持按行分页(offset + limit) + Read(ctx context.Context, req *ReadRequest) (*FileContent, error) + // 在指定路径中搜索匹配 pattern 的内容,返回匹配列表 + GrepRaw(ctx context.Context, req *GrepRequest) ([]GrepMatch, error) + // 根据 glob pattern 和路径查找匹配的文件 + GlobInfo(ctx context.Context, req *GlobInfoRequest) ([]FileInfo, error) + // 写入或创建文件 + Write(ctx context.Context, req *WriteRequest) error + // 替换文件中的字符串内容 + Edit(ctx context.Context, req *EditRequest) error +} +``` + +### 扩展接口 + +除核心文件操作外,Backend 还可以选择性地实现 Shell 命令执行能力: + +```go +// Shell 提供同步命令执行能力 +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +// StreamingShell 提供流式命令执行能力,适用于长时间运行的命令 +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +当 Backend 同时实现了 `Shell` 或 `StreamingShell` 接口时,Filesystem Middleware 会额外注册 `execute` 工具,允许 Agent 执行 shell 命令。 + +### 核心数据类型 + + + + + + + + + + + + +
类型描述
FileInfo
文件/目录信息:路径、是否目录、大小、修改时间
FileContent
文件内容 + 行号信息
GrepMatch
搜索匹配结果:内容、路径、行号
ReadRequest
读取请求:路径、offset(从第几行开始,1-based)、limit(读取行数)
GrepRequest
搜索请求:pattern(支持正则)、路径、glob 过滤、文件类型过滤等
WriteRequest
写入请求:路径、内容
EditRequest
编辑请求:路径、旧字符串、新字符串、是否全部替换
ExecuteRequest
命令执行请求:命令字符串、是否后台运行
ExecuteResponse
命令执行结果:输出内容、退出码、是否被截断
+ +## 内置实现:InMemoryBackend + +`InMemoryBackend` 是框架内置的 Backend 实现,将文件存储在内存 map 中,主要用于: + +- **单元测试**:无需真实文件系统即可测试 Agent 和 Middleware 的文件操作逻辑 +- **轻量场景**:不需要持久化的临时文件操作 +- **工具结果卸载**:Filesystem Middleware 的大型工具结果卸载功能默认使用 InMemoryBackend 存储 + +```go +import "github.com/cloudwego/eino/adk/filesystem" + +ctx := context.Background() +backend := filesystem.NewInMemoryBackend() + +// 写入文件 +err := backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/example/test.txt", + Content: "Hello, World!\nLine 2\nLine 3", +}) + +// 读取文件(支持分页) +content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/example/test.txt", + Offset: 1, + Limit: 10, +}) + +// 列出目录 +files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/example", +}) + +// 搜索内容(支持正则) +matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Pattern: "Hello", + Path: "/example", +}) + +// 编辑文件 +err = backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/example/test.txt", + OldString: "Hello", + NewString: "Hi", + ReplaceAll: false, +}) +``` + +特性: + +- 线程安全(基于 `sync.RWMutex`) +- GrepRaw 支持正则匹配、大小写不敏感、上下文行数等高级选项 +- GrepRaw 内部采用并行处理(最多 10 个 worker) + +## 外部实现 + +以下 Backend 实现位于 [eino-ext](https://github.com/cloudwego/eino-ext) 仓库: + +- **Local Backend** — 本地文件系统实现,直接操作本机磁盘,零配置开箱即用 +- **Ark Agentkit Sandbox Backend** — 火山引擎 Agentkit 远程沙箱实现,在隔离的云端环境中执行文件操作 + +### 实现对比 + + + + + + + + + +
特性InMemoryLocalAgentkit Sandbox
执行模型内存本地直接远程沙箱
网络依赖需要
配置复杂度零配置零配置需要凭证
持久化
Shell 支持支持(含流式)支持
适用场景测试/临时开发/本地环境多租户/生产环境
+ +## 自定义实现 + +如需对接自定义存储(如 OSS、数据库等),只需实现 `Backend` 接口即可: + +```go +type MyBackend struct { + // ... +} + +func (b *MyBackend) LsInfo(ctx context.Context, req *filesystem.LsInfoRequest) ([]filesystem.FileInfo, error) { + // 自定义实现 +} + +func (b *MyBackend) Read(ctx context.Context, req *filesystem.ReadRequest) (*filesystem.FileContent, error) { + // 自定义实现 +} + +// ... 实现其余方法 +``` + +如果需要支持命令执行,还可以额外实现 `Shell` 或 `StreamingShell` 接口。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md similarity index 99% rename from content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md rename to content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md index 68e9eac2d4c..47767e5af6c 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] title: Ark Agentkit Sandbox diff --git "a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" "b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" similarity index 99% rename from "content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" rename to "content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" index 4f1b07ab907..00a3dff1644 100644 --- "a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" +++ "b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-12" +date: "2026-03-24" lastmod: "" tags: [] title: 本地文件系统 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md new file mode 100644 index 00000000000..c4edcf4e9d1 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md @@ -0,0 +1,280 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: AgentsMD +weight: 9 +--- + +## 概述 + +`agentsmd` 是 Eino ADK 提供的一个中间件,用于在每次模型调用时**自动将 Agents.md 文件内容注入到模型输入消息中**。注入是瞬态的——内容在模型调用时动态添加,不会持久化到会话状态中,因此**不会被摘要/压缩中间件处理**。 + +**核心价值**:通过 Agents.md 文件为 Agent 定义系统级的行为指令和上下文信息(类似 Claude Code 的 CLAUDE.md),无需手动管理 system prompt 的拼接。 + +**包路径**:`github.com/cloudwego/eino/adk/middlewares/agentsmd` + +--- + +## 快速开始 + +### 最小化示例 + +```go +package main + +import ( + "context" + "fmt" + + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/adk/middlewares/agentsmd" +) + +func main() { + ctx := context.Background() + + // 1. 准备 Backend(文件读取后端) + backend := NewLocalFileBackend("/path/to/project") + + // 2. 创建 agentsmd 中间件 + mw, err := agentsmd.New(ctx, &agentsmd.Config{ + Backend: backend, + AgentsMDFiles: []string{"/home/user/project/agents.md"}, + }) + if err != nil { + panic(err) + } + + // 3. 将中间件配置到 Agent + // agent := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // Middlewares: []adk.ChatModelAgentMiddleware{mw}, + // }) + _ = mw + fmt.Println("agentsmd middleware created successfully") +} +``` + +--- + +## 配置详解 + +### Config 结构体 + +```go +type Config struct { + // Backend 提供文件访问能力,用于加载 Agents.md 文件。 + // 可以使用本地文件系统、远程存储或任何其他后端实现。 + // 必填。 + Backend Backend + + // AgentsMDFiles 指定要加载的 Agents.md 文件路径的有序列表。 + // 文件按照给定顺序加载和注入。 + // 文件内部支持 @import 语法进行递归引入(最大深度 5)。 + AgentsMDFiles []string + + // AllAgentsMDMaxBytes 限制所有加载的 Agents.md 内容的总字节大小。 + // 文件按顺序加载;一旦累计大小超过此限制,剩余文件将被跳过。 + // 每个单独的文件始终完整加载。 + // 0 表示无限制。 + AllAgentsMDMaxBytes int + + // OnLoadWarning 是一个可选的回调函数,在加载过程中发生非致命错误时调用 + // (如文件未找到、循环 @import、深度超限等)。 + // 如果为 nil,警告通过 log.Printf 输出。 + // + // 注意:Backend.Read 的非 os.ErrNotExist 错误(如权限被拒、I/O 错误) + // 不会被视为警告,而是会中止加载过程。 + OnLoadWarning func(filePath string, err error) +} +``` + +### 配置参数说明 + + + + + + + +
参数类型必填默认值说明
Backend
Backend
-文件读取后端,负责实际的文件 I/O
AgentsMDFiles
[]string
-要加载的 Agents.md 文件路径列表(至少一个)
AllAgentsMDMaxBytes
int
0
(无限制)
所有文件的总字节数上限
OnLoadWarning
func(string, error)
log.Printf
非致命错误的回调函数
+ +--- + +## Backend 接口 + +### 接口定义 + +```go +type Backend interface { + // Read 读取文件内容。 + // 如果文件不存在,实现应返回包装了 os.ErrNotExist 的 error + // (以便 errors.Is(err, os.ErrNotExist) 返回 true)。 + // 这样 loader 可以静默跳过缺失文件并通过 OnLoadWarning 通知。 + // 其他错误(如权限被拒、I/O 错误)会中止加载过程。 + Read(ctx context.Context, req *ReadRequest) (*FileContent, error) +} +``` + +### 类型定义 + +```go +// ReadRequest 定义读取文件的请求参数 +type ReadRequest struct { + FilePath string // 文件路径 + Offset int // 起始行号(1-based) +} + +// FileContent 定义文件内容的返回结构 +type FileContent struct { + Content string // 文件的文本内容 +} +``` + +--- + +## @import 语法 + +Agents.md 文件支持 `@import` 语法,可以递归引入其他文件。 + +### 语法格式 + +在 Agents.md 文件中,使用 `@路径/文件名` 引用其他文件: + +```markdown +# 项目指令 + +你是一个代码助手。 + +请参考以下规范: +@rules/code-style.md +@rules/api-conventions.md +``` + +### 规则 + +1. **路径解析**:相对路径基于当前文件所在目录解析,绝对路径直接使用 +2. **最大递归深度**:5 层(超过后跳过并触发 `OnLoadWarning`) +3. **循环引用检测**:自动检测并跳过循环引用(触发 `OnLoadWarning`) +4. **全局去重**:同一文件不会被重复加载 +5. **支持的文件扩展名**(路径中不含 `/` 时):`.md`, `.txt`, `.mdx`, `.yaml`, `.yml`, `.json`, `.toml` +6. **误报过滤**:不含 `/` 且扩展名不在允许列表中的 `@引用` 会被忽略(避免将 `@someone` 或 `@example.com` 识别为导入) + +### @import 目录结构示例 + +``` +project/ +├── Agents.md # 主入口文件 +├── rules/ +│ ├── code-style.md # 代码风格规范 +│ ├── api-conventions.md # API 规范 +│ └── testing.md # 测试规范 +└── context/ + └── architecture.md # 架构说明 +``` + +--- + +## 工作原理 + +### 注入流程 + +``` +用户消息 + 历史消息 + │ + ▼ +┌─────────────────────┐ +│ agentsmd 中间件 │ +│ (WrapModel) │ +│ │ +│ 1. 加载 Agents.md │ +│ 2. 缓存到 RunLocal │ +│ 3. 生成注入消息 │ +└─────────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ 注入后的消息序列 │ +│ │ +│ [System] 系统提示词 │ +│ [User] ← Agents.md 内容注入 │ ← 插入在第一条 User 消息之前 +│ [User] 用户历史消息 1 │ +│ [Assistant] 助手回复 1 │ +│ [User] 用户当前消息 │ +└─────────────────────────────────────┘ + │ + ▼ + 模型调用 (Generate / Stream) +``` + +### 关键机制 + +1. **瞬态注入**:Agents.md 内容仅在模型调用时临时插入,不写入 `ChatModelAgentState`,因此不会被摘要/压缩中间件处理 +2. **Run 级别缓存**:同一次 Agent `Run()` 中,Agents.md 内容加载后会缓存在 `RunLocalValue` 中,后续的模型调用(如多轮工具调用)直接复用缓存,避免重复读取 +3. **插入位置**:内容作为 `User` 角色消息插入在第一条 User 消息之前;如果没有 User 消息,则追加到末尾 +4. **国际化**:格式化输出自动适配中英文(根据系统语言环境) + +--- + +## 注意事项 + +### 中间件顺序 + +**推荐将 ****agentsmd**** 中间件放在 summarization/compression 中间件之后。** 这样可以确保 Agents.md 内容: + +- 不会被摘要中间件压缩掉 +- 每次模型调用都能获得完整的指令内容 + +```go +Middlewares: []adk.ChatModelAgentMiddleware{ + summarizationMiddleware, // 先摘要 + agentsMDMiddleware, // 后注入 Agents.md +} +``` + +### 错误处理 + + + + + + + + + +
场景行为
文件不存在 (
os.ErrNotExist
)
跳过该文件,触发
OnLoadWarning
循环
@import
跳过循环文件,触发
OnLoadWarning
@import
深度超过 5 层
跳过,触发
OnLoadWarning
累计大小超过
AllAgentsMDMaxBytes
跳过后续文件,触发
OnLoadWarning
(第一个文件始终完整加载)
权限被拒 / I/O 错误中止加载,返回 error
所有文件内容为空不注入,原样传递输入消息
+ +### Backend 实现要求 + +- 文件不存在时**必须**返回 `os.ErrNotExist` 包裹的错误(`fmt.Errorf("... : %w", os.ErrNotExist)`),否则 loader 无法区分"文件缺失"和"真正的 I/O 错误" +- `Read` 方法应当是并发安全的 + +### 性能考虑 + +- 合理设置 `AllAgentsMDMaxBytes`,避免注入过多内容占用模型上下文窗口 +- Agents.md 内容在每次 `Run()` 中只加载一次(Run 级别缓存),但**每次新的 ****Run()**** 都会重新加载**,因此文件内容的修改会在下次 Run 时生效 +- 避免在 Agents.md 中 `@import` 过多文件,递归深度上限为 5 层 + +### Agents.md 编写建议 + +- 保持内容精炼,只包含对模型行为真正有影响的指令 +- 使用 `@import` 拆分关注点(代码规范、API 规范、架构说明等) +- 避免在 Agents.md 中包含大量代码示例或数据,以免浪费上下文窗口 +- 文件内容会被包裹在 `` 标签中传递给模型,模型会将其视为系统级指令 + +--- + +## FAQ + +**Q: Agents.md 的内容会被保存到对话历史中吗?** +A: 不会。内容是在模型调用时动态注入的,不会写入 `ChatModelAgentState`,因此对话历史中不会出现 Agents.md 的内容。 + +**Q: 如果某个 Agents.md 文件不存在会怎样?** +A: 该文件会被跳过,触发 `OnLoadWarning` 回调(默认 `log.Printf`),不会导致整体加载失败。 + +**Q: @import 的路径是相对于什么目录?** +A: 相对于当前文件所在目录。例如 `/project/Agents.md` 中的 `@rules/style.md` 会解析为 `/project/rules/style.md`。 + +**Q: 多个文件中 @import 了同一个文件会重复加载吗?** +A: 不会。loader 维护了全局去重 map,同一个文件路径只会被读取和注入一次。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md new file mode 100644 index 00000000000..e96a2709d8c --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md @@ -0,0 +1,187 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: FileSystem +weight: 2 +--- + +> 💡 Package: [github.com/cloudwego/eino/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) + +## 概述 + +FileSystem Middleware 为 Agent 提供文件系统访问能力。它通过 [FileSystem Backend](/zh/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/filesystem_backend) 接口操作文件系统,自动向 Agent 注入一组文件操作工具及对应的 system prompt,使 Agent 能够直接进行文件读写、搜索、编辑等操作。 + +核心功能: + +- **文件系统工具注入** — 自动注册 ls、read_file、write_file、edit_file、glob、grep 等工具 +- **Shell 命令执行** — 可选注入 execute 工具,支持同步和流式命令执行 +- **工具级别配置** — 每个工具均可独立配置名称、描述、自定义实现或禁用 +- **多语言提示词** — 工具描述和 system prompt 支持中英文切换 + +## 创建中间件 + +推荐使用 `New` 函数创建中间件(返回 `ChatModelAgentMiddleware`): + +```go +import "github.com/cloudwego/eino/adk/middlewares/filesystem" + +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + // 如果需要 shell 命令执行能力,设置 Shell 或 StreamingShell + Shell: myShell, +}) +if err != nil { + // handle error +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +> 💡 +> `New` 返回 `ChatModelAgentMiddleware`,提供更好的上下文传播能力(通过 `BeforeAgent` hook 在运行时修改 Agent 的 instruction 和 tools)。 + +## MiddlewareConfig 配置项 + +```go +type MiddlewareConfig struct { + // Backend 提供文件系统操作 + // 必填 + Backend filesystem.Backend + + // Shell 提供 shell 命令执行能力 + // 如果设置,会注册 execute 工具 + // 可选,与 StreamingShell 互斥 + Shell filesystem.Shell + + // StreamingShell 提供流式 shell 命令执行能力 + // 如果设置,会注册流式 execute 工具(支持实时输出) + // 可选,与 Shell 互斥 + StreamingShell filesystem.StreamingShell + + // 以下为各工具的独立配置,均为可选 + LsToolConfig *ToolConfig // ls 工具配置 + ReadFileToolConfig *ToolConfig // read_file 工具配置 + WriteFileToolConfig *ToolConfig // write_file 工具配置 + EditFileToolConfig *ToolConfig // edit_file 工具配置 + GlobToolConfig *ToolConfig // glob 工具配置 + GrepToolConfig *ToolConfig // grep 工具配置 + + // CustomSystemPrompt 覆盖默认的系统提示词 + // 可选,默认 ToolsSystemPrompt + CustomSystemPrompt *string + + // 以下字段已 Deprecated,请使用对应的 *ToolConfig.Desc 替代 + // CustomLsToolDesc, CustomReadFileToolDesc, CustomGrepToolDesc, + // CustomGlobToolDesc, CustomWriteFileToolDesc, CustomEditToolDesc +} +``` + +### ToolConfig + +每个工具均可通过 `ToolConfig` 独立配置: + +```go +type ToolConfig struct { + // Name 覆盖工具名称 + // 可选,不设置则使用默认名称(如 "ls"、"read_file" 等) + Name string + + // Desc 覆盖工具描述 + // 可选,不设置则使用默认描述 + Desc *string + + // CustomTool 提供自定义工具实现 + // 如果设置,将使用此自定义实现替代基于 Backend 的默认实现 + // 可选 + CustomTool tool.BaseTool + + // Disable 禁用此工具 + // 如果为 true,该工具将不会被注册 + // 可选,默认 false + Disable bool +} +``` + +示例 — 自定义工具名称并禁用写入: + +```go +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + ReadFileToolConfig: &filesystem.ToolConfig{ + Name: "cat_file", // 自定义名称 + }, + WriteFileToolConfig: &filesystem.ToolConfig{ + Disable: true, // 禁用写入工具 + }, +}) +``` + +## 注入的工具 + + + + + + + + + + +
工具默认名称描述条件
列出目录
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
+ +每个工具均可通过对应的 `*ToolConfig` 禁用(`Disable: true`)或提供自定义实现(`CustomTool`)。 + +## 多语言支持 + +工具描述和内置提示词默认为英文。如需切换为中文,可通过 `adk.SetLanguage()` 设置: + +```go +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // 切换为中文 +adk.SetLanguage(adk.LanguageEnglish) // 切换为英文(默认) +``` + +也可以通过 `ToolConfig.Desc` 或 `CustomSystemPrompt` 自定义各工具的说明文本。 + +## [deprecated] 工具结果卸载 + +> 💡 +> 该功能即将在 0.8.0 中 deprecate。请迁移到 Middleware: ToolReduction + +> 注意:工具结果卸载仅在旧的 `Config` + `NewMiddleware` 函数中可用。推荐的 `MiddlewareConfig` + `New` 不包含此功能,如需要请配合 ToolReduction middleware 使用。 + +当工具调用结果过大(例如读取大文件、grep 命中大量内容),如果继续将完整结果放入对话上下文,会导致: + +- token 急剧增加 +- Agent 历史上下文污染 +- 推理效率变差 + +为此,旧版 Middleware(`NewMiddleware`)提供了自动卸载机制: + +- 当结果大小超过阈值(默认 20,000 tokens)时,不直接返回全部内容给 LLM +- 实际结果会保存到文件系统(Backend) +- 上下文中仅包含摘要和文件路径(Agent 可再次调用 `read_file` 工具按需读取) + +该功能默认启用,可通过 `Config`(非 `MiddlewareConfig`)配置: + +```go +type Config struct { + // ... Backend, Shell, StreamingShell, ToolConfig 等字段同 MiddlewareConfig + + // 关闭自动卸载 + WithoutLargeToolResultOffloading bool + + // 自定义触发阈值(默认 20000 tokens) + LargeToolResultOffloadingTokenLimit int + + // 自定义卸载文件生成路径 + // 默认路径格式: /large_tool_result/{ToolCallID} + LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) +} +``` diff --git a/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md b/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md index 90b1d677e6c..2e1ff75e8e9 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md +++ b/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-12" +date: "2026-03-24" lastmod: "" tags: [] title: Agent Callback diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md index 381c85fb092..6c4a682268c 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md +++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] title: ChatModelAgent diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md index 68d2d13d9eb..3fcfb76163f 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md +++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-02" +date: "2026-03-24" lastmod: "" tags: [] title: DeepAgents diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md b/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md index c3c846c2328..d32061c34a9 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md +++ b/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md @@ -145,7 +145,7 @@ AgentRunner 是 Agent 的执行器,为 Agent 运行所需要的拓展功能加 只有通过 Runner 执行 agent 时,才可以使用 ADK 的如下功能: - Interrupt & Resume -- 切面机制(测试版本支持,正式发布前不保证 API 兼容,详见 Agent 扩展章节) +- 切面机制 - Context 环境的预处理 ```go diff --git a/content/zh/docs/eino/ecosystem_integration/_index.md b/content/zh/docs/eino/ecosystem_integration/_index.md index 1dcb09ae790..dd106a516b6 100644 --- a/content/zh/docs/eino/ecosystem_integration/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/_index.md @@ -11,9 +11,9 @@ weight: 6 ### ChatModel -- openai: [OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_openai) -- ark: [ARK](/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_ark) -- 更多组件请见:[ChatModel 组件列表](/zh/docs/eino/ecosystem_integration/chat_model) +- openai: [ChatModel - OpenAI](https://github.com/cloudwego/eino-ext/blob/main/components/model/openai/README.md) +- ark: [ChatModel - ARK](https://github.com/cloudwego/eino-ext/blob/main/components/model/ark/README.md) +- ollama: [ChatModel - Ollama](https://github.com/cloudwego/eino-ext/blob/main/components/model/ollama/README.md) ### Document diff --git a/content/zh/docs/eino/overview/eino_adk0_1.md b/content/zh/docs/eino/overview/eino_adk0_1.md index 95c7bf539a5..af28e17b529 100644 --- a/content/zh/docs/eino/overview/eino_adk0_1.md +++ b/content/zh/docs/eino/overview/eino_adk0_1.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-24" lastmod: "" tags: [] title: Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统 diff --git a/content/zh/docs/eino/overview/graph_or_agent.md b/content/zh/docs/eino/overview/graph_or_agent.md index 7eebda5d8e8..96d9c21c323 100644 --- a/content/zh/docs/eino/overview/graph_or_agent.md +++ b/content/zh/docs/eino/overview/graph_or_agent.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-02" +date: "2026-03-24" lastmod: "" tags: [] title: Agent 还是 Graph?AI 应用路线辨析 diff --git a/content/zh/docs/eino/quick_start/chapter_01_chatmodel_and_message.md b/content/zh/docs/eino/quick_start/chapter_01_chatmodel_and_message.md index e961e52b1be..4dcb14ee4eb 100644 --- a/content/zh/docs/eino/quick_start/chapter_01_chatmodel_and_message.md +++ b/content/zh/docs/eino/quick_start/chapter_01_chatmodel_and_message.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-12" +date: "2026-03-24" lastmod: "" tags: [] title: 第一章:ChatModel 与 Message(Console) diff --git a/content/zh/docs/eino/quick_start/chapter_09_a2ui_protocol.md b/content/zh/docs/eino/quick_start/chapter_09_a2ui_protocol.md index 4aa55ea6ab2..b6ff586670f 100644 --- a/content/zh/docs/eino/quick_start/chapter_09_a2ui_protocol.md +++ b/content/zh/docs/eino/quick_start/chapter_09_a2ui_protocol.md @@ -3,7 +3,7 @@ Description: "" date: "2026-03-16" lastmod: "" tags: [] -title: 第九章:A2UI 协议(流式 UI 组件) +title: 第十章:A2UI 协议(流式 UI 组件) weight: 10 --- diff --git a/content/zh/docs/eino/quick_start/chapter_09_skill_console.md b/content/zh/docs/eino/quick_start/chapter_09_skill_console.md index 00f92c5f447..e66120d0562 100644 --- a/content/zh/docs/eino/quick_start/chapter_09_skill_console.md +++ b/content/zh/docs/eino/quick_start/chapter_09_skill_console.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] title: 第九章:Skill(Console) @@ -91,7 +91,7 @@ Enter your message (empty line to exit): 2. 用 `skill.NewBackendFromFilesystem` 把 `EINO_EXT_SKILLS_DIR` 变成一个 Skill Backend 3. 用 `skill.NewMiddleware` 生成中间件,并把它塞进 DeepAgent 的 `Handlers` -**关键代码片段(注意:这是简化后的代码片段,不能直接运行,完整代码请参考 cmd/ch09/main.go):** +**关键代码片段(注意:这是简化后的代码片段,不能直接运行,完整代码请参考 ****cmd/ch09/main.go****):** ```go backend, _ := localbk.NewBackend(ctx, &localbk.Config{}) diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md index b14a047b9cd..5224228048c 100644 --- a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-03-09" +date: "2026-03-24" lastmod: "" tags: [] title: v0.8.*-adk middlewares @@ -65,7 +65,7 @@ agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ > 💡 > **功能**: 自动对话历史摘要,防止超出模型上下文窗口限制 -📚 **详细文档**: [Middleware: Summarization](/zh/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_summarization) +📚 **详细文档**: [Middleware: FileSystem](/zh/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_filesystem) 当对话历史的 Token 数量超过阈值时,自动调用 LLM 生成摘要,压缩上下文。 diff --git a/static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png b/static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png deleted file mode 100644 index 21d758852a4a2ead56eb99f7b56e9e7936edff1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162840 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 z 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 z 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�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 From a2d9c907325ed2ad63fdf46b84d6572379ac0399 Mon Sep 17 00:00:00 2001 From: "shentong.martin" Date: Tue, 24 Mar 2026 18:58:36 +0800 Subject: [PATCH 2/3] docs(eino): sync english translations --- .../Middleware_FileSystem/_index.md | 237 --------------- .../Middleware_Skill.md | 264 +++++++--------- .../filesystem_backend/_index.md | 175 +++++++++++ .../backend_ark_agentkit_sandbox.md} | 13 +- ...07\344\273\266\347\263\273\347\273\237.md" | 17 +- .../middleware_agentsmd.md | 281 ++++++++++++++++++ .../middleware_filesystem.md | 187 ++++++++++++ .../eino_adk/adk_agent_callback.md | 202 +++++++------ .../agent_implementation/chat_model.md | 2 + .../agent_implementation/deepagents.md | 2 +- .../quick_start/chapter_09_a2ui_protocol.md | 240 ++++++++++++++- .../quick_start/chapter_09_skill_console.md | 134 ++++++++- .../Eino_v0.8._-adk_middlewares/_index.md | 16 +- 13 files changed, 1235 insertions(+), 535 deletions(-) delete mode 100644 content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md create mode 100644 content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md rename content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/{Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md => filesystem_backend/backend_ark_agentkit_sandbox.md} (92%) rename content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md => "content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" (90%) create mode 100644 content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md create mode 100644 content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md deleted file mode 100644 index 7b2441160d3..00000000000 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -Description: "" -date: "2026-03-12" -lastmod: "" -tags: [] -title: 'Middleware: FileSystem' -weight: 1 ---- - -> 💡 -> Package: [https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) - -# Overview - -FileSystem Middleware provides Agents with file system access and large tool result offloading capabilities. -It defines a unified file system Backend interface, allowing users to use the default in-memory implementation directly or extend custom backends based on their needs. - -Core features include: - -- Access to virtual file system (ls/read/write/edit/glob/grep) -- Automatically offload oversized tool results to the file system, keeping summaries in the Agent context with content loaded on demand - -# Backend Interface - -FileSystem Middleware operates on the file system through the Backend interface in `github.com/cloudwego/eino/adk/filesystem`: - -```go -type Backend interface { - // List files at the specified path (structured information) - LsInfo(path string) ([]FileInfo, error) - // Read file content by offset and limit (returns LLM-friendly text) - Read(ctx context.Context, filePath string, offset, limit int) (string, error) - // Grep pattern in the specified path, returns list of matches - GrepRaw(ctx context.Context, pattern string, path, glob *string) ([]GrepMatch, error) - // Glob match based on pattern and path - GlobInfo(ctx context.Context, pattern, path string) ([]FileInfo, error) - // Write or update a file - Write(ctx context.Context, filePath, content string) error - // Replace string in a file - Edit(ctx context.Context, filePath, oldString, newString string, replaceAll bool) error -} -``` - -### **Extended Interfaces** - -```go -type Shell interface { - Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) -} - -type StreamingShell interface { - ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) -} -``` - -`InMemoryBackend` is an in-memory implementation of the `Backend` interface, storing files in a map with concurrent-safe access. - -```go -import "github.com/cloudwego/eino/adk/filesystem" - -ctx := context.Background() -backend := filesystem.NewInMemoryBackend() - -// Write a file -err := backend.Write(ctx, &filesystem.WriteRequest{ - FilePath: "/example/test.txt", - Content: "Hello, World!\nLine 2\nLine 3", -}) - -// Read a file -content, err := backend.Read(ctx, &filesystem.ReadRequest{ - FilePath: "/example/test.txt", - Offset: 1, - Limit: 10, -}) - -// List directory -files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ - Path: "/example", -}) - -// Search content -matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ - Pattern: "Hello", - Path: "/example", -}) - -// Edit a file -err = backend.Edit(ctx, &filesystem.EditRequest{ - FilePath: "/example/test.txt", - OldString: "Hello", - NewString: "Hi", - ReplaceAll: false, -}) -``` - -Other Backend implementations: - -# Filesystem Middleware - -The Middleware automatically injects a set of tools and corresponding system prompts into the Agent, enabling it to directly operate on the file system. - -### **Creating the Middleware** - -It is recommended to use the `New` function to create the middleware (returns `ChatModelAgentMiddleware`): - -```go -import "github.com/cloudwego/eino/adk/middlewares/filesystem" - -middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ - Backend: myBackend, - // Set Shell or StreamingShell if shell command execution capability is needed - Shell: myShell, -}) -if err != nil { - // handle error -} - -agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ - // ... - Middlewares: []adk.ChatModelAgentMiddleware{middleware}, -}) -``` - -### **Config Options** - -```go -type MiddlewareConfig struct { - // Backend provides file system operations, required - Backend filesystem.Backend - - // Shell provides shell command execution capability - // If set, the execute tool will be registered - // Optional, mutually exclusive with StreamingShell - Shell filesystem.Shell - - // StreamingShell provides streaming shell command execution capability - // If set, the streaming execute tool will be registered - // Optional, mutually exclusive with Shell - StreamingShell filesystem.StreamingShell - - // CustomSystemPrompt overrides the default system prompt - // Optional, defaults to ToolsSystemPrompt - CustomSystemPrompt *string - - // Custom names for each tool, all optional - CustomLsToolName *string // default "ls" - CustomReadFileToolName *string // default "read_file" - CustomWriteFileToolName *string // default "write_file" - CustomEditFileToolName *string // default "edit_file" - CustomGlobToolName *string // default "glob" - CustomGrepToolName *string // default "grep" - CustomExecuteToolName *string // default "execute" - - // Custom descriptions for each tool, all optional - CustomLsToolDesc *string - CustomReadFileToolDesc *string - CustomGrepToolDesc *string - CustomGlobToolDesc *string - CustomWriteFileToolDesc *string - CustomEditToolDesc *string - CustomExecuteToolDesc *string -} -``` - -> 💡 -> The `New` function returns `ChatModelAgentMiddleware`, providing better context propagation capabilities. For scenarios requiring large tool result offloading, please use the `NewMiddleware` function or use it together with the ToolReduction middleware. - -Injected tools: - -These tools all come with default English descriptions and built-in system prompts. To switch to Chinese, you can set it via `adk.SetLanguage()`: - -``` -import "github.com/cloudwego/eino/adk" - -adk.SetLanguage(adk.LanguageChinese) // Switch to Chinese -adk.SetLanguage(adk.LanguageEnglish) // Switch to English (default) -``` - -You can also customize the description text and tool names through `Config`: - -```go -type MiddlewareConfig struct { - // Override default System Prompt (optional) - CustomSystemPrompt *string - - // Override tool names (optional) - CustomLsToolName *string - CustomReadFileToolName *string - // ... - - // Override tool descriptions (optional) - CustomLsToolDesc *string - CustomReadFileToolDesc *string - // ... -} -``` - -# [deprecated] Tool Result Offloading - -> 💡 -> This feature will be deprecated in 0.8.0. Migrate to [Middleware: ToolReduction](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction) - -When tool call results are too large (e.g., reading large files, grep hitting a lot of content), continuing to put the complete results in the conversation context will cause: - -- Token count increases dramatically -- Agent history context pollution -- Inference efficiency deterioration - -To address this, the Middleware provides an automatic offloading mechanism: - -- When the result size exceeds the threshold (default 20,000 tokens) - → The complete content is not returned directly to the LLM -- The actual result is saved to the file system -- The context only includes: - - Summary - - File path (agent can call the tool again to read it) - - - -This feature is enabled by default and behavior can be adjusted through configuration: - -```go -type Config struct { - // other config... - - // Disable automatic offloading - WithoutLargeToolResultOffloading bool - - // Custom trigger threshold (default 20000 tokens) - LargeToolResultOffloadingTokenLimit int - - // Custom offload file path generation - // Default path format: /large_tool_result/{ToolCallID} - LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) -} -``` diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md index c83bf6d7485..f9afbf5c065 100644 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md @@ -1,40 +1,40 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] -title: 'Middleware: Skill' -weight: 2 +title: Skill +weight: 3 --- -Skill Middleware provides Skill support for Eino ADK Agents, enabling Agents to dynamically discover and use predefined skills to complete tasks more accurately and efficiently. +Skill middleware adds Skill support to Eino ADK agents, enabling agents to dynamically discover and use predefined skills to complete tasks more accurately and efficiently. # What is a Skill -A Skill is a folder containing instructions, scripts, and resources that Agents can discover and use on demand to extend their capabilities. The core of a Skill is a `SKILL.md` file containing metadata (at least name and description) and instructions guiding the Agent to perform specific tasks. +A Skill is a folder that contains instructions, scripts, and resources. Agents can discover and use these skills on demand to extend their capabilities. The core of a Skill is a `SKILL.md` file, which includes metadata (at least `name` and `description`) and guidance for the agent to execute a specific type of task. ``` my-skill/ -├── SKILL.md # Required: Instructions + metadata -├── scripts/ # Optional: Executable code -├── references/ # Optional: Reference documentation -└── assets/ # Optional: Templates, resources +├── SKILL.md # Required: instructions + metadata +├── scripts/ # Optional: executable code +├── references/ # Optional: reference docs +└── assets/ # Optional: templates/resources ``` -Skills use **Progressive Disclosure** to efficiently manage context: +Skills use **Progressive Disclosure** to manage context efficiently: -1. **Discovery**: At startup, the Agent only loads the name and description of each available Skill, enough to determine when the Skill might be needed -2. **Activation**: When a task matches a Skill's description, the Agent reads the complete `SKILL.md` content into context -3. **Execution**: The Agent follows instructions to execute the task, and can also load other files or execute bundled code as needed. This approach keeps the Agent responsive while allowing on-demand access to more context. +1. **Discovery**: on startup, the agent only loads each skill’s name and description — enough to decide when the skill might be useful +2. **Activation**: when a task matches a skill’s description, the agent loads the full `SKILL.md` content into context +3. **Execution**: the agent follows the instructions and can load other files or execute bundled code as needed. This keeps the agent responsive while still allowing on-demand access to additional context. > 💡 > Ref: [https://agentskills.io/home](https://agentskills.io/home) -# Interface Introduction +# Interfaces ## FrontMatter -The metadata structure of a Skill, used for quick display of Skill information during discovery phase, avoiding loading full content: +Skill metadata used for quick display during discovery, avoiding loading full content: ```go type FrontMatter struct { @@ -48,32 +48,32 @@ type FrontMatter struct { - - - - - + + + + +
FieldTypeDescription
Name
string
Unique identifier for the Skill. Agent calls the Skill by this name. Recommend using short, meaningful names (e.g.,
pdf-processing
,
web-research
). Corresponds to the
name
field in SKILL.md frontmatter
Description
string
Description of Skill functionality. This is the key basis for Agent to decide whether to use the Skill, should clearly explain applicable scenarios and capabilities. Corresponds to the
description
field in SKILL.md frontmatter
Context
ContextMode
Context mode. Possible values:
fork
(copy history messages to create new Agent for execution),
isolate
(create new Agent with isolated context for execution). Leave empty for inline mode (directly return Skill content)
Agent
string
Specify the Agent name to use. Used with
Context
field, gets the corresponding Agent factory function via
AgentHub
. Leave empty to use default Agent
Model
string
Specify the model name to use. Gets the corresponding model instance via
ModelHub
. In Context mode, passed to Agent factory; in inline mode, switches the model used by subsequent ChatModel calls
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
name
field 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
description
field 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.
### ContextMode ```go const ( - ContextModeFork ContextMode = "fork" // Copy history messages - ContextModeIsolate ContextMode = "isolate" // Isolate context + ContextModeFork ContextMode = "fork" // Isolated context + ContextModeForkWithContext ContextMode = "fork_with_context" // Copy history messages ) ``` - - - + + +
ModeDescription
Inline (default)Skill content is returned directly as tool result, processed by current Agent
ForkCreate new Agent, copy current conversation history, execute Skill task independently and return result
IsolateCreate new Agent with isolated context (only containing Skill content), execute independently and return result
Inline (default)Skill content is returned as the tool result and the current agent continues processing
ForkWithContextCreate a new agent, copy current conversation history, execute the skill independently, and return the result
ForkCreate a new agent with isolated context (only skill content), execute independently, and return the result
## Skill -Complete Skill structure, containing metadata and actual instruction content: +Complete skill structure (metadata + instruction content): ```go type Skill struct { @@ -85,18 +85,18 @@ type Skill struct { - - - + + +
FieldTypeDescription
FrontMatter
FrontMatter
Embedded metadata structure, including
Name
,
Description
,
Context
,
Agent
,
Model
Content
string
Body content after frontmatter in SKILL.md file. Contains detailed instructions, workflows, examples, etc. Agent reads this content after activating Skill
BaseDirectory
string
Absolute path of Skill directory. Agent can use this path to access other resource files in the Skill directory (such as scripts, templates, reference docs, etc.)
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.).
## Backend -Skill backend interface, defining how skills are retrieved. The Backend interface decouples skill storage from usage, providing the following benefits: +Skill backend interface defines how skills are retrieved. It decouples skill storage from usage: -- **Flexible storage**: Skills can be stored in local file system, database, remote service, cloud storage, etc. -- **Extensibility**: Teams can implement custom Backends as needed, such as dynamically loading from Git repository, getting from config center, etc. -- **Test friendly**: Can easily create Mock Backend for unit testing +- **Flexible storage**: store skills in local filesystem, databases, remote services, cloud storage, etc. +- **Extensible**: implement custom backends (e.g. load from Git repos, config centers) +- **Test-friendly**: easy to build mock backends for unit tests ```go type Backend interface { @@ -107,32 +107,13 @@ type Backend interface { - - + +
MethodDescription
List
List metadata of all available skills. Called at Agent startup to build skill tool descriptions, letting Agent know which skills are available
Get
Get complete skill content by name. Called when Agent decides to use a skill, returns complete Skill structure with detailed instructions
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.
-## AgentHub and ModelHub - -When Skills use Context mode (fork/isolate), AgentHub and ModelHub need to be configured: - -```go -// AgentFactory creates Agent instances -type AgentFactory func(ctx context.Context, m model.ToolCallingChatModel) (adk.Agent, error) - -// AgentHub provides Agent factory functions -type AgentHub interface { - Get(ctx context.Context, name string) (AgentFactory, error) -} +### NewBackendFromFilesystem -// ModelHub provides model instances -type ModelHub interface { - Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) -} -``` - -### **NewBackendFromFilesystem** - -Backend implementation based on `filesystem.Backend` interface, reads skills from specified directory: +A filesystem-backed backend implementation that reads skills from a directory via `filesystem.Backend`: ```go type BackendFromFilesystemConfig struct { @@ -145,147 +126,96 @@ func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystem - - + +
FieldTypeRequiredDescription
Backend
filesystem.Backend
YesFile system backend implementation for file operations
BaseDir
string
YesPath to skills root directory. Scans all first-level subdirectories under this directory, looking for directories containing
SKILL.md
files as skills
Backend
filesystem.Backend
YesFilesystem backend implementation used for file operations
BaseDir
string
YesRoot directory for skills. It scans all first-level subdirectories and treats the ones containing
SKILL.md
as skills.
How it works: -- Scans first-level subdirectories under `BaseDir` -- Looks for `SKILL.md` file in each subdirectory -- Parses YAML frontmatter to get metadata -- Deeply nested `SKILL.md` files are ignored +- scan first-level subdirectories under `BaseDir` +- look for `SKILL.md` in each subdirectory +- parse YAML frontmatter to get metadata +- deeply nested `SKILL.md` files are ignored -### **filesystem.Backend Implementations** +### filesystem.Backend Implementations -The `filesystem.Backend` interface has the following two implementations to choose from: +There are two `filesystem.Backend` implementations to choose from. See [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_filesystem). -#### **Local Backend (Local File System)** +## AgentHub and ModelHub -Implementation based on local file system, suitable for Unix/MacOS environments: +When Skills use context mode (fork/isolate), you need to configure AgentHub and ModelHub: ```go -import "github.com/cloudwego/eino-ext/adk/backend/local" - -type Config struct { - ValidateCommand func(string) error // optional +// AgentHubOptions contains options passed to AgentHub.Get when creating an agent for skill execution. +type AgentHubOptions struct { + // Model is the resolved model instance when a skill specifies a "model" field in frontmatter. + // nil means the skill did not specify a model override; implementations should use their default. + Model model.ToolCallingChatModel } -func NewBackend(ctx context.Context, cfg *Config) (filesystem.Backend, error) -``` - - - - -
FieldTypeRequiredDescription
ValidateCommand
func(string) error
NoCommand validation function for security checks before command execution
- -> **Note**: Only supports Unix/MacOS, does not support Windows. - -#### **Sandbox Backend (Sandbox Environment)** - -Implementation based on Volcengine AgentKit sandbox tool, suitable for scenarios requiring isolated execution environments: - -```go -import "github.com/cloudwego/eino-ext/adk/backend/agentkit" - -type Config struct { - AccessKeyID string - SecretAccessKey string - Region Region // Optional, default cn-beijing - ToolID string - SessionID string // Provide at least one of SessionID or UserSessionID - UserSessionID string // Provide at least one of SessionID or UserSessionID - SessionTTL int // Optional, default 1800 seconds - ExecutionTimeout int // Optional - HTTPClient *http.Client // Optional +// AgentHub provides agent instances for context mode (fork/fork_with_context) execution. +type AgentHub interface { + // Get returns an Agent by name. When name is empty, implementations should return a default agent. + // The opts parameter carries skill-level overrides (e.g., model) resolved by the framework. + Get(ctx context.Context, name string, opts *AgentHubOptions) (adk.Agent, error) } -func NewSandboxToolBackend(config *Config) (filesystem.Backend, error) -``` - - - - - - - - - - - -
FieldTypeRequiredDescription
AccessKeyID
string
YesVolcengine Access Key ID
SecretAccessKey
string
YesVolcengine Secret Access Key
Region
Region
NoRegion, supports
cn-beijing
(default) and
cn-shanghai
ToolID
string
YesSandbox Tool ID
SessionID
string
NoSession ID, provide at least one of SessionID or UserSessionID
UserSessionID
string
NoUser Session ID, provide at least one of SessionID or UserSessionID
SessionTTL
int
NoSession TTL (seconds), range 60-86400, default 1800
ExecutionTimeout
int
NoCode execution timeout (seconds)
- -> For more information see: [Volcengine AgentKit Documentation](https://www.volcengine.com/docs/86681/1847934) - -**LocalBackend** Built-in local file system backend implementation, reads skills from specified directory: - -```go -type LocalBackendConfig struct { - BaseDir string +// ModelHub provides model instances. +type ModelHub interface { + Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) } - -func NewLocalBackend(config *LocalBackendConfig) (*LocalBackend, error) ``` - - - -
FieldTypeRequiredDescription
BaseDir
string
YesPath to skills root directory. LocalBackend scans all subdirectories under this directory, looking for directories containing
SKILL.md
files as skills
- -How it works: - -- Scans first-level subdirectories under `BaseDir` -- Looks for `SKILL.md` file in each subdirectory -- Parses YAML frontmatter to get metadata +## ## Initialization -Create Skill Middleware (recommend using `NewChatModelAgentMiddleware`): +Create the Skill middleware (recommended: `NewMiddleware`): ```go -func NewChatModelAgentMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error) +func NewMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error) ``` -Config is configured as: +Config: ```go type Config struct { - // Backend skill backend implementation, required + // Backend is required Backend Backend - // SkillToolName skill tool name, default "skill" + // SkillToolName defaults to "skill" SkillToolName *string - // AgentHub provides Agent factory functions for Context mode - // Required when Skill uses "context: fork" or "context: isolate" + // AgentHub provides agent factories for context mode + // Required when skill uses "context: fork" or "context: isolate" AgentHub AgentHub - // ModelHub provides model instances for Skills specifying models + // ModelHub provides model instances for skill-specified models ModelHub ModelHub - // CustomSystemPrompt custom system prompt + // CustomSystemPrompt customizes system prompt CustomSystemPrompt SystemPromptFunc - // CustomToolDescription custom tool description + // CustomToolDescription customizes tool description CustomToolDescription ToolDescriptionFunc } ``` - - - - + + + +
FieldTypeRequiredDefaultDescription
Backend
Backend
Yes
  • Skill backend implementation. Responsible for skill storage and retrieval, can use built-in
    LocalBackend
    or custom implementation
    SkillToolName
    *string
    No
    "skill"
    Skill tool name. Agent calls skill tool by this name. If your Agent already has a tool with the same name, you can customize the name through this field to avoid conflicts
    AgentHub
    AgentHub
    No
  • Provides Agent factory functions. Required when Skill uses
    context: fork
    or
    context: isolate
    ModelHub
    ModelHub
    No
  • Provides model instances. Used when Skill specifies the
    model
    field
    Backend
    Backend
    Yes
  • Skill backend implementation responsible for storage and retrieval. You can use the built-in
    LocalBackend
    or 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: fork
    or
    context: isolate
    .
    ModelHub
    ModelHub
    No
  • Provides model instances. Used when a skill specifies the
    model
    field.
    CustomSystemPrompt
    SystemPromptFunc
    NoBuilt-in promptCustom system prompt function
    CustomToolDescription
    ToolDescriptionFunc
    NoBuilt-in descriptionCustom tool description function
    # Quick Start -Taking loading pdf skill from local as an example, complete code at [https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill](https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill). +Example: loading a pdf skill locally. Full code: [https://github.com/cloudwego/eino-examples/tree/main/adk/middlewares/skill](https://github.com/cloudwego/eino-examples/tree/main/adk/middlewares/skill). -- Create skills directory in working directory: +- Create a skills directory under your working directory: ```go workdir/ @@ -297,42 +227,48 @@ workdir/ └── other files ``` -- Create local filesystem backend, and create Skill middleware based on backend: +- Create a local filesystem backend and build the Skill middleware: ```go -import ( +import ( "github.com/cloudwego/eino/adk/middlewares/skill" "github.com/cloudwego/eino-ext/adk/backend/local" -) +) +ctx := context.Background() be, err := local.NewBackend(ctx, &local.Config{}) if err != nil { log.Fatal(err) } -wd, _ := os.Getwd() -workDir := filepath.Join(wd, "adk", "middlewares", "skill", "workdir") skillBackend, err := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{ Backend: be, BaseDir: skillsDir, }) +if err != nil { + log.Fatalf("Failed to create skill backend: %v", err) +} + +sm, err := skill.NewMiddleware(ctx, &skill.Config{ + Backend: skillBackend, +}) ``` -- Create local Filesystem Middleware based on backend, for agent to read skill other files and execute scripts: +- Create a local FileSystem middleware so the agent can read other skill files and execute scripts: ```go import ( "github.com/cloudwego/eino/adk/middlewares/filesystem" ) -fsm, err := filesystem.NewMiddleware(ctx, &filesystem.Config{ - Backend: be, - WithoutLargeToolResultOffloading: true, +fsm, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: be, + StreamingShell: be, }) ``` -- Create Agent and configure middlewares +- Create an agent and configure middlewares: ```go agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ @@ -340,11 +276,11 @@ agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ Description: "An agent that can analyze logs", Instruction: "You are a helpful assistant.", Model: cm, - Middlewares: []adk.ChatModelAgentMiddleware{fsm, skillMiddleware}, + Handlers: []adk.ChatModelAgentMiddleware{fsm, sm}, }) ``` -- Call Agent and observe results +- Run the agent and observe output: ```go runner := adk.NewRunner(ctx, adk.RunnerConfig{ @@ -421,9 +357,9 @@ answer: Here's the analysis result of the log file: The log file contains critical issues related to database connectivity and a warning about memory usage. Let me know if you need further analysis! ``` -# Principles +# How It Works -Skill middleware adds system prompt and skill tool to Agent, system prompt content is as follows, {tool_name} is the tool name of skill tool: +The Skill middleware adds a system prompt and a skill tool to the agent. The system prompt is below, where `{tool_name}` is the tool name of the skill tool: ```python # Skills System @@ -457,7 +393,7 @@ User: "Can you research the latest developments in quantum computing?" Remember: Skills make you more capable and consistent. When in doubt, check if a skill exists for the task! ``` -Skill tool receives the skill name to load, returns the complete content in the corresponding SKILL.md, and informs agent of all available skill names and descriptions in the tool description: +The skill tool takes a skill name to load and returns the full content of the corresponding SKILL.md. Its tool description lists all available skills with their names and descriptions: ```sql Execute a skill within the main conversation @@ -495,9 +431,9 @@ Important: ``` -Execution example: +Example: > 💡 -> Skill Middleware only provides the ability to load SKILL.md as shown above. If the Skill itself needs to read files, execute scripts, etc., users need to configure the corresponding capabilities for the agent separately. +> 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/filesystem_backend/_index.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md new file mode 100644 index 00000000000..5eb63ed655d --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md @@ -0,0 +1,175 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: FileSystem Backend +weight: 1 +--- + +> 💡 +> Package: [github.com/cloudwego/eino/adk/filesystem](https://github.com/cloudwego/eino/tree/main/adk/filesystem) + +## Background and Goals + +In AI Agent scenarios, the agent often needs to interact with a filesystem: reading file content, searching code, editing configs, executing commands, and so on. However, different runtime environments access the filesystem very differently: + +- **Local development**: operate on the local filesystem directly, works out of the box +- **Cloud sandbox**: operate on an isolated sandbox filesystem via remote APIs, requires authentication and networking +- **Testing**: use an in-memory simulated filesystem without real disk I/O +- **Custom storage**: integrate with OSS, databases, or other non-traditional “filesystems” + +If each environment implements its own set of file operations, middleware and agent code become tightly coupled to the underlying storage implementation, making reuse and testing difficult. + +To address this, Eino ADK defines the `filesystem.Backend` interface as a **unified filesystem operation protocol**. Its design goals are: + +1. **Decouple storage from business logic**: middleware depends only on the Backend interface and does not care whether the underlying implementation is local disk, a remote sandbox, or an in-memory mock +2. **Pluggable replacement**: by switching Backend implementations, the same agent can run in different environments without changing any business code +3. **Testability**: a built-in `InMemoryBackend` makes it easy to simulate filesystem behavior in unit tests +4. **Extensibility**: all methods use struct parameters, so adding new fields in the future won’t break compatibility for existing implementations + +## Backend Interface + +```go +type Backend interface { + // List files and directories under the given path + LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error) + // Read file content, supports line-based pagination (offset + limit) + Read(ctx context.Context, req *ReadRequest) (*FileContent, error) + // Search for matches of pattern under the given path and return the match list + GrepRaw(ctx context.Context, req *GrepRequest) ([]GrepMatch, error) + // Find matching files by glob pattern and base path + GlobInfo(ctx context.Context, req *GlobInfoRequest) ([]FileInfo, error) + // Write or create a file + Write(ctx context.Context, req *WriteRequest) error + // Replace string content in a file + Edit(ctx context.Context, req *EditRequest) error +} +``` + +### Extension Interfaces + +Besides the core file operations, a Backend can optionally implement shell command execution: + +```go +// Shell provides synchronous command execution +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +// StreamingShell provides streaming command execution for long-running commands +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +When a Backend implements `Shell` or `StreamingShell`, the FileSystem middleware additionally registers the `execute` tool so the agent can run shell commands. + +### Core Data Types + + + + + + + + + + + + +
    TypeDescription
    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
    + +## Built-in Implementation: InMemoryBackend + +`InMemoryBackend` is a built-in Backend implementation that stores files in an in-memory map, mainly used for: + +- **Unit tests**: test agent and middleware file operations without a real filesystem +- **Lightweight scenarios**: temporary file operations without persistence +- **Large tool result offloading**: the FileSystem middleware’s large tool result offloading feature uses InMemoryBackend by default + +```go +import "github.com/cloudwego/eino/adk/filesystem" + +ctx := context.Background() +backend := filesystem.NewInMemoryBackend() + +// Write file +err := backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/example/test.txt", + Content: "Hello, World!\nLine 2\nLine 3", +}) + +// Read file (paginated) +content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/example/test.txt", + Offset: 1, + Limit: 10, +}) + +// List directory +files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/example", +}) + +// Search content (regex supported) +matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Pattern: "Hello", + Path: "/example", +}) + +// Edit file +err = backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/example/test.txt", + OldString: "Hello", + NewString: "Hi", + ReplaceAll: false, +}) +``` + +Features: + +- Thread-safe (based on `sync.RWMutex`) +- GrepRaw supports regex, case-insensitive, context lines, and other advanced options +- GrepRaw uses parallel processing internally (up to 10 workers) + +## External Implementations + +The following Backend implementations live in the [eino-ext](https://github.com/cloudwego/eino-ext) repository: + +- **Local Backend** — a local filesystem implementation that operates on the host disk with zero configuration +- **Ark Agentkit Sandbox Backend** — a Volcengine Agentkit remote sandbox implementation that executes file operations in an isolated cloud environment + +### Implementation Comparison + + + + + + + + + +
    FeatureInMemoryLocalAgentkit Sandbox
    Execution modelIn-memoryLocal directRemote sandbox
    Network dependencyNoNoYes
    Configuration complexityZero configZero configCredentials required
    PersistenceNoYesYes
    Shell supportNoYes (including streaming)Yes
    Use casesTests/temporaryDevelopment/localMulti-tenant/production
    + +## Custom Implementations + +To integrate custom storage (e.g. OSS, databases), you only need to implement the `Backend` interface: + +```go +type MyBackend struct { + // ... +} + +func (b *MyBackend) LsInfo(ctx context.Context, req *filesystem.LsInfoRequest) ([]filesystem.FileInfo, error) { + // Custom implementation +} + +func (b *MyBackend) Read(ctx context.Context, req *filesystem.ReadRequest) (*filesystem.FileContent, error) { + // Custom implementation +} + +// ... implement the remaining methods +``` + +If you also need command execution, implement `Shell` or `StreamingShell` as well. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md similarity index 92% rename from content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md rename to content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md index 6e9e702aa2e..7f10f023cdd 100644 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md @@ -1,9 +1,9 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] -title: 'Backend: Ark Agentkit Sandbox' +title: Ark Agentkit Sandbox weight: 1 --- @@ -11,7 +11,7 @@ weight: 1 Package: `github.com/cloudwego/eino-ext/adk/backend/agentkit` -Note: If your eino version is v0.8.0 or above, you need to use ark agentkit backend [v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.0-alpha.1) version. +Note: If your eino version is v0.8.0 or above, you need to use ark agentkit backend [adk/backend/agentkit/v0.2.1](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.1). ### Overview @@ -92,9 +92,10 @@ func main() { }) // Read file - content, err := backend.Read(ctx, &filesystem.ReadRequest{ + fContent, err := backend.Read(ctx, &filesystem.ReadRequest{ FilePath: "/home/gem/hello.txt", }) + fmt.Println(fContent.Content) } ``` @@ -192,10 +193,6 @@ result, _ := backend.Execute(ctx, &filesystem.ExecuteRequest{ ### FAQ -**Q: Write returns "file already exists" error** - -This is a security feature. Use a different filename or use Edit to modify existing files. - **Q: Authentication failed** Check environment variables, verify AK/SK match, and ensure account has Ark Sandbox permissions. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md "b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" similarity index 90% rename from content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md rename to "content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" index 45d69c9eb1d..9334bf19a63 100644 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md +++ "b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" @@ -1,9 +1,9 @@ --- Description: "" -date: "2026-03-12" +date: "2026-03-24" lastmod: "" tags: [] -title: 'Backend: Local FileSystem' +title: Local File System weight: 2 --- @@ -67,9 +67,10 @@ func main() { }) // Read file - content, err := backend.Read(ctx, &filesystem.ReadRequest{ + fcontent, err := backend.Read(ctx, &filesystem.ReadRequest{ FilePath: "/tmp/hello.txt", }) + fmt.Println(fcontent.Content) } ``` @@ -217,17 +218,13 @@ absPath, _ := filepath.Abs("./relative/path") ### FAQ -**Q: Why are absolute paths required?** +**Q: Why does running grep fail with `ripgrep (rg) is not installed or not in PATH. Please install it:` [https://github.com/BurntSushi/ripgrep#installation](https://github.com/BurntSushi/ripgrep#installation)?** -To prevent directory traversal attacks. Use `filepath.Abs()` to convert. - -**Q: Write fails** - -File already exists (security feature), path is not absolute, or insufficient permissions. +The local Grep command relies on `ripgrep` by default. If your system does not have `ripgrep` installed, install it following the official guide. **Q: Does GrepRaw support regex?** -No, it uses literal matching. For regex, use Execute to call system grep. +Yes. GrepRaw uses `ripgrep` under the hood for grep operations, so regex patterns are supported. **Q: Windows support?** diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md new file mode 100644 index 00000000000..93301c65f2f --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md @@ -0,0 +1,281 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: AgentsMD +weight: 9 +--- + +## Overview + +`agentsmd` is an Eino ADK middleware that **automatically injects the content of Agents.md into the model input messages on every model call**. The injection is ephemeral: it is added dynamically for each model call and is not persisted into the session state, so it **won’t be processed by summarization/compression middlewares**. + +**Core value**: define system-level behavior instructions and context for an agent via an Agents.md file (similar to Claude Code’s CLAUDE.md), without manually composing system prompts. + +**Package**: `github.com/cloudwego/eino/adk/middlewares/agentsmd` + +--- + +## Quick Start + +### Minimal Example + +```go +package main + +import ( + "context" + "fmt" + + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/adk/middlewares/agentsmd" +) + +func main() { + ctx := context.Background() + + // 1. Prepare Backend (file reading backend) + backend := NewLocalFileBackend("/path/to/project") + + // 2. Create agentsmd middleware + mw, err := agentsmd.New(ctx, &agentsmd.Config{ + Backend: backend, + AgentsMDFiles: []string{"/home/user/project/agents.md"}, + }) + if err != nil { + panic(err) + } + + // 3. Attach the middleware to the agent + // agent := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // Middlewares: []adk.ChatModelAgentMiddleware{mw}, + // }) + _ = mw + fmt.Println("agentsmd middleware created successfully") +} +``` + +--- + +## Configuration + +### Config + +```go +type Config struct { + // Backend provides file access to load Agents.md files. + // It can be a local filesystem, remote storage, or any other backend. + // Required. + Backend Backend + + // AgentsMDFiles is an ordered list of Agents.md file paths to load. + // Files are loaded and injected in the given order. + // Files support recursive @import (max depth 5). + AgentsMDFiles []string + + // AllAgentsMDMaxBytes limits the total bytes of all loaded Agents.md content. + // Files are loaded in order; once the cumulative size exceeds this limit, + // the remaining files will be skipped. + // Each individual file is always loaded in full. + // 0 means unlimited. + AllAgentsMDMaxBytes int + + // OnLoadWarning is an optional callback invoked on non-fatal errors during loading + // (e.g. file not found, cyclic @import, depth limit exceeded). + // If nil, warnings are printed via log.Printf. + // + // Note: Backend.Read errors other than os.ErrNotExist (e.g. permission denied, I/O errors) + // are not treated as warnings and will abort the loading process. + OnLoadWarning func(filePath string, err error) +} +``` + +### Parameters + + + + + + + +
    ParameterTypeRequiredDefaultDescription
    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 Interface + +### Definition + +```go +type Backend interface { + // Read reads file content. + // If the file does not exist, implementations should return an error that wraps os.ErrNotExist + // (so errors.Is(err, os.ErrNotExist) returns true). + // This lets the loader skip missing files silently and notify via OnLoadWarning. + // Other errors (permission denied, I/O errors) abort the loading process. + Read(ctx context.Context, req *ReadRequest) (*FileContent, error) +} +``` + +### Types + +```go +// ReadRequest defines request parameters for reading a file +type ReadRequest struct { + FilePath string // file path + Offset int // starting line number (1-based) +} + +// FileContent defines the return structure of file content +type FileContent struct { + Content string // file text content +} +``` + +--- + +## @import Syntax + +Agents.md supports `@import` to recursively include other files. + +### Syntax + +In Agents.md, use `@path/to/file` to reference another file: + +```markdown +# Project instructions + +You are a coding assistant. + +Please follow these rules: +@rules/code-style.md +@rules/api-conventions.md +``` + +### Rules + +1. **Path resolution**: relative paths are resolved from the current file’s directory; absolute paths are used as-is +2. **Max recursion depth**: 5 (beyond that the import is skipped and `OnLoadWarning` is triggered) +3. **Cycle detection**: cyclic imports are detected and skipped (`OnLoadWarning` is triggered) +4. **Global de-duplication**: the same file is not loaded twice +5. **Supported extensions** (when the path contains no `/`): `.md`, `.txt`, `.mdx`, `.yaml`, `.yml`, `.json`, `.toml` +6. **False-positive filtering**: `@ref` without `/` whose extension is not allowed will be ignored (to avoid treating `@someone` or `@example.com` as an import) + +### Example Directory Layout + +``` +project/ +├── Agents.md # entry file +├── rules/ +│ ├── code-style.md # code style rules +│ ├── api-conventions.md # API conventions +│ └── testing.md # testing rules +└── context/ + └── architecture.md # architecture notes +``` + +--- + +## How It Works + +### Injection Flow + +``` +User message + history + │ + ▼ +┌─────────────────────┐ +│ agentsmd middleware │ +│ (WrapModel) │ +│ │ +│ 1. Load Agents.md │ +│ 2. Cache in RunLocal│ +│ 3. Build injected msg│ +└─────────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ Injected message sequence │ +│ │ +│ [System] system prompt │ +│ [User] ← Agents.md injection │ ← inserted before the first User message +│ [User] previous user message 1 │ +│ [Assistant] assistant reply 1 │ +│ [User] current user message │ +└─────────────────────────────────────┘ + │ + ▼ +Model call (Generate / Stream) +``` + +### Key Mechanics + +1. **Ephemeral injection**: Agents.md content is inserted only for model calls and not written into `ChatModelAgentState`, so it won’t be summarized/compressed +2. **Run-level caching**: within a single agent `Run()`, the loaded Agents.md content is cached in `RunLocalValue`; subsequent model calls reuse it to avoid repeated reads +3. **Insertion position**: injected as a `User` role message before the first user message; if there is no user message, it is appended to the end +4. **I18n**: formatted output adapts to Chinese/English automatically (based on the system language environment) + +--- + +## Notes + +### Middleware Ordering + +**It is recommended to place the `agentsmd` middleware after summarization/compression middlewares.** This ensures Agents.md content: + +- won’t be compressed away by summarization +- is fully available on every model call + +```go +Middlewares: []adk.ChatModelAgentMiddleware{ + summarizationMiddleware, // summarize first + agentsMDMiddleware, // then inject Agents.md +} +``` + +### Error Handling + + + + + + + + + +
    ScenarioBehavior
    File not found (
    os.ErrNotExist
    )
    Skip the file and trigger
    OnLoadWarning
    Cyclic
    @import
    Skip the cyclic file and trigger
    OnLoadWarning
    @import
    depth > 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 errorAbort loading and return error
    All file contents emptyDo not inject; pass through original messages
    + +### Backend Requirements + +- When a file does not exist, implementations **must** return an error that wraps `os.ErrNotExist` (e.g. `fmt.Errorf(\"... : %w\", os.ErrNotExist)`), otherwise the loader cannot distinguish “missing file” vs “real I/O error” +- `Read` should be concurrency-safe + +### Performance Considerations + +- Set `AllAgentsMDMaxBytes` reasonably to avoid injecting too much content and consuming the model context window +- Agents.md is loaded once per `Run()` (run-level caching), but **every new `Run()` reloads it**, so file edits take effect on the next run +- Avoid importing too many files; the recursion depth limit is 5 + +### Writing Agents.md + +- Keep it concise and include only instructions that truly affect model behavior +- Use `@import` to split concerns (code style, API conventions, architecture notes, etc.) +- Avoid large code examples or datasets in Agents.md to prevent wasting context window +- The content is wrapped in `` tags when passed to the model, so the model treats it as system-level instructions + +--- + +## FAQ + +**Q: Will Agents.md content be saved into the conversation history?** +A: No. The content is injected dynamically during model calls and is not written into `ChatModelAgentState`, so it won’t appear in history. + +**Q: What happens if an Agents.md file does not exist?** +A: The file is skipped and `OnLoadWarning` is triggered (defaults to `log.Printf`). It does not fail the whole load. + +**Q: What is the base directory for @import paths?** +A: The directory of the current file. For example, `@rules/style.md` in `/project/Agents.md` resolves to `/project/rules/style.md`. + +**Q: If multiple files import the same file, will it be loaded multiple times?** +A: No. The loader maintains a global de-duplication map; the same file path is read and injected only once. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md new file mode 100644 index 00000000000..e7fba84f352 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md @@ -0,0 +1,187 @@ +--- +Description: "" +date: "2026-03-24" +lastmod: "" +tags: [] +title: FileSystem +weight: 2 +--- + +> 💡 Package: [github.com/cloudwego/eino/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) + +## Overview + +The FileSystem middleware provides filesystem access for agents. It operates the filesystem through the [FileSystem Backend](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/filesystem_backend) interface and automatically injects a set of file operation tools and the corresponding system prompt, enabling the agent to read/write/search/edit files directly. + +Core capabilities: + +- **Filesystem tool injection** — automatically registers tools such as ls, read_file, write_file, edit_file, glob, grep +- **Shell command execution** — optionally injects the execute tool, supports both sync and streaming execution +- **Per-tool configuration** — each tool can be configured independently (name/description/custom implementation/disable) +- **Multilingual prompts** — tool descriptions and system prompts support Chinese/English switching + +## Create the Middleware + +It is recommended to use `New` to create the middleware (returns `ChatModelAgentMiddleware`): + +```go +import "github.com/cloudwego/eino/adk/middlewares/filesystem" + +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + // To enable shell command execution, set Shell or StreamingShell + Shell: myShell, +}) +if err != nil { + // handle error +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +> 💡 +> `New` returns `ChatModelAgentMiddleware` with better context propagation (it can modify the agent’s instruction and tools at runtime via the `BeforeAgent` hook). + +## MiddlewareConfig + +```go +type MiddlewareConfig struct { + // Backend provides filesystem operations + // Required + Backend filesystem.Backend + + // Shell provides synchronous shell command execution + // If set, the execute tool will be registered + // Optional, mutually exclusive with StreamingShell + Shell filesystem.Shell + + // StreamingShell provides streaming shell command execution + // If set, the streaming execute tool will be registered (real-time output) + // Optional, mutually exclusive with Shell + StreamingShell filesystem.StreamingShell + + // Per-tool configuration (all optional) + LsToolConfig *ToolConfig // ls tool config + ReadFileToolConfig *ToolConfig // read_file tool config + WriteFileToolConfig *ToolConfig // write_file tool config + EditFileToolConfig *ToolConfig // edit_file tool config + GlobToolConfig *ToolConfig // glob tool config + GrepToolConfig *ToolConfig // grep tool config + + // CustomSystemPrompt overrides the default system prompt + // Optional, defaults to ToolsSystemPrompt + CustomSystemPrompt *string + + // Deprecated fields, use the corresponding *ToolConfig.Desc instead + // CustomLsToolDesc, CustomReadFileToolDesc, CustomGrepToolDesc, + // CustomGlobToolDesc, CustomWriteFileToolDesc, CustomEditToolDesc +} +``` + +### ToolConfig + +Each tool can be configured independently via `ToolConfig`: + +```go +type ToolConfig struct { + // Name overrides the tool name + // Optional. Defaults to the built-in name (e.g. "ls", "read_file") + Name string + + // Desc overrides the tool description + // Optional. Defaults to the built-in description + Desc *string + + // CustomTool provides a custom tool implementation + // If set, it replaces the default implementation built on Backend + // Optional + CustomTool tool.BaseTool + + // Disable disables this tool + // When true, the tool will not be registered + // Optional, defaults to false + Disable bool +} +``` + +Example — rename a tool and disable write: + +```go +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + ReadFileToolConfig: &filesystem.ToolConfig{ + Name: "cat_file", // custom name + }, + WriteFileToolConfig: &filesystem.ToolConfig{ + Disable: true, // disable write tool + }, +}) +``` + +## Injected Tools + + + + + + + + + + +
    ToolDefault nameDescriptionCondition
    List directory
    ls
    List files and directories under the given pathInjected 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 fileInjected when Backend is not nil
    Edit file
    edit_file
    Replace strings in a fileInjected when Backend is not nil
    Glob
    glob
    Find files by glob patternInjected when Backend is not nil
    Search content
    grep
    Search file content by pattern, supports multiple output modesInjected when Backend is not nil
    Execute command
    execute
    Execute shell commandsRequires Shell or StreamingShell
    + +Each tool can be disabled via its corresponding `*ToolConfig` (`Disable: true`) or replaced with a custom implementation (`CustomTool`). + +## Multilingual Support + +Tool descriptions and built-in prompts default to English. To switch to Chinese, use `adk.SetLanguage()`: + +```go +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // switch to Chinese +adk.SetLanguage(adk.LanguageEnglish) // switch to English (default) +``` + +You can also customize each tool’s text via `ToolConfig.Desc` or override the system prompt via `CustomSystemPrompt`. + +## [deprecated] Large Tool Result Offloading + +> 💡 +> This feature will be deprecated in 0.8.0. Please migrate to Middleware: ToolReduction. + +> Note: Large tool result offloading is only available in the legacy `Config` + `NewMiddleware` API. The recommended `MiddlewareConfig` + `New` does not include it. If you need it, use the ToolReduction middleware. + +When tool call results are too large (e.g. reading large files, grep matching too many lines), keeping the full result in the conversation context can cause: + +- token usage to spike +- agent history context pollution +- worse reasoning efficiency + +So the legacy middleware (`NewMiddleware`) provides an automatic offloading mechanism: + +- when the result exceeds a threshold (default 20,000 tokens), it does not return the full content to the LLM +- the actual result is saved to the filesystem (Backend) +- the context contains only a summary and a file path (the agent can call `read_file` again to fetch on demand) + +This feature is enabled by default and can be configured via `Config` (not `MiddlewareConfig`): + +```go +type Config struct { + // ... Backend, Shell, StreamingShell, ToolConfig fields are the same as MiddlewareConfig + + // Disable automatic offloading + WithoutLargeToolResultOffloading bool + + // Custom threshold (default 20000 tokens) + LargeToolResultOffloadingTokenLimit int + + // Custom offloading path generator + // Default path format: /large_tool_result/{ToolCallID} + LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) +} +``` diff --git a/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md b/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md index 9319dbf8634..c4bf6d5e2a7 100644 --- a/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md +++ b/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md @@ -1,13 +1,18 @@ --- Description: "" -date: "2026-03-12" +date: "2026-03-24" lastmod: "" tags: [] -title: 'Eino ADK: Agent Callback' +title: Agent Callback weight: 9 --- -This feature adds Callback support to ADK Agents, similar to the callback mechanism in the compose package. Through callbacks, users can observe the Agent's execution lifecycle for logging, tracing, monitoring, and other purposes. +This feature adds Callback support to ADK agents, similar to the callback mechanism in the compose package. With callbacks, users can observe the agent execution lifecycle and implement logging, tracing, monitoring, and more. + +> 💡 +> **Tip**: The cozeloop ADK trace version is available at [https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0](https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0) +> +> Make sure to use a trace callback handler implementation that supports v0.8, otherwise agent tracing won’t work properly. ## Overview @@ -15,69 +20,69 @@ The ADK Agent Callback mechanism shares the same infrastructure as the callback - Uses the same `callbacks.Handler` interface - Uses the same `callbacks.RunInfo` structure -- Can be combined with other component callbacks (such as ChatModel, Tool, etc.) +- Can be combined with callbacks of other components (e.g. ChatModel, Tool) > 💡 -> Through Agent Callback, you can hook into key points of Agent execution to implement observability capabilities like tracing, logging, and metrics. This feature was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). +> With Agent Callback, you can hook into key points of agent execution to implement observability such as tracing, logging, and metrics. This capability was introduced in v0.8.0. ## Core Types ### ComponentOfAgent -Component type identifier used to identify Agent-related events in callbacks: +Component type identifier used to recognize agent-related events in callbacks: ```go const ComponentOfAgent components.Component = "Agent" ``` -Used in `callbacks.RunInfo.Component` to filter callback events related only to Agents. +Used in `callbacks.RunInfo.Component` to filter callback events related to agents only. ### AgentCallbackInput -Input type for Agent callbacks, passed in the `OnStart` callback: +Input type for agent callbacks, passed to `OnStart`: ```go type AgentCallbackInput struct { - // Input contains the Agent input for new runs. Nil when resuming execution. + // Input contains the agent input for a new run. It is nil when resuming. Input *AgentInput - // ResumeInfo contains information when resuming from an interrupt. Nil for new runs. + // ResumeInfo contains information for resuming from an interrupt. It is nil for a new run. ResumeInfo *ResumeInfo } ``` - - - + + +
    Call MethodField Values
    Agent.Run()
    Input
    field has value,
    ResumeInfo
    is nil
    Agent.Resume()
    ResumeInfo
    field has value,
    Input
    is nil
    CallField values
    Agent.Run()
    Input
    is set,
    ResumeInfo
    is nil
    Agent.Resume()
    ResumeInfo
    is set,
    Input
    is nil
    ### AgentCallbackOutput -Output type for Agent callbacks, passed in the `OnEnd` callback: +Output type for agent callbacks, passed to `OnEnd`: ```go type AgentCallbackOutput struct { - // Events provides the Agent event stream. Each handler receives an independent copy. + // Events provides the agent event stream. Each handler receives its own copy. Events *AsyncIterator[*AgentEvent] } ``` > 💡 -> **Important**: The `Events` iterator should be consumed **asynchronously** to avoid blocking Agent execution. Each callback handler receives an independent copy of the event stream without interfering with each other. +> **Important**: consume `Events` **asynchronously** to avoid blocking agent execution. Each callback handler gets an independent copy of the event stream, so they do not interfere with each other. ## API Usage ### WithCallbacks -Run option to add callback handlers to receive Agent lifecycle events: +Run option that adds callback handlers to receive agent lifecycle events: ```go func WithCallbacks(handlers ...callbacks.Handler) AgentRunOption ``` -### Type Conversion Functions +### Type Conversion Helpers -Convert generic callback types to Agent-specific types: +Convert generic callback types to agent-specific types: ```go // Convert input type @@ -87,13 +92,13 @@ func ConvAgentCallbackInput(input callbacks.CallbackInput) *AgentCallbackInput func ConvAgentCallbackOutput(output callbacks.CallbackOutput) *AgentCallbackOutput ``` -Functions return nil if the type doesn't match. +If the type does not match, these functions return nil. -## Usage Examples +## Examples -### Method 1: Using HandlerBuilder +### Option 1: Use HandlerBuilder -Use `callbacks.NewHandlerBuilder()` to build a generic callback handler: +Build a generic callback handler via `callbacks.NewHandlerBuilder()`: ```go import ( @@ -116,14 +121,14 @@ handler := callbacks.NewHandlerBuilder(). OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { if info.Component == adk.ComponentOfAgent { agentOutput := adk.ConvAgentCallbackOutput(output) - // Consume event stream asynchronously + // Consume events asynchronously go func() { for { event, ok := agentOutput.Events.Next() if !ok { break } - // Process event... + // Handle event... fmt.Printf("Event from %s: %+v\n", event.AgentName, event) } }() @@ -132,12 +137,21 @@ handler := callbacks.NewHandlerBuilder(). }). Build() -iter := agent.Run(ctx, input, adk.WithCallbacks(handler)) +// Create Runner - callbacks only work when running the agent via Runner +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: agent, + EnableStreaming: input.EnableStreaming, +}) + +iter := runner.Run(ctx, input.Messages, adk.WithCallbacks(handler)) ``` -### Method 2: Using HandlerHelper (Recommended) +> 💡 +> **Important**: this is the correct usage. Callbacks only work when running the agent through Runner. If you call `agent.Run()` directly, callbacks will not be triggered. + +### Option 2: Use HandlerHelper (Recommended) -Using `template.HandlerHelper` makes type conversion more convenient: +`template.HandlerHelper` makes type conversion easier: ```go import ( @@ -164,7 +178,7 @@ helper := template.NewHandlerHelper(). if !ok { break } - // Process event... + // Handle event... } }() return ctx @@ -172,15 +186,27 @@ helper := template.NewHandlerHelper(). }). Handler() -iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +// Create Runner - callbacks only work when running the agent via Runner +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: agent, + EnableStreaming: input.EnableStreaming, +}) + +iter := runner.Run(ctx, input.Messages, adk.WithCallbacks(helper)) ``` > 💡 -> `HandlerHelper` automatically performs type conversion, making the code more concise. It also supports combining callback handlers for multiple components. +> **Important**: callbacks only work when running the agent through Runner. If you call `agent.Run()` directly, callbacks will not be triggered. +> +> 💡 +> `HandlerHelper` performs type conversion automatically and keeps the code concise. It also supports composing callbacks for multiple components. -## Tracing Application +## Tracing Use Case -The most common use case for Agent Callback is implementing distributed tracing. Here's an example using OpenTelemetry for tracing: +> 💡 +> **Important**: AgentCallback only works when executed via Runner. If you call Agent.Run() directly, callbacks will not be triggered because the callback mechanism is implemented at the flowAgent layer. Create a Runner via `adk.NewRunner()` and execute the agent via `Runner.Run()` or `Runner.Query()`. + +The most common use case is distributed tracing. Below is an example using OpenTelemetry: ```go import ( @@ -188,8 +214,24 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" + + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/callbacks" ) +// Create an Agent (ChatModelAgent as example) +agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "my_agent", + Description: "A helpful assistant", + Model: chatModel, +}) + +// Create Runner - callbacks only work when running the agent via Runner +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: agent, + EnableStreaming: true, +}) + tracer := otel.Tracer("my-agent-tracer") handler := callbacks.NewHandlerBuilder(). @@ -226,62 +268,36 @@ handler := callbacks.NewHandlerBuilder(). return ctx }). Build() -``` - -### Combining with compose Callbacks -Since ADK Agent callbacks share the same infrastructure as compose callbacks, you can use the same handler to process callbacks from both Agents and other components (like ChatModel, Tool): - -```go -helper := template.NewHandlerHelper(). - // Agent callback - Agent(&template.AgentCallbackHandler{ - OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *adk.AgentCallbackInput) context.Context { - ctx, _ = tracer.Start(ctx, "agent:"+info.Name) - return ctx - }, - OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { - trace.SpanFromContext(ctx).End() - return ctx - }, - }). - // ChatModel callback - ChatModel(&template.ModelCallbackHandler{ - OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *model.CallbackInput) context.Context { - ctx, _ = tracer.Start(ctx, "model:"+info.Name) - return ctx - }, - OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *model.CallbackOutput) context.Context { - trace.SpanFromContext(ctx).End() - return ctx - }, - }). - // Tool callback - Tool(&template.ToolCallbackHandler{ - OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context { - ctx, _ = tracer.Start(ctx, "tool:"+input.Name) - return ctx - }, - OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context { - trace.SpanFromContext(ctx).End() - return ctx - }, - }). - Handler() +// Execute via Runner and pass the callback handler +iter := runner.Query(ctx, "Hello, agent!", adk.WithCallbacks(handler)) -// Use combined handler -iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +// Consume event stream +for { + event, ok := iter.Next() + if !ok { + break + } + if event.Err != nil { + log.Error(event.Err) + break + } + // Handle event... +} ``` > 💡 -> **Tip**: For the cozeloop ADK trace version, see [https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1](https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1) +> **Reminder**: callbacks only work when running the agent via Runner. If you call `agent.Run()` directly, even if you pass `adk.WithCallbacks(handler)`, agent-level callbacks will not be triggered. +> +> 💡 +> **Tip**: The cozeloop ADK trace version is available at [https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0](https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0) ## Agent Type Identifiers -Built-in Agents implement the `components.Typer` interface, returning their type identifier, which populates the `callbacks.RunInfo.Type` field: +Built-in agents implement `components.Typer` and return their type identifier, which is filled into `callbacks.RunInfo.Type`: - + @@ -289,24 +305,24 @@ Built-in Agents implement the `components.Typer` interface, returning their type
    Agent TypeGetType() Return Value
    Agent typeGetType() return value
    ChatModelAgent
    "ChatModel"
    workflowAgent (Sequential)
    "Sequential"
    workflowAgent (Parallel)
    "Parallel"
    DeterministicTransfer Agent
    "DeterministicTransfer"
    -## Callback Behavior +## Callback Semantics -### Callback Invocation Timing +### Callback Timing
    -Run Method1. Initialize callback context2. Process input3. Call
    OnStart
    4. Execute Agent logic5. Register
    OnEnd
    (when event stream is created)
    -Resume Method1. Build ResumeInfo2. Initialize callback context3. Call
    OnStart
    4. Resume Agent execution5. Register
    OnEnd
    (when event stream is created)
    +Run1. Initialize callback context2. Handle input3. Call
    OnStart
    4. Execute agent logic5. Register
    OnEnd
    (when iterator is created) +Resume1. Build ResumeInfo2. Initialize callback context3. Call
    OnStart
    4. Resume agent execution5. Register
    OnEnd
    (when iterator is created) -### OnEnd Invocation Timing +### OnEnd Timing -The `OnEnd` callback is registered **when the iterator is created**, not when the generator closes. This allows handlers to consume events while the stream is being transmitted. +`OnEnd` is registered **when the iterator is created**, not when the generator is closed. This enables handlers to consume events while the stream is being produced. ## Notes -### 1. Consume Event Stream Asynchronously +### 1. Consume Events Asynchronously -The `AgentCallbackOutput.Events` in callback handlers **must** be consumed asynchronously, otherwise it will block Agent execution: +In callback handlers, `AgentCallbackOutput.Events` **must** be consumed asynchronously, otherwise it will block agent execution: ```go // ✅ Correct @@ -317,20 +333,20 @@ OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallb if !ok { break } - // Process event + // Handle event } }() return ctx } -// ❌ Wrong - will cause deadlock +// ❌ Wrong - will deadlock OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { for { event, ok := output.Events.Next() if !ok { break } - // Process event + // Handle event } return ctx } @@ -338,8 +354,8 @@ OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallb ### 2. No OnError Callback -Since `Agent.Run()` and `Agent.Resume()` method signatures don't return errors, Agent callbacks **do not support** `OnError`. Error information is passed through the `AgentEvent.Err` field in the event stream. +Because `Agent.Run()` and `Agent.Resume()` do not return error, agent callbacks **do not support** `OnError`. Errors are carried via `AgentEvent.Err` in the event stream. -### 3. Event Stream Copying Mechanism +### 3. Event Stream Copying -When there are multiple callback handlers, each handler receives an independent copy of the event stream without interfering with each other. The last handler receives the original events to reduce memory allocation. +When multiple callback handlers are registered, each handler receives an independent copy of the event stream. The last handler receives the original stream to reduce allocations. diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md index 896acb5f25e..bfd09a76eb8 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md @@ -604,6 +604,8 @@ func NewBookRecommendAgent() adk.Agent { } ``` +### + ### Step 4: Run via Runner ```go diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md index e54b259d0de..9f7f876db3d 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md @@ -75,7 +75,7 @@ type Config struct { StreamingShellProvides Shell capability with streaming results, optional, mutually exclusive with Shellexecute(streaming) -DeepAgents implements built-in filesystem by referencing filesystem middleware. For more detailed capability description of this middleware, see: [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem) +DeepAgents implements built-in filesystem by referencing filesystem middleware. For more detailed capability description of this middleware, see: [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_filesystem) ### Task Decomposition and Planning diff --git a/content/en/docs/eino/quick_start/chapter_09_a2ui_protocol.md b/content/en/docs/eino/quick_start/chapter_09_a2ui_protocol.md index 3f1c4246564..b5a9204f732 100644 --- a/content/en/docs/eino/quick_start/chapter_09_a2ui_protocol.md +++ b/content/en/docs/eino/quick_start/chapter_09_a2ui_protocol.md @@ -3,22 +3,250 @@ Description: "" date: "2026-03-16" lastmod: "" tags: [] -title: "Final: A2UI protocol (streaming UI components)" +title: "Chapter 10: A2UI Protocol (Streaming UI Components)" weight: 10 --- -Goal of this chapter: implement the A2UI protocol and render the Agent output as a stream of UI components. +Goal of this chapter: implement the A2UI protocol and render agent output as streaming UI components. ## Important: A2UI’s boundary -A2UI is not part of the Eino framework itself. It is a business-layer UI protocol/rendering approach. This chapter integrates A2UI into the Agent you built across previous chapters to provide an end-to-end, production-oriented example: model calls, tool calls, workflow orchestration, and finally delivering results as a more user-friendly UI. +A2UI is not part of the Eino framework itself. It is a business-layer UI protocol/rendering approach. This chapter integrates A2UI into the agent built in earlier chapters to provide an end-to-end, production-ready example: model calls, tool calls, workflow orchestration, and finally presenting results in a more user-friendly UI. In real-world products, you can choose different UI forms depending on your product: - Web/App: custom components, tables, cards, charts - IM/office suite: message cards, interactive forms -- CLI: plain text or TUI +- CLI: plain text or TUI (terminal UI) -## Full tutorial +Eino focuses on “composable intelligent execution and orchestration.” “How to present to users” is a business-layer concern you can extend freely. -- [ch10_a2ui.md](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md) +## Code location + +- Entry: [main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/main.go) +- Agent construction: [agent.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/agent.go) +- Server routes: [server/server.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/server/server.go) +- A2UI subset types: [a2ui/types.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/a2ui/types.go) +- A2UI event streamer: [a2ui/streamer.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/a2ui/streamer.go) +- Frontend page: [static/index.html](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/static/index.html) + +## Prerequisites + +Same as Chapter 1: configure a ChatModel (OpenAI or Ark). + +## Run + +In `quickstart/chatwitheino`, run: + +```bash +go run . +``` + +Output example: + +``` +starting server on http://localhost:8080 +``` + +### (Optional) Enable ch09 skills + +The final web agent aligns with Chapter 9 logic: when `EINO_EXT_SKILLS_DIR` points to a valid skills directory, it registers the `skill` middleware so the model can load `eino-guide` / `eino-component` / `eino-compose` / `eino-agent` on demand. + +```bash +go run ./scripts/sync_eino_ext_skills.go -src /path/to/eino-ext -dest ./skills/eino-ext -clean +EINO_EXT_SKILLS_DIR="$(pwd)/skills/eino-ext" go run . +``` + +## From text to UI: why A2UI + +The agents we built in the first eight chapters only output text, but modern AI applications need richer interaction. + +**Limitations of pure text:** + +- Cannot display structured data (tables, lists, cards) +- Cannot update in real time (progress, status changes) +- Cannot embed interactive elements (buttons, forms, links) +- Cannot support multimedia (images, video, audio) + +**A2UI’s positioning:** + +- **A2UI is a protocol from agent to UI**: defines how agent outputs map to UI components +- **A2UI supports streaming rendering**: components update in real time without waiting for the full response +- **A2UI is declarative**: the agent declares “what to show” and the UI handles rendering + +**Simple analogy:** + +- **Plain text output** = “terminal CLI” (text only) +- **A2UI** = “web app” (can render any UI components) + +## Key concepts + +### A2UI v0.8 subset (scope of this example) + +This quickstart does not implement a “full A2UI standard library.” Instead, it implements a **subset of A2UI v0.8**: the goal is to push the agent event stream to the browser as a stable, incrementally renderable UI component tree. + +The current supported A2UI message types and component types are defined in [a2ui/types.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/a2ui/types.go). + +### A2UI messages: BeginRendering / SurfaceUpdate / DataModelUpdate / InterruptRequest + +Each SSE line (`data: {...}`) carries one A2UI Message. The Message is an “envelope” that contains exactly one field: + +**Key snippet (simplified; see a2ui/types.go for the full code):** + +```go +type Message struct { + BeginRendering *BeginRenderingMsg + SurfaceUpdate *SurfaceUpdateMsg + DataModelUpdate *DataModelUpdateMsg + DeleteSurface *DeleteSurfaceMsg + InterruptRequest *InterruptRequestMsg +} +``` + +Where: + +- `BeginRendering`: tells the frontend “start rendering a surface (session)” and provides the root node ID +- `SurfaceUpdate`: adds/updates a batch of components (components form a tree and reference each other by id) +- `DataModelUpdate`: updates data bindings (for streaming incremental text into a Text component) +- `InterruptRequest`: when the agent triggers an interrupt (e.g. approval), asks the frontend to render approve/reject entry points + +### A2UI components: Text / Column / Card / Row + +This example implements only 4 UI components (see [a2ui/types.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/a2ui/types.go)): + +- `Text`: text rendering (supports `usageHint` to distinguish caption/body/title); when `dataKey` exists, text comes from `DataModelUpdate` +- `Column` / `Row`: layout (children is a list of component IDs) +- `Card`: card container (children is a list of component IDs) + +## A2UI implementation: converting AgentEvent to A2UI SSE + +The core web pipeline is: + +- Run the agent to get `*adk.AsyncIterator[*adk.AgentEvent]` +- Convert the event stream to A2UI JSONL/SSE for the browser (see [a2ui/streamer.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/a2ui/streamer.go)) +- Frontend parses SSE `data:` lines and renders the component tree (see [static/index.html](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/static/index.html)) + +### Server routes (high level) + +Key endpoints related to A2UI (see [server/server.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/server/server.go)): + +- `GET /`: serves the frontend page `static/index.html` +- `POST /sessions/:id/chat`: returns SSE stream (A2UI messages), renders the agent output as it runs +- `GET /sessions/:id/render`: returns JSONL (A2UI messages) for replaying history +- `POST /sessions/:id/approve`: handles interrupt approval/rejection and continues streaming + +### Event streaming (high level) + +The server passes `Runner.Run(...)` events to `a2ui.StreamToWriter(...)`, which: + +- splits user/assistant/tool outputs +- renders tool call / tool result as “chip cards” +- turns assistant streaming tokens into `DataModelUpdate` for incremental rendering +- sends `InterruptRequest` when an interrupt happens, and waits for human approval + +## Frontend integration: fetch + SSE (not WebSocket) + +- The frontend calls `fetch('/sessions/:id/chat')`, then reads `res.body` as a stream, splits by lines, and parses `data: {...}` JSON (see [static/index.html](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/static/index.html)). + +**Key snippet (simplified; see static/index.html for full code):** + +```javascript +const res = await fetch(`/sessions/${id}/chat`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({message}), +}); + +const reader = res.body.getReader(); +const decoder = new TextDecoder(); +let buffer = ''; +while (true) { + const {done, value} = await reader.read(); + if (done) break; + buffer += decoder.decode(value, {stream: true}); + const lines = buffer.split('\n'); + buffer = lines.pop(); + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed.startsWith('data:')) { + const jsonStr = trimmed.slice(5).trimStart(); + processA2UIMessage(JSON.parse(jsonStr)); + } + } +} +``` + +## A2UI streaming flow (overview) + +``` +┌─────────────────────────────────────────┐ +│ User: Analyze this file │ +└─────────────────────────────────────────┘ + ↓ + ┌──────────────────────┐ + │ Agent starts │ + │ A2UI: AddText │ + │ "Analyzing..." │ + └──────────────────────┘ + ↓ + ┌──────────────────────┐ + │ Tool call │ + │ A2UI: AddProgress │ + │ Progress: 0% │ + └──────────────────────┘ + ↓ + ┌──────────────────────┐ + │ Tool running │ + │ A2UI: UpdateProgress│ + │ Progress: 50% │ + └──────────────────────┘ + ↓ + ┌──────────────────────┐ + │ Tool finished │ + │ A2UI: tool result │ + └──────────────────────┘ + ↓ + ┌──────────────────────┐ + │ Show result │ + │ A2UI: DataModelUpdate│ + │ (stream assistant) │ + └──────────────────────┘ +``` + +## Chapter summary + +- **A2UI**: a protocol from agent to UI defining how agent output maps to UI components +- **Subset implementation**: this example only implements Text/Column/Card/Row plus data binding +- **Streaming output**: the backend streams A2UI JSONL over SSE, the frontend renders incrementally +- **Events to UI**: convert `AgentEvent` into visual outputs for tool calls, tool results, and assistant streams + +## Series wrap-up: the full vision of this Quickstart Agent + +By the end of this chapter, we have an agent that ties together Eino’s core capabilities. Think of it as an extensible “end-to-end agent application skeleton”: + +- Runtime: Runner-driven execution with streaming output and event model +- Tooling: filesystem/shell tools with safe error handling +- Middleware: pluggable middleware/handlers for errors, retries, approvals, and more +- Observability: callbacks/trace to connect key pipelines for debugging and production monitoring +- Human-in-the-loop: interrupt/resume + checkpoint for approvals, parameter requests, branch choices +- Deterministic orchestration: compose (graph/chain/workflow) organizes complex business flows +- Delivery: UI integration like A2UI is business-layer — pick what fits your product + +You can gradually replace/extend any part: models, tools, storage, workflows, frontend protocol — without starting over. + +## Further exploration + +**Other component types:** + +- Chart components (line, bar, pie) +- Map components +- Timeline components +- Tree components +- Tabs components + +**Advanced features:** + +- Component interactions (click, drag, input) +- Conditional rendering +- Component animations +- Responsive layout diff --git a/content/en/docs/eino/quick_start/chapter_09_skill_console.md b/content/en/docs/eino/quick_start/chapter_09_skill_console.md index 6f7558bcbfd..ee7a57add54 100644 --- a/content/en/docs/eino/quick_start/chapter_09_skill_console.md +++ b/content/en/docs/eino/quick_start/chapter_09_skill_console.md @@ -1,24 +1,142 @@ --- Description: "" -date: "2026-03-16" +date: "2026-03-24" lastmod: "" tags: [] title: "Chapter 9: Skill (Console)" weight: 9 --- -Goal of this chapter: introduce the `skill` middleware so the Agent can discover and load reusable skill documents (`SKILL.md`) and use them on demand through tool calls. +Goal of this chapter: on top of Chapter 8 (RAG + Interrupt/Resume + Checkpoint), introduce the `skill` middleware so the agent can discover and load reusable skill documents (`SKILL.md`) and invoke them via tool calls. ## Code location -- Entry code: [cmd/ch09/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch09/main.go) +- Entry: [cmd/ch09/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch09/main.go) - Sync script: [scripts/sync_eino_ext_skills.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/scripts/sync_eino_ext_skills.go) -## Full tutorial +## Prerequisites -- [ch09_skill.md](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch09_skill.md) +- Same as Chapter 1: configure a ChatModel (OpenAI or Ark) +- Prepare skills provided by the eino-ext PR (`eino-guide` / `eino-component` / `eino-compose` / `eino-agent`) -## What you learn +Why these four? -- How “progressive disclosure” works for skills (index → load on demand). -- How to integrate skills into an Agent without permanently inflating prompt context. +ChatWithEino is positioned as “help users learn Eino and assist with Eino coding using AI.” These four skills cover the key knowledge areas: + +- `eino-guide`: entry point and navigation (where to start, how to run quickly) +- `eino-component`: component interfaces and implementation references (Model/Embedding/Retriever/Tool/Callback, etc.) +- `eino-compose`: orchestration and deterministic workflow references (Graph/Chain/Workflow, etc.) +- `eino-agent`: ADK/Agent references (Agent/Runner/Middleware/Filesystem/Human-in-the-loop, etc.) + +Skill sources: + +- Local path to the `eino-ext` repository (the script reads `/skills/...`) +- Or any directory where skills are already installed (containing the above subdirectories) + +## From Graph Tool to Skill: why “skill docs” + +Chapter 8 solves “how to make a complex workflow callable as a Tool” (Graph Tool). But for a framework-learning/development assistant agent, there is another problem: **how to inject stable, reusable knowledge and instructions into the agent, and let it load them on demand at runtime**. + +That is the role of Skills: + +- **Tool** is more like an “action/capability”: read files, run workflows, call external systems +- **Skill** is more like a “reusable knowledge/instruction pack”: a set of markdown files (`SKILL.md` + `reference/*.md`) that describe “how to do something” + +Simple analogy: + +- **Tool** = “what you can do” (function/interface) +- **Skill** = “how to do it” (reusable handbook/manual) + +## Run + +In `quickstart/chatwitheino`, do: + +### 1) Sync eino-ext skills into a local directory + +To let the `skill` middleware discover skills, place them under a single directory and follow the scan convention: + +- `EINO_EXT_SKILLS_DIR//SKILL.md` + +Sync command (recommended): + +```bash +go run ./scripts/sync_eino_ext_skills.go -src /path/to/eino-ext -dest ./skills/eino-ext -clean +``` + +Notes: + +- `-src` supports two forms: + - The root of the `eino-ext` repo (the script reads `/skills/...`) + - A directory where skills are already installed (should contain `eino-guide/`, `eino-component/`, etc.) +- `-dest` defaults to `./skills/eino-ext` (can be omitted) + +### 2) Start Chapter 9 + +```bash +EINO_EXT_SKILLS_DIR=/absolute/path/to/chatwitheino/skills/eino-ext go run ./cmd/ch09 +``` + +Output example (snippet): + +``` +Skills dir: /.../skills/eino-ext +Enter your message (empty line to exit): +``` + +## Enable Skill in DeepAgent + +Skill invocation is not automatic. You must register the `skill` middleware when building the agent. It’s a three-step setup: + +1. Use a local filesystem backend (this chapter uses `eino-ext/adk/backend/local`) to provide file reading/Glob +2. Use `skill.NewBackendFromFilesystem` to turn `EINO_EXT_SKILLS_DIR` into a skill backend +3. Use `skill.NewMiddleware` to create the middleware and attach it to DeepAgent’s `Handlers` + +**Key snippet (simplified; see cmd/ch09/main.go for full code):** + +```go +backend, _ := localbk.NewBackend(ctx, &localbk.Config{}) + +skillBackend, _ := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{ + Backend: backend, + BaseDir: skillsDir, // = $EINO_EXT_SKILLS_DIR +}) +skillMiddleware, _ := skill.NewMiddleware(ctx, &skill.Config{ + Backend: skillBackend, +}) + +agent, _ := deep.New(ctx, &deep.Config{ + ChatModel: cm, + Backend: backend, + StreamingShell: backend, + Handlers: []adk.ChatModelAgentMiddleware{ + skillMiddleware, + // ... other middlewares like approval/safeTool/retry + }, +}) +``` + +Notes: + +- This quickstart checks `EINO_EXT_SKILLS_DIR` existence at runtime: if it exists, it registers `skillMiddleware`; otherwise it skips it (the agent still runs and can use RAG tools). +- Skill tool input is JSON: `{"skill": ""}`, e.g. `{"skill":"eino-guide"}`. + +## Quick verification (recommended) + +After startup, send a prompt that forces a skill tool call to verify that skills are discovered and loadable: + +``` +Use the skill tool with skill="eino-guide" and tell me what the entry point is for getting started. +``` + +You should see output similar to: + +- `[tool result] Launching skill: eino-guide` +- Tool result includes `Base directory for this skill: .../eino-guide` + +## What you will see + +- When the model calls the skill tool, the console prints: + - `[tool call] ...` + - `[tool result] ...` (truncated) +- Sessions are stored under `SESSION_DIR` (default `./data/sessions`) and can be resumed: + - `go run ./cmd/ch09 --session ` diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md index 1ffaf4c3780..24e6d20ae02 100644 --- a/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md @@ -59,7 +59,7 @@ agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ }) ``` -See [Eino ADK: ChatModelAgentMiddleware](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware) for details +See [Eino ADK: ChatModelAgentMiddleware](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware) for details --- @@ -68,7 +68,7 @@ See [Eino ADK: ChatModelAgentMiddleware](/docs/eino/core_modules/eino_adk/Eino_A > 💡 > **Function**: Automatic conversation history summarization to prevent exceeding model context window limits -📚 **Detailed Documentation**: [Middleware: Summarization](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization) +📚 **Detailed Documentation**: [Middleware: Summarization](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_summarization) When the token count of conversation history exceeds a threshold, automatically calls LLM to generate a summary and compress the context. @@ -95,7 +95,7 @@ mw, err := summarization.New(ctx, &summarization.Config{ > 💡 > **Function**: Tool result compression to optimize context usage efficiency -📚 **Detailed Documentation**: [Middleware: ToolReduction](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction) +📚 **Detailed Documentation**: [Middleware: ToolReduction](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_toolreduction) Provides two-phase tool output management: @@ -120,7 +120,7 @@ mw, err := reduction.New(ctx, &reduction.Config{ > 💡 > **Function**: File system operation toolset -📚 **Detailed Documentation**: [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem) +📚 **Detailed Documentation**: [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_filesystem) **New Capabilities**: @@ -133,7 +133,7 @@ mw, err := reduction.New(ctx, &reduction.Config{ > 💡 > **Function**: Dynamic loading and execution of Skills -📚 **Detailed Documentation**: [Middleware: Skill](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill) +📚 **Detailed Documentation**: [Middleware: Skill](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_skill) **New Capabilities**: @@ -146,7 +146,7 @@ mw, err := reduction.New(ctx, &reduction.Config{ > 💡 > **Function**: Task planning and execution tools -📚 **Detailed Documentation**: [Middleware: PlanTask](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask) +📚 **Detailed Documentation**: [Middleware: PlanTask](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_plantask) Supports Agent creation and management of task plans, suitable for complex task scenarios requiring step-by-step execution. @@ -155,7 +155,7 @@ Supports Agent creation and management of task plans, suitable for complex task > 💡 > **Function**: Tool search with dynamic retrieval from a large number of tools -📚 **Detailed Documentation**: [Middleware: ToolSearch](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch) +📚 **Detailed Documentation**: [Middleware: ToolSearch](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_toolsearch) When there are many tools, dynamically selects the most relevant tools through semantic search to avoid context overload. @@ -164,7 +164,7 @@ When there are many tools, dynamically selects the most relevant tools through s > 💡 > **Function**: Patch dangling tool calls to ensure message history completeness -📚 **Detailed Documentation**: [Middleware: PatchToolCalls](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls) +📚 **Detailed Documentation**: [Middleware: PatchToolCalls](/docs/eino/core_modules/eino_adk/eino_adk_chatmodelagentmiddleware/middleware_patchtoolcalls) Scans message history and inserts placeholder messages for tool calls missing responses. Suitable for scenarios where tool calls are interrupted or cancelled. From b990fcfd4d74ba391c97d1cb2b073d9b9fb71040 Mon Sep 17 00:00:00 2001 From: "shentong.martin" Date: Tue, 24 Mar 2026 19:30:05 +0800 Subject: [PATCH 3/3] docs(eino): normalize middleware titles --- .../Middleware_PatchToolCalls.md | 2 +- .../Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md | 2 +- .../Middleware_Summarization.md | 2 +- .../Middleware_ToolReduction.md | 2 +- .../Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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 58919c396d3..7feea7b33e5 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 @@ -3,7 +3,7 @@ Description: "" date: "2026-03-02" lastmod: "" tags: [] -title: 'Middleware: PatchToolCalls' +title: PatchToolCalls weight: 7 --- 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 308aacb0c12..740b3775891 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 @@ -3,7 +3,7 @@ Description: "" date: "2026-03-02" lastmod: "" tags: [] -title: 'Middleware: PlanTask' +title: PlanTask weight: 4 --- 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 dd9cd72f6ba..8895dc6f705 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 @@ -3,7 +3,7 @@ Description: "" date: "2026-03-02" lastmod: "" tags: [] -title: 'Middleware: Summarization' +title: Summarization weight: 3 --- diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md index 7d6e8d9cb3d..3fe9bf47048 100644 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md @@ -3,7 +3,7 @@ Description: "" date: "2026-03-12" lastmod: "" tags: [] -title: 'Middleware: ToolReduction' +title: ToolReduction weight: 6 --- diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md index 4b7bc301c2d..f3d60b57c33 100644 --- a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md @@ -3,7 +3,7 @@ Description: "" date: "2026-03-02" lastmod: "" tags: [] -title: 'Middleware: ToolSearch' +title: ToolSearch weight: 5 ---