From 16302af7d200d324a79c882541c93f76d30a2f87 Mon Sep 17 00:00:00 2001
From: hhs <386998068@qq.com>
Date: Fri, 19 Jun 2026 14:35:06 +0800
Subject: [PATCH 001/116] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20Eino=20?=
=?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=8F=82=E8=80=83=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/Eino/EiNO 项目实战.md | 292 ++++
docs/Eino/Eino 知识索引.md | 245 ++++
docs/Eino/docs/core_modules/_index.md | 22 +
.../chain_and_graph_orchestration/_index.md | 57 +
.../call_option_capabilities.md | 306 ++++
.../callback_manual.md | 738 ++++++++++
.../chain_graph_introduction.md | 675 +++++++++
.../checkpoint_interrupt.md | 441 ++++++
.../orchestration_design_principles.md | 477 +++++++
.../stream_programming_essentials.md | 222 +++
.../workflow_orchestration_framework.md | 732 ++++++++++
.../docs/core_modules/components/_index.md | 75 +
.../components/agentic_chat_model_guide.md | 1191 ++++++++++++++++
.../components/agentic_chat_template_guide.md | 322 +++++
.../components/agentic_tools_node_guide.md | 378 +++++
.../components/chat_model_guide.md | 538 +++++++
.../components/chat_template_guide.md | 302 ++++
.../document_loader_guide/_index.md | 308 ++++
.../document_parser_interface_guide.md | 268 ++++
.../components/document_transformer_guide.md | 279 ++++
.../components/embedding_guide.md | 273 ++++
.../core_modules/components/indexer_guide.md | 446 ++++++
.../core_modules/components/lambda_guide.md | 226 +++
.../components/retriever_guide.md | 446 ++++++
.../components/tools_node_guide/_index.md | 717 ++++++++++
.../tools_node_guide/how_to_create_a_tool.md | 680 +++++++++
docs/Eino/docs/core_modules/devops/_index.md | 10 +
.../core_modules/devops/ide_plugin_guide.md | 91 ++
.../devops/visual_debug_plugin_guide.md | 400 ++++++
.../visual_orchestration_plugin_guide.md | 114 ++
.../Middleware_PatchToolCalls.md | 159 +++
.../Middleware_PlanTask.md | 292 ++++
.../Middleware_Skill.md | 439 ++++++
.../Middleware_Summarization.md | 210 +++
.../Middleware_ToolReduction.md | 317 +++++
.../Middleware_ToolSearch.md | 145 ++
.../_index.md | 521 +++++++
.../filesystem_backend/_index.md | 175 +++
.../backend_ark_agentkit_sandbox.md | 202 +++
.../backend_本地文件系统.md | 231 +++
.../middleware_agentsmd.md | 280 ++++
.../middleware_filesystem.md | 187 +++
.../Eino/docs/core_modules/eino_adk/_index.md | 10 +
.../eino_adk/adk_agent_callback.md | 361 +++++
.../eino_adk/agent_collaboration.md | 521 +++++++
.../core_modules/eino_adk/agent_extension.md | 118 ++
.../docs/core_modules/eino_adk/agent_hitl.md | 1189 ++++++++++++++++
.../eino_adk/agent_implementation/_index.md | 12 +
.../agent_implementation/chat_model.md | 897 ++++++++++++
.../agent_implementation/deepagents.md | 196 +++
.../agent_implementation/plan_execute.md | 510 +++++++
.../agent_implementation/supervisor.md | 499 +++++++
.../eino_adk/agent_implementation/workflow.md | 1265 +++++++++++++++++
.../core_modules/eino_adk/agent_interface.md | 390 +++++
.../core_modules/eino_adk/agent_preview.md | 162 +++
.../core_modules/eino_adk/agent_quickstart.md | 93 ++
.../flow_integration_components/_index.md | 135 ++
.../multi_agent_hosting.md | 420 ++++++
.../react_agent_manual.md | 591 ++++++++
.../Eino/docs/ecosystem_integration/_index.md | 67 +
.../ecosystem_integration/callbacks/_index.md | 26 +
.../chat_model/_index.md | 33 +
.../chat_model/agentic_model_ark.md | 440 ++++++
.../chat_model/agentic_model_openai.md | 457 ++++++
.../chat_template/_index.md | 25 +
.../ecosystem_integration/document/_index.md | 32 +
.../ecosystem_integration/embedding/_index.md | 30 +
.../ecosystem_integration/indexer/_index.md | 33 +
.../ecosystem_integration/retriever/_index.md | 34 +
.../docs/ecosystem_integration/tool/_index.md | 33 +
docs/Eino/docs/overview/_index.md | 399 ++++++
.../docs/overview/bytedance_eino_practice.md | 488 +++++++
docs/Eino/docs/overview/eino_adk0_1.md | 571 ++++++++
.../docs/overview/eino_adk_excel_agent.md | 541 +++++++
docs/Eino/docs/overview/eino_open_source.md | 187 +++
docs/Eino/docs/overview/graph_or_agent.md | 348 +++++
.../Eino_v0.4._-compose_optimization.md | 47 +
.../Eino_v0.5._-ADK_implementation.md | 72 +
.../Eino_v0.6._-jsonschema_optimization.md | 41 +
.../Eino_v0.7._-interrupt_resume_refactor.md | 139 ++
.../Eino_v0.8_不兼容更新.md | 308 ++++
.../Eino_v0.8._-adk_middlewares/_index.md | 276 ++++
.../release_notes_and_migration/_index.md | 65 +
.../v01_first_release.md | 96 ++
.../v02_second_release.md | 124 ++
.../v03_tiny_break_change.md | 39 +
docs/Eino/quick_start/README.md | 98 ++
.../chapter_01_chatmodel_and_message.md | 308 ++++
...ter_02_chatmodelagent_runner_agentevent.md | 431 ++++++
.../async_iterator_consumption.md | 94 ++
.../why_ctx_in_agent_interface.md | 122 ++
.../chapter_03_memory_and_session.md | 330 +++++
.../chapter_04_tool_and_filesystem.md | 395 +++++
.../Eino/quick_start/chapter_05_middleware.md | 433 ++++++
.../chapter_06_callback_and_trace.md | 323 +++++
.../chapter_07_interrupt_resume.md | 272 ++++
.../Eino/quick_start/chapter_08_graph_tool.md | 368 +++++
.../quick_start/chapter_09_skill_console.md | 197 +++
.../quick_start/chapter_10_a2ui_protocol.md | 366 +++++
99 files changed, 30486 insertions(+)
create mode 100644 docs/Eino/EiNO 项目实战.md
create mode 100644 docs/Eino/Eino 知识索引.md
create mode 100644 docs/Eino/docs/core_modules/_index.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/_index.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/callback_manual.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
create mode 100644 docs/Eino/docs/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
create mode 100644 docs/Eino/docs/core_modules/components/_index.md
create mode 100644 docs/Eino/docs/core_modules/components/agentic_chat_model_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/agentic_chat_template_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/agentic_tools_node_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/chat_model_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/chat_template_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/document_loader_guide/_index.md
create mode 100644 docs/Eino/docs/core_modules/components/document_loader_guide/document_parser_interface_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/document_transformer_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/embedding_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/indexer_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/lambda_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/retriever_guide.md
create mode 100644 docs/Eino/docs/core_modules/components/tools_node_guide/_index.md
create mode 100644 docs/Eino/docs/core_modules/components/tools_node_guide/how_to_create_a_tool.md
create mode 100644 docs/Eino/docs/core_modules/devops/_index.md
create mode 100644 docs/Eino/docs/core_modules/devops/ide_plugin_guide.md
create mode 100644 docs/Eino/docs/core_modules/devops/visual_debug_plugin_guide.md
create mode 100644 docs/Eino/docs/core_modules/devops/visual_orchestration_plugin_guide.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/_index.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_ark_agentkit_sandbox.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/filesystem_backend/backend_本地文件系统.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_agentsmd.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/middleware_filesystem.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/_index.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/adk_agent_callback.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_collaboration.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_extension.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_hitl.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/_index.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/chat_model.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/deepagents.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/plan_execute.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/supervisor.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_implementation/workflow.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_interface.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_preview.md
create mode 100644 docs/Eino/docs/core_modules/eino_adk/agent_quickstart.md
create mode 100644 docs/Eino/docs/core_modules/flow_integration_components/_index.md
create mode 100644 docs/Eino/docs/core_modules/flow_integration_components/multi_agent_hosting.md
create mode 100644 docs/Eino/docs/core_modules/flow_integration_components/react_agent_manual.md
create mode 100644 docs/Eino/docs/ecosystem_integration/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/callbacks/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/chat_model/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/chat_model/agentic_model_ark.md
create mode 100644 docs/Eino/docs/ecosystem_integration/chat_model/agentic_model_openai.md
create mode 100644 docs/Eino/docs/ecosystem_integration/chat_template/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/document/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/embedding/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/indexer/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/retriever/_index.md
create mode 100644 docs/Eino/docs/ecosystem_integration/tool/_index.md
create mode 100644 docs/Eino/docs/overview/_index.md
create mode 100644 docs/Eino/docs/overview/bytedance_eino_practice.md
create mode 100644 docs/Eino/docs/overview/eino_adk0_1.md
create mode 100644 docs/Eino/docs/overview/eino_adk_excel_agent.md
create mode 100644 docs/Eino/docs/overview/eino_open_source.md
create mode 100644 docs/Eino/docs/overview/graph_or_agent.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.4._-compose_optimization.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_不兼容更新.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/_index.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/v01_first_release.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/v02_second_release.md
create mode 100644 docs/Eino/docs/release_notes_and_migration/v03_tiny_break_change.md
create mode 100644 docs/Eino/quick_start/README.md
create mode 100644 docs/Eino/quick_start/chapter_01_chatmodel_and_message.md
create mode 100644 docs/Eino/quick_start/chapter_02_chatmodelagent_runner_agentevent.md
create mode 100644 docs/Eino/quick_start/chapter_02_chatmodelagent_runner_agentevent/async_iterator_consumption.md
create mode 100644 docs/Eino/quick_start/chapter_02_chatmodelagent_runner_agentevent/why_ctx_in_agent_interface.md
create mode 100644 docs/Eino/quick_start/chapter_03_memory_and_session.md
create mode 100644 docs/Eino/quick_start/chapter_04_tool_and_filesystem.md
create mode 100644 docs/Eino/quick_start/chapter_05_middleware.md
create mode 100644 docs/Eino/quick_start/chapter_06_callback_and_trace.md
create mode 100644 docs/Eino/quick_start/chapter_07_interrupt_resume.md
create mode 100644 docs/Eino/quick_start/chapter_08_graph_tool.md
create mode 100644 docs/Eino/quick_start/chapter_09_skill_console.md
create mode 100644 docs/Eino/quick_start/chapter_10_a2ui_protocol.md
diff --git a/docs/Eino/EiNO 项目实战.md b/docs/Eino/EiNO 项目实战.md
new file mode 100644
index 0000000..9f8b1a8
--- /dev/null
+++ b/docs/Eino/EiNO 项目实战.md
@@ -0,0 +1,292 @@
+---
+tags: [eino, go, ai-agent, rag, tool-calling, project-theme]
+create time: 2026-05-05 14:30
+---
+
+# EiNO 项目实战:个人知识助手
+
+## 概述
+
+基于字节跳动 EINO 框架(Go 语言),从零构建一个**个人知识助手 Agent**。该项目深度融合 **RAG(检索增强生成)** 与 **Tool Calling(工具调用)** 两大核心能力,采用 Supervisor 多 Agent 编排模式,帮助用户高效检索笔记、整理知识、发现关联。场景聚焦日常知识管理,不依赖外部基础设施,是入门 EINO 框架的理想项目。
+
+## 正文
+
+### 1. 项目背景
+
+> [!question] 思考:当你积累了上千篇笔记,某天想找一篇"半年前记过的 Go 并发模式",只记得大概内容却忘了标题——你会怎么办?
+
+传统做法:逐个翻文件夹 → 搜关键词 → 翻了几分钟还是没找到。而一个智能知识助手可以:
+
+1. **听懂模糊描述**:"那个讲 goroutine 泄漏排查的文章"→ 语义检索精准定位
+2. **整理碎片知识**:"把最近关于 EINO 的笔记汇总成一篇综述"
+3. **发现隐藏关联**:"这篇 RAG 笔记和那篇向量数据库笔记其实在讲同一件事"
+
+这个场景**天然适合 AI Agent**:检索知识库(RAG)+ 操作笔记(Tool Calling)+ 多步骤任务(ReAct)。
+
+**用 EINO 的原因**:
+- Go 原生协程,本地跑也轻量
+- 编译时类型检查,工具定义清晰、不易出错
+- ADK 内置 Supervisor / Plan-Execute / Interrupt 等模式,开箱即用
+
+---
+
+### 2. 系统架构总览
+
+```mermaid
+graph TD
+ U["用户提问"] --> S["Supervisor Agent
知识总管家"]
+
+ S --> R["Retrieval Agent
知识检索专家"]
+ S --> W["Writer Agent
内容处理专家"]
+ S --> O["Organizer Agent
知识整理专家"]
+
+ R --> VDB["向量数据库
笔记内容索引"]
+ R --> T1["Tool: 语义搜索
相似笔记召回"]
+ R --> T2["Tool: 关键词搜索
精确匹配"]
+
+ W --> T3["Tool: 创建笔记
写入 Markdown"]
+ W --> T4["Tool: 摘要提取
生成笔记摘要"]
+ W --> T5["Tool: 标签推荐
自动打标签"]
+
+ O --> T6["Tool: 关联发现
Wiki-link 推荐"]
+ O --> T7["Tool: 知识图谱
关联关系查询"]
+
+ style S fill:#4A90D9,color:#fff
+ style VDB fill:#27AE60,color:#fff
+```
+
+> **Supervisor 模式**:Supervisor Agent 接收用户指令,根据意图路由——搜索类交给 Retrieval Agent、写作类交给 Writer Agent、整理类交给 Organizer Agent,最终汇总结果返回。
+
+---
+
+### 3. RAG 模块设计
+
+RAG 负责从用户的笔记库中检索相关内容,让 Agent "读懂你的知识库"。
+
+#### 3.1 笔记索引管道
+
+```mermaid
+flowchart LR
+ A["Markdown 笔记库"] --> B["Document Loader
按段落分块"]
+ B --> C["Embedding
文本向量化"]
+ C --> D["Indexer
写入向量库"]
+ D --> E["Hybrid Retriever
混合检索"]
+ E --> F["Agent
上下文注入"]
+```
+
+#### 3.2 核心代码:混合检索器
+
+```go
+// RetrieverService 混合检索:语义匹配 + 标签过滤
+type RetrieverService struct {
+ client milvus.Client
+ embedder embedding.Embedder
+}
+
+func (s *RetrieverService) Retrieve(ctx context.Context, query string, tags []string) ([]*schema.Document, error) {
+ // 1. 将用户查询转为向量
+ vector, err := s.embedder.Embed(ctx, query)
+ if err != nil {
+ return nil, fmt.Errorf("embed query: %w", err)
+ }
+
+ // 2. 构建标量过滤:限定标签范围
+ // 例如: "tag in ['go', 'concurrency', 'eino']"
+ expr := buildTagFilterExpr(tags)
+
+ // 3. 混合检索:Top-K=5
+ results, err := s.client.Search(ctx, "notes_collection",
+ nil, expr,
+ []string{"content", "title", "tags"},
+ vector,
+ milvus.NewTopKMetricType(milvus.L2, 5),
+ milvus.NewSearchParam(16),
+ )
+ // ... 转换为 EINO Document 格式
+ return s.convertToDocs(results), nil
+}
+```
+
+> [!tip] 设计要点
+> 混合检索 = **语义相似度**("我记得大概意思") + **标签过滤**("应该是 Go 相关的")。相比纯关键词搜索,它能找到表述不同但意思相近的笔记——这正是知识管理中最常见的场景。
+
+---
+
+### 4. Tool Calling 模块设计
+
+Agent 通过工具与笔记系统交互:搜索、创建、整理、发现关联。
+
+#### 4.1 工具清单
+
+| 工具 | 类型 | 描述 |
+|------|------|------|
+| `semantic_search` | 查询 | 语义搜索笔记,支持模糊自然语言描述 |
+| `keyword_search` | 查询 | 精确关键词 + 标签搜索 |
+| `create_note` | 写入 | 创建新笔记(Markdown + YAML frontmatter) |
+| `generate_summary` | 处理 | 为指定笔记生成摘要 |
+| `suggest_tags` | 处理 | 根据内容自动推荐标签 |
+| `find_related` | 查询 | 发现关联笔记,推荐 Wiki-link |
+
+#### 4.2 核心代码:定义工具
+
+```go
+// === 写笔记工具 ===
+type CreateNoteParams struct {
+ Title string `json:"title" desc:"笔记标题"`
+ Content string `json:"content" desc:"Markdown 格式正文"`
+ Tags []string `json:"tags" desc:"标签列表,如 ['go', 'eino']"`
+}
+
+func CreateNoteTool(vaultPath string) componenttool.BaseTool {
+ return &componenttool.Tool{
+ Name: "create_note",
+ Desc: "在知识库中创建一篇新的 Markdown 笔记",
+ Func: func(ctx context.Context, params *CreateNoteParams) (string, error) {
+ fullPath := filepath.Join(vaultPath, params.Title+".md")
+ content := buildMarkdownWithFrontmatter(params)
+ if err := os.WriteFile(fullPath, []byte(content), 0o644); err != nil {
+ return "", fmt.Errorf("write note: %w", err)
+ }
+ return fmt.Sprintf("笔记已创建: %s", fullPath), nil
+ },
+ }
+}
+
+// === 关联发现工具 ===
+type FindRelatedParams struct {
+ NoteTitle string `json:"note_title" desc:"目标笔记标题"`
+}
+
+func FindRelatedTool(retriever *RetrieverService) componenttool.BaseTool {
+ return &componenttool.Tool{
+ Name: "find_related",
+ Desc: "根据笔记内容,从知识库中发现与之关联的其他笔记,推荐 Wiki-link",
+ Func: func(ctx context.Context, params *FindRelatedParams) (string, error) {
+ noteContent := readNote(params.NoteTitle)
+ related, _ := retriever.Retrieve(ctx, noteContent, nil)
+ return formatWikiLinkSuggestions(related), nil
+ },
+ }
+}
+```
+
+> [!question] 思考:如果用户说"帮我把最近一周关于 EINO 的笔记整理成一篇综述",Agent 需要依次调用哪些工具?顺序能否调换?
+
+---
+
+### 5. Multi-Agent 编排:Supervisor 模式
+
+```go
+// === 构建 Supervisor,编排三个 Specialist Agent ===
+func BuildKnowledgeSupervisor(ctx context.Context) (*adk.Supervisor, error) {
+ // Retrieval Agent: 负责搜索和检索
+ retrievalAgent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "RetrievalAgent",
+ Instruction: "你是知识检索专家,擅长从笔记库中找到最相关的内容...",
+ Model: model,
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []componenttool.BaseTool{
+ SemanticSearchTool(retriever),
+ KeywordSearchTool(),
+ FindRelatedTool(retriever),
+ },
+ },
+ },
+ MaxIterations: 10,
+ })
+
+ // Writer Agent: 负责创建和整理内容
+ writerAgent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "WriterAgent",
+ Model: model,
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []componenttool.BaseTool{
+ CreateNoteTool(vaultPath),
+ SummaryTool(model),
+ SuggestTagsTool(model),
+ },
+ },
+ },
+ MaxIterations: 8,
+ })
+
+ // Organizer Agent: 负责关联发现和知识图谱
+ organizerAgent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ // ... 配置 FindRelated 等整理工具
+ MaxIterations: 8,
+ })
+
+ return adk.NewSupervisor(ctx, &adk.SupervisorConfig{
+ Name: "KnowledgeSupervisor",
+ Model: model,
+ Instruction: "你是知识总管。根据用户意图路由任务...",
+ SubAgents: []adk.Agent{retrievalAgent, writerAgent, organizerAgent},
+ })
+}
+```
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant S as Supervisor
+ participant R as Retrieval Agent
+ participant W as Writer Agent
+
+ U->>S: "帮我整理最近关于 EINO 的笔记,
写一篇学习综述"
+
+ S->>S: 意图分类 → 检索 + 写作(复合任务)
+
+ S->>R: 委派检索任务
+ R->>R: Tool: semantic_search("EINO") → 找到 5 篇
+ R->>R: Tool: find_related → 发现 3 篇关联笔记
+ R->>S: 返回 8 篇笔记及其内容
+
+ S->>W: 委派写作任务(附检索结果)
+ W->>W: 基于 8 篇笔记撰写综述草稿
+ W->>W: Tool: suggest_tags → ["eino", "agent", "go"]
+ W->>S: 返回综述 + 推荐标签
+
+ S->>U: 📝 综述全文 + 🏷️ 推荐标签 + 🔗 关联笔记
+```
+
+---
+
+### 6. 进阶拓展方向
+
+> [!info] 掌握了基础架构后,可以探索这些方向
+
+1. **会话式检索**:支持多轮追问——"上次那篇关于 goroutine 的"→"不是那篇,是讲泄漏排查的"→ Agent 结合上下文逐步缩小范围,像和人对话一样自然。
+
+2. **定时知识回顾**:用 Cron 触发 Agent,每周自动检索本周新增笔记 → 生成"本周知识地图"→ 推送回顾通知。类似间隔重复,但由 AI 驱动。
+
+3. **跨源增强**:当本地笔记不足时,Agent 可调用 `web_search` 工具获取外部信息作为补充,生成"本地知识 + 外部参考"的混合回答,并标注来源。
+
+4. **知识冲突检测**:当新笔记与已有笔记表述矛盾(如某篇写"Go defer 是栈顺序",另一篇写"是队列顺序"),Agent 自动标记冲突,提醒用户核实。
+
+5. **MCP 集成**:将工具标准化为 MCP Server,让其他 AI 客户端(如 Claude Desktop、VS Code 插件)也能直接调用你的知识助手。
+
+---
+
+### 7. 关键 EINO 概念速查
+
+| EINO 概念 | 本项目对应 |
+|-----------|-----------|
+| `ChatModelAgent` | RetrievalAgent / WriterAgent / OrganizerAgent |
+| `Supervisor` | KnowledgeSupervisor(多 Agent 总调度) |
+| `Tool / ToolsNode` | 语义搜索、创建笔记、摘要、标签、关联发现 |
+| `Retriever` | 混合检索器(语义 + 标签过滤) |
+| `Embedding` | 文本向量化 |
+| `Indexer` | 笔记内容写入向量库 |
+| `Document Loader` | Markdown 笔记按段落分块加载 |
+| `ChatTemplate` | System Prompt + 检索结果注入 |
+| `Interrupt / Resume` | 删改操作前的确认拦截(进阶) |
+| `Graph / Compile` | 编排所有节点、编译成可执行图 |
+
+---
+
+## 关联笔记
+
+- [[EINO ADK 深入]]
+
diff --git a/docs/Eino/Eino 知识索引.md b/docs/Eino/Eino 知识索引.md
new file mode 100644
index 0000000..bae52a0
--- /dev/null
+++ b/docs/Eino/Eino 知识索引.md
@@ -0,0 +1,245 @@
+---
+tags: [eino, llm-framework, golang, agent, ai]
+create time: 2026-04-29 21:30
+---
+
+# Eino 知识索引
+
+## 概述
+
+Eino 是字节跳动(CloudWeGo)开源的大模型应用开发框架,基于 Go 语言。覆盖从组件定义、流程编排到 DevOps 工具链的全流程。本文档作为知识索引,列举 Eino 的关键概念与使用入口,后续可基于此构建知识问答。
+
+---
+
+## 一、是什么 —— Eino 核心定位
+
+| 维度 | 说明 |
+|------|------|
+| **语言** | Go(强类型,编译时类型检查) |
+| **定位** | 大模型应用开发框架,覆盖全流程 |
+| **仓库** | [github.com/cloudwego/eino](https://github.com/cloudwego/eino) + [eino-ext](https://github.com/cloudwego/eino-ext) |
+| **特色** | 组件抽象 → 图编排 → ADK Agent → DevOps 工具链,层层递进 |
+| **适用场景** | 从简单对话到复杂 Multi-Agent 系统,均可应对 |
+
+> [!question] 思考:为什么选 Go 而不是 Python?强类型在大模型应用规模化后能带来什么收益?
+
+---
+
+## 二、怎么分层 —— Eino 架构分层
+
+```mermaid
+graph TD
+ A["Eino 框架"] --> B["组件层 Components"]
+ A --> C["编排层 Orchestration"]
+ A --> D["ADK 层 Agent 开发套件"]
+ A --> E["工具层 DevOps"]
+
+ B --> B1["ChatModel"]
+ B --> B2["ChatTemplate"]
+ B --> B3["Tool / ToolsNode"]
+ B --> B4["Retriever"]
+ B --> B5["Document Loader"]
+ B --> B6["Embedding / Indexer"]
+ B --> B7["Lambda 自定义"]
+
+ C --> C1["Chain 链式"]
+ C --> C2["Graph 有向图"]
+ C --> C3["Workflow 字段映射"]
+ C --> C4["Stream 流处理"]
+ C --> C5["Callback 横切面"]
+
+ D --> D1["Agent 接口"]
+ D --> D2["ChatModelAgent ReAct"]
+ D --> D3["WorkflowAgents"]
+ D --> D4["Multi-Agent 范式"]
+ D --> D5["Middleware 中间件"]
+
+ E --> E1["Tracing 链路追踪"]
+ E --> E2["IDE 插件 / 可视化"]
+ E --> E3["Debug 调试"]
+```
+
+---
+
+## 三、怎么用 —— 快速上手路径
+
+### 3.1 入门四步走
+
+| 步骤 | 主题 | 入口文档 |
+|------|------|----------|
+| **Step 1** | ChatModel 与 Message:学会调模型 | [[Eino/quick_start/chapter_01_chatmodel_and_message]] |
+| **Step 2** | ChatModelAgent + Runner + AgentEvent:学会跑 Agent | [[Eino/quick_start/chapter_02_chatmodelagent_runner_agentevent]] |
+| **Step 3** | Memory & Session:让 Agent 有记忆 | [[Eino/quick_start/chapter_03_memory_and_session]] |
+| **Step 4** | Tool & FileSystem:让 Agent 能干活 | [[Eino/quick_start/chapter_04_tool_and_filesystem]] |
+
+### 3.2 进阶专题
+
+| 主题 | 关键内容 | 入口文档 |
+|------|----------|----------|
+| **Middleware** | 横切面注入,PlanTask / Summarization / Skill 等内置中间件 | [[Eino/quick_start/chapter_05_middleware]] |
+| **Callback & Trace** | 执行过程可观测、可追踪 | [[Eino/quick_start/chapter_06_callback_and_trace]] |
+| **Interrupt & Resume** | Human-in-the-loop,断点续跑 | [[Eino/quick_start/chapter_07_interrupt_resume]] |
+| **Graph & Tool** | 低代码编排,图即是工具 | [[Eino/quick_start/chapter_08_graph_tool]] |
+| **A2UI Protocol** | Agent 生成 UI 协议 | [[chapter_10_a2ui_protocol]] |
+| **Skill Console** | 技能注册与管理 | [[Eino/quick_start/chapter_09_skill_console]] |
+
+---
+
+## 四、关键概念清单
+
+### 4.1 组件 Component
+
+| 组件 | 职责 | 为什么需要 |
+|------|------|-----------|
+| **ChatModel** | 与大模型交互的核心接口 | 统一不同模型(OpenAI / Ark / Gemini)的调用方式 |
+| **ChatTemplate** | 构造 Prompt 模板 | 将变量注入系统/用户消息,结构化管理 |
+| **Tool / ToolsNode** | 可被模型调用的工具 | 让 LLM 从"只会说"变成"能执行" |
+| **Retriever** | 检索外部知识 | RAG 的核心,给模型注入上下文 |
+| **Document Loader** | 加载各类文档 | 将 PDF / Markdown / 网页等转为可处理文本 |
+| **Embedding / Indexer** | 向量化 + 索引 | 语义检索基础 |
+| **Lambda** | 自定义函数作为组件 | 当现有组件不满足需求时,自由扩展 |
+
+> [!question] 思考:Lambda 和 Tool 的区别是什么?什么场景用哪个?
+
+### 4.2 编排 Orchestration
+
+| 编排方式 | 特点 | 适用场景 |
+|----------|------|----------|
+| **Chain** | 简单的顺序执行,有向无环 | 线性 Pipeline |
+| **Graph** | 有向图(可含环),灵活分支控制 | ReAct Agent 等复杂路由 |
+| **Workflow** | 字段级别的数据映射与传递 | 抖音场景:字段粒度的图映射 |
+
+额外能力:
+- **Stream 流处理**:自动处理流式输入输出,支持流的复制、合并、拼接
+- **Callback 横切面**:在组件执行前后注入逻辑(日志、追踪、统计)
+- **编译时类型检查**:Graph Compile 时验证节点类型对齐,而非运行时才发现
+
+### 4.3 ADK —— Agent 开发套件
+
+#### Agent 接口(统一抽象)
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+三个核心要素:**身份(Name)** + **职责(Description)** + **标准化执行(Run → AsyncIterator)**
+
+#### Agent 类型全景
+
+```mermaid
+graph LR
+ A["Agent 接口"] --> B["ChatModelAgent\nReAct 模式"]
+ A --> C["WorkflowAgents\n流程编排"]
+ A --> D["Multi-Agent\n协作范式"]
+ A --> E["自定义 Agent\n实现接口"]
+
+ C --> C1["Sequential\n顺序执行"]
+ C --> C2["Parallel\n并发执行"]
+ C --> C3["Loop\n循环执行"]
+
+ D --> D1["Supervisor\n集中式协调"]
+ D --> D2["Plan-Execute\n规划-执行-反思"]
+ D --> D3["DeepAgents\n规划驱动集中协作"]
+```
+
+| Agent 类型 | 一句话描述 | 典型场景 |
+|------------|-----------|----------|
+| **ChatModelAgent** | 基于 ReAct 的思考-行动循环 | 需要模型自主决策和工具调用的场景 |
+| **SequentialAgent** | 按顺序依次执行子 Agent | ETL 流水线、CI/CD |
+| **ParallelAgent** | 多个子 Agent 并发执行 | 多源数据采集、多渠道推送 |
+| **LoopAgent** | 循环执行直到满足退出条件 | 数据同步、迭代优化 |
+| **Supervisor** | 中心调度,统一分配与汇总 | 科研项目管理、客服流程 |
+| **Plan-Execute** | Planner → Executor → Replanner 闭环 | Excel 处理、多步骤推理 |
+| **DeepAgents** | Main Agent + WriteTodos + TaskTool | 长流程阶段性管理、多角色协作 |
+
+#### Agent 协作机制
+
+| 机制 | 使用方式 | 适用场景 |
+|------|---------|----------|
+| **History** | 框架自动传递前序 Agent 输出 | Agent 间默认信息流 |
+| **Shared Session** | KV 存储,`GetSessionValue` / `AddSessionValue` | 跨 Agent 共享状态 |
+| **Transfer(移交)** | `NewTransferToAgentAction` 将任务移交子 Agent | 边界清晰的层级式分工 |
+| **ToolCall(工具调用)** | `NewAgentTool` 将 Agent 封装为 Tool | 仅需参数、无需完整上下文时 |
+
+#### 中断与恢复
+
+- Agent 运行时通过 `Interrupt Action` 主动中断
+- `CheckPointStore` 保存断点状态
+- `Resume` 方法携带新信息从断点恢复
+- 适用场景:需要外部输入、人工审批、长时等待
+
+#### Middleware 中间件体系
+
+| 内置中间件 | 功能 |
+|------------|------|
+| **PlanTask** | 任务规划与拆解 |
+| **Summarization** | 上下文摘要压缩 |
+| **ToolSearch** | 动态工具搜索与选择 |
+| **ToolReduction** | 工具调用结果精简 |
+| **Skill** | 技能注册与匹配 |
+| **FileSystem** | 文件读写能力注入 |
+| **PatchToolCalls** | 工具调用修正 |
+
+---
+
+## 五、实战案例索引
+
+| 案例 | 核心知识点 | 入口文档 |
+|------|-----------|----------|
+| **ReAct Agent** | Graph 编排 + Tool 调用 | [[Eino/docs/overview/eino_open_source]] |
+| **Excel Agent** | Plan-Execute + Multi-Agent + CodeAgent | [[Eino/docs/overview/eino_adk_excel_agent]] |
+| **项目管理 Agent** | Supervisor + 中断恢复 + Transfer | [[Eino/docs/overview/eino_adk0_1]] |
+| **字节内部实践** | 豆包、抖音等真实场景 | [[Eino/docs/overview/bytedance_eino_practice]] |
+
+---
+
+## 六、进阶对比与选型
+
+| 对比维度 | Eino | LangChain / LlamaIndex |
+|----------|------|------------------------|
+| **语言** | Go(强类型) | Python(动态类型) |
+| **类型安全** | 编译时校验 | 运行时才发现 |
+| **长期维护** | 类型系统天然可维护 | 动态语言大型项目维护成本高 |
+| **并发模型** | goroutine 原生并发 | asyncio / 多线程 |
+| **编排能力** | Graph + Workflow 双模式 | LCEL + Graph |
+| **Agent 框架** | ADK 统一抽象 + 多种范式 | LangGraph Agent |
+
+> [!question] 思考:Eino 的 Graph vs Agent,什么时候直接用 Graph,什么时候用 ADK Agent?
+
+---
+
+## 七、版本演进速览
+
+| 版本 | 关键变化 |
+|------|----------|
+| **v0.1** | 首个开源版本,核心组件 + Chain/Graph |
+| **v0.2** | Callback 体系完善 |
+| **v0.3** | 小幅 break change |
+| **v0.4** | Compose 优化 |
+| **v0.5** | ADK 实现 |
+| **v0.6** | JSON Schema 优化 |
+| **v0.7** | Interrupt/Resume 重构 |
+| **v0.8** | ADK Middleware 体系 |
+
+---
+
+## 八、外部资源
+
+- 项目主页:[https://www.cloudwego.io](https://www.cloudwego.io)
+- GitHub:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino)
+- 扩展库:[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+- 示例:[https://github.com/cloudwego/eino-examples](https://github.com/cloudwego/eino-examples)
+- 文档:[https://www.cloudwego.io/zh/docs/eino/](https://www.cloudwego.io/zh/docs/eino/)
+
+---
+
+## 关联笔记
+
+- [[Eino/docs/overview/eino_open_source]] — 开源发布文
+- [[Eino/docs/overview/eino_adk0_1]] — ADK 设计模式详解
+- [[Eino/docs/overview/eino_adk_excel_agent]] — Excel Agent 实战
+- [[Eino/quick_start/README]] — 快速开始总览
diff --git a/docs/Eino/docs/core_modules/_index.md b/docs/Eino/docs/core_modules/_index.md
new file mode 100644
index 0000000..6f6a375
--- /dev/null
+++ b/docs/Eino/docs/core_modules/_index.md
@@ -0,0 +1,22 @@
+---
+Description: ""
+date: "2025-07-21"
+lastmod: ""
+tags: []
+title: 核心模块
+weight: 4
+---
+
+Eino 中的核心模块有如下几个部分:
+
+- **Components 组件**:[Eino: Components 组件](/zh/docs/eino/core_modules/components)
+
+Eino 抽象出来的大模型应用中常用的组件,例如 `ChatModel`、`Embedding`、`Retriever` 等,这是实现一个大模型应用搭建的积木,是应用能力的基础,也是复杂逻辑编排时的原子对象。
+
+- **Chain/Graph 编排**:[Eino: Chain/Graph 编排功能](/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction)
+
+多个组件混合使用来实现业务逻辑的串联,Eino 提供 Chain/Graph 的编排方式,把业务逻辑串联的复杂度封装在了 Eino 内部,提供易于理解的业务逻辑编排接口,提供统一的横切面治理能力。
+
+- **Flow 集成工具 (agents)**: [Eino: Flow 集成组件](/zh/docs/eino/core_modules/flow_integration_components)
+
+Eino 把最常用的大模型应用模式封装成简单、易用的工具,让通用场景的大模型应用开发极致简化,目前提供了 `ReAct Agent` 和 `Host Multi Agent`。
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/_index.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/_index.md
new file mode 100644
index 0000000..c4a4520
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/_index.md
@@ -0,0 +1,57 @@
+---
+Description: ""
+date: "2025-07-21"
+lastmod: ""
+tags: []
+title: Chain & Graph & Workflow 编排功能
+weight: 2
+---
+
+在大模型应用中,`Components` 组件是提供 『原子能力』的最小单元,比如:
+
+- `ChatModel` 提供了大模型的对话能力
+- `Embedding` 提供了基于语义的文本向量化能力
+- `Retriever` 提供了关联内容召回的能力
+- `ToolsNode` 提供了执行外部工具的能力
+
+> 详细的组件介绍可以参考: [Eino: Components 组件](/zh/docs/eino/core_modules/components)
+
+一个大模型应用,除了需要这些原子能力之外,还需要根据场景化的业务逻辑,**对这些原子能力进行组合、串联**,这就是 **『编排』**。
+
+大模型应用的开发有其自身典型的特征: 自定义的业务逻辑本身不会很复杂,几乎主要都是对『原子能力』的组合串联。
+
+传统代码开发过程中,业务逻辑用 “代码的执行逻辑” 来表达,迁移到大模型应用开发中时,最直接想到的方法就是 “自行调用组件,自行把结果作为下一组件的输入进行调用”。这样的结果,就是 `代码杂乱`、`很难复用`、`没有切面能力`……
+
+当开发者们追求代码『**优雅**』和『**整洁之道**』时,就发现把传统代码组织方式用到大模型应用中时有着巨大的鸿沟。
+
+Eino 的初衷是让大模型应用开发变得非常简单,就一定要让应用的代码逻辑 “简单” “直观” “优雅” “健壮”。
+
+Eino 对「编排」有着这样的洞察:
+
+- 编排要成为在业务逻辑之上的清晰的一层,**不能让业务逻辑融入到编排中**。
+- 大模型应用的核心是 “对提供原子能力的组件” 进行组合串联,**组件是编排的 “第一公民”**。
+- 抽象视角看编排:编排是在构建一张网络,数据则在这个网络中流动,网络的每个节点都对流动的数据有格式/内容的要求,一个能顺畅流动的数据网络,关键就是 “**上下游节点间的数据格式是否对齐**?”。
+- 业务场景的复杂度会反映在编排产物的复杂性上,只有**横向的治理能力**才能让复杂场景不失控。
+- 大模型是会持续保持高速发展的,大模型应用也是,只有**具备扩展能力的应用才拥有生命力**。
+
+于是,Eino 提供了 “基于 Graph 模型 (node + edge) 的,以**组件**为原子节点的,以**上下游类型对齐**为基础的编排” 的解决方案。
+
+具体来说,实现了如下特性:
+
+- 一切以 “组件” 为核心,规范了业务功能的封装方式,让**职责划分变得清晰**,让**复用**变成自然而然
+ - 详细信息参考:[Eino: Components 组件](/zh/docs/eino/core_modules/components)
+- 业务逻辑复杂度封装到组件内部,编排层拥有更全局的视角,让**逻辑层次变得非常清晰**
+- 提供了切面能力,callback 机制支持了基于节点的**统一治理能力**
+ - 详细信息参考:[Eino: Callback 用户手册](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)
+- 提供了 call option 的机制,**扩展性**是快速迭代中的系统最基本的诉求
+ - 详细信息参考:[Eino: CallOption 能力与规范](/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities)
+- 提供了 “类型对齐” 的开发方式的强化,降低开发者心智负担,把 golang 的**类型安全**特性发挥出来
+ - 详细信息参考:[Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+- 提供了 “**流的自动转换**” 能力,让 “流” 在「编排系统的复杂性来源榜」中除名
+ - 详细信息参考:[Eino 流式编程要点](/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+
+Graph 本身是强大且语义完备的,可以用这项底层几乎绘制出所有的 “数据流动网络”,比如 “分支”、“并行”、“循环”。
+
+但 Graph 并不是没有缺点的,基于 “点” “边” 模型的 Graph 在使用时,要求开发者要使用 `graph.AddXXXNode()` 和 `graph.AddEdge()` 两个接口来创建一个数据通道,强大但是略显复杂。
+
+而在现实的大多数业务场景中,往往仅需要 “按顺序串联” 即可,因此,Eino 封装了接口更易于使用的 `Chain`。Chain 是对 Graph 的封装,除了 “环” 之外,Chain 暴露了几乎所有 Graph 的能力。
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/call_option_capabilities.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
new file mode 100644
index 0000000..c79454c
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
@@ -0,0 +1,306 @@
+---
+Description: ""
+date: "2025-11-20"
+lastmod: ""
+tags: []
+title: CallOption 能力与规范
+weight: 6
+---
+
+**CallOption**: 对 Graph 编译产物进行调用时,直接传递数据给特定的一组节点(Component、Implementation、Node)的渠道
+
+- 和 节点 Config 的区别: 节点 Config 是实例粒度的配置,也就是从实例创建到实例消除,Config 中的值一旦确定就不需要改变了
+- CallOption:是请求粒度的配置,不同的请求,其中的值是不一样的。更像是节点入参,但是这个入参是直接由 Graph 的入口直接传入,而不是上游节点传入。
+ - 举例:给一个 ChatModel 节点传入 Temperature 配置;给一个 Lambda 节点传入自定义 option。
+
+## 组件 CallOption 形态
+
+组件 CallOption 配置,有两个粒度:
+
+- 组件的抽象(Abstract/Interface)统一定义的 CallOption 配置【组件抽象 CallOption】
+- 组件的实现(Type/Implementation)定义的该类型组件专用的 CallOption 配置【组件实现 CallOption】
+
+以 ChatModel 这个 Component 为例,介绍 CallOption 的形态
+
+### Model 抽象与实现的目录
+
+```
+// 抽象所在代码位置
+eino/components/model
+├── interface.go
+├── option.go // Component 抽象粒度的 CallOption 入参
+
+// 抽象实现所在代码位置
+eino-ext/components/model
+├── claude
+│ ├── option.go // Component 的一种实现的 CallOption 入参
+│ └── chatmodel.go
+├── ollama
+│ ├── call_option.go // Component 的一种实现的 CallOption 入参
+│ ├── chatmodel.go
+```
+
+### Model 抽象
+
+如上所述,在定义组件的 CallOption 时,需要区分【组件抽象 CallOption】、【组件实现 CallOption】两种场景。 而是否要提供 【组件实现 CallOption】,则是由 组件抽象 来决定的。
+
+组件抽象提供的 CallOption 扩展能力如下(以 Model 为例,其他组件类似):
+
+```go
+package model
+
+type ChatModel interface {
+ Generate(ctx context.Context, input []*schema.Message, opts ...Option) (*schema.Message, error)
+ Stream(ctx context.Context, input []*schema.Message, opts ...Option) (
+ *schema.StreamReader[*schema.Message], error)
+
+ // BindTools bind tools to the model.
+ // BindTools before requesting ChatModel generally.
+ // notice the non-atomic problem of BindTools and Generate.
+ BindTools(tools []*schema.ToolInfo) error
+}
+
+// 此结构体是【组件抽象CallOption】的统一定义。 组件的实现可根据自己的需要取用【组件抽象CallOption】的信息
+// Options is the common options for the model.
+type Options struct {
+ // Temperature is the temperature for the model, which controls the randomness of the model.
+ Temperature *float32
+ // MaxTokens is the max number of tokens, if reached the max tokens, the model will stop generating, and mostly return an finish reason of "length".
+ MaxTokens *int
+ // Model is the model name.
+ Model *string
+ // TopP is the top p for the model, which controls the diversity of the model.
+ TopP *float32
+ // Stop is the stop words for the model, which controls the stopping condition of the model.
+ Stop []string
+}
+
+// Option is the call option for ChatModel component.
+type Option struct {
+ // 此字段是为【组件抽象CallOption】服务的 apply 方法,例如 WithTemperature
+ // 如果组件抽象不想提供【组件抽象CallOption】,可不提供此字段,同时不提供 GetCommonOptions() 方法
+ apply func(opts *Options)
+
+ // 此字段是为【组件实现CallOption】服务的 apply 方法。并假设 apply 方法为:func(*T)
+ // 如果组件抽象不想提供【组件实现CallOption】,可不提供此字段,同时不提供 GetImplSpecificOptions() 方法
+ implSpecificOptFn any
+}
+
+// WithTemperature is the option to set the temperature for the model.
+func WithTemperature(temperature float32) Option {
+ return Option{
+ apply: func(opts *Options) {
+ opts.Temperature = &temperature
+ },
+ }
+}
+
+// WithMaxTokens is the option to set the max tokens for the model.
+func WithMaxTokens(maxTokens int) Option {
+ return Option{
+ apply: func(opts *Options) {
+ opts.MaxTokens = &maxTokens
+ },
+ }
+}
+
+// WithModel is the option to set the model name.
+func WithModel(name string) Option {
+ return Option{
+ apply: func(opts *Options) {
+ opts.Model = &name
+ },
+ }
+}
+
+// WithTopP is the option to set the top p for the model.
+func WithTopP(topP float32) Option {
+ return Option{
+ apply: func(opts *Options) {
+ opts.TopP = &topP
+ },
+ }
+}
+
+// WithStop is the option to set the stop words for the model.
+func WithStop(stop []string) Option {
+ return Option{
+ apply: func(opts *Options) {
+ opts.Stop = stop
+ },
+ }
+}
+
+// GetCommonOptions extract model Options from Option list, optionally providing a base Options with default values.
+func GetCommonOptions(base *Options, opts ...Option) *Options {
+ if base == nil {
+ base = &Options{}
+ }
+
+ for i := range opts {
+ opt := opts[i]
+ if opt.apply != nil {
+ opt.apply(base)
+ }
+ }
+
+ return base
+}
+
+// 组件实现方基于此方法,封装自己的Option函数:func WithXXX(xxx string) Option{}
+func WrapImplSpecificOptFn[T any](optFn func(*T)) Option {
+ return Option{
+ implSpecificOptFn: optFn,
+ }
+}
+
+// GetImplSpecificOptions provides tool author the ability to extract their own custom options from the unified Option type.
+// T: the type of the impl specific options struct.
+// This function should be used within the tool implementation's InvokableRun or StreamableRun functions.
+// It is recommended to provide a base T as the first argument, within which the tool author can provide default values for the impl specific options.
+func GetImplSpecificOptions[T any](base *T, opts ...Option) *T {
+ if base == nil {
+ base = new(T)
+ }
+
+ for i := range opts {
+ opt := opts[i]
+ if opt.implSpecificOptFn != nil {
+ optFn, ok := opt.implSpecificOptFn.(func(*T))
+ if ok {
+ optFn(base)
+ }
+ }
+ }
+
+ return base
+}
+```
+
+### Claude 实现
+
+[https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/option.go](https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/option.go)
+
+```go
+package claude
+
+import (
+ "github.com/cloudwego/eino/components/model"
+)
+
+type options struct {
+ TopK *int32
+}
+
+func WithTopK(k int32) model.Option {
+ return model.WrapImplSpecificOptFn(func(o *options) {
+ o.TopK = &k
+ })
+}
+```
+
+[https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/claude.go](https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/claude.go)
+
+```go
+func (c *claude) genMessageNewParams(input []*schema.Message, opts ...model.Option) (anthropic.MessageNewParams, error) {
+ if len(input) == 0 {
+ return anthropic.MessageNewParams{}, fmt.Errorf("input is empty")
+ }
+
+ commonOptions := model.GetCommonOptions(&model.Options{
+ Model: &c.model,
+ Temperature: c.temperature,
+ MaxTokens: &c.maxTokens,
+ TopP: c.topP,
+ Stop: c.stopSequences,
+ }, opts...)
+ claudeOptions := model.GetImplSpecificOptions(&options{TopK: c.topK}, opts...)
+
+ // omit mulple lines...
+ return nil, nil
+}
+```
+
+## 编排中的 CallOption
+
+[https://github.com/cloudwego/eino/blob/main/compose/runnable.go](https://github.com/cloudwego/eino/blob/main/compose/runnable.go)
+
+Graph 编译产物是 Runnable
+
+```go
+type Runnable[I, O any] interface {
+ Invoke(ctx context.Context, input I, opts ...Option) (output O, err error)
+ Stream(ctx context.Context, input I, opts ...Option) (output *schema.StreamReader[O], err error)
+ Collect(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output O, err error)
+ Transform(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output *schema.StreamReader[O], err error)
+}
+```
+
+Runnable 各方法均接收 compose.Option 列表。
+
+[https://github.com/cloudwego/eino/blob/main/compose/graph_call_options.go](https://github.com/cloudwego/eino/blob/main/compose/graph_call_options.go)
+
+包括 graph run 整体的配置,各类组件的配置,特定 Lambda 的配置等。
+
+```go
+// Option is a functional option type for calling a graph.
+type Option struct {
+ options []any
+ handler []callbacks.Handler
+
+ paths []*NodePath
+
+ maxRunSteps int
+}
+
+// DesignateNode set the key of the node which will the option be applied to.
+// notice: only effective at the top graph.
+// e.g.
+//
+// embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small"))
+// runnable.Invoke(ctx, "input", embeddingOption.DesignateNode("my_embedding_node"))
+func (o Option) DesignateNode(key ...string) Option {
+ nKeys := make([]*NodePath, len(key))
+ for i, k := range key {
+ nKeys[i] = NewNodePath(k)
+ }
+ return o.DesignateNodeWithPath(nKeys...)
+}
+
+// DesignateNodeWithPath sets the path of the node(s) to which the option will be applied to.
+// You can make the option take effect in the subgraph by specifying the key of the subgraph.
+// e.g.
+// DesignateNodeWithPath({"sub graph node key", "node key within sub graph"})
+func (o Option) DesignateNodeWithPath(path ...*NodePath) Option {
+ o.paths = append(o.paths, path...)
+ return o
+}
+
+// WithEmbeddingOption is a functional option type for embedding component.
+// e.g.
+//
+// embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small"))
+// runnable.Invoke(ctx, "input", embeddingOption)
+func WithEmbeddingOption(opts ...embedding.Option) Option {
+ return withComponentOption(opts...)
+}
+```
+
+compose.Option 可以按需分配给 Graph 中不同的节点。
+
+
+
+```go
+// 所有节点都生效的 call option
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
+
+// 只对特定类型节点生效的 call option
+compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5))
+compiledGraph.Invoke(ctx, input, WithToolOption(WithXXX("xxx"))
+
+// 只对特定节点生效的 call option
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
+
+// 只对特定内部嵌套图或其中节点生效的 Call option
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNodeWithPath(NewNodePath("1", "2"))
+```
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/callback_manual.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/callback_manual.md
new file mode 100644
index 0000000..4ee4d46
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/callback_manual.md
@@ -0,0 +1,738 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Callback 用户手册
+weight: 5
+---
+
+## 解决的问题
+
+Component(包括 Lambda)、Graph 编排共同解决“把业务逻辑定义出来”的问题。而 logging, tracing, metrics, 上屏展示等横切面性质的功能,需要有机制把功能注入到 Component(包括 Lambda)、Graph 中。
+
+另一方面,用户可能想拿到某个具体 Component 实现的执行过程中的中间信息,比如 VikingDBRetriever 额外给出查询的 DB Name,ArkChatModel 额外给出请求的 temperature 等参数。需要有机制把中间状态透出。
+
+Callbacks 支持“**横切面功能注入**”和“**中间状态透出**”,具体是:用户提供、注册“function”(Callback Handler),Component 和 Graph 在固定的“时机”(或者说切面、位点)回调这些 function,给出对应的信息。
+
+## 核心概念
+
+核心概念串起来,就是:Eino 中的 Component 和 Graph 等**实体**,在固定的**时机** (Callback Timing),回调用户提供的 **function** (Callback Handler),并把**自己是谁** (RunInfo),以及**当时发生了什么** (Callback Input & Output) 传出去。
+
+### 触发实体
+
+Component(包括官方定义的组件类型和 Lambda),Graph Node(以及 Chain/Workflow Node),Graph 自身(以及 Chain/Workflow)。这三类实体,都有横切面功能注入、中间状态透出的需求,因此都会触发 callback。具体见下面的“[触发方式](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)”一节。
+
+### 触发时机
+
+```go
+// CallbackTiming enumerates all the timing of callback aspects.
+type CallbackTiming = callbacks.CallbackTiming
+
+const (
+ TimingOnStart CallbackTiming = iota // 进入并开始执行
+ TimingOnEnd // 成功完成即将 return
+ TimingOnError // 失败并即将 return err
+ TimingOnStartWithStreamInput // OnStart,但是输入是 StreamReader
+ TimingOnEndWithStreamOutput // OnEnd,但是输出是 StreamReader
+)
+```
+
+不同的触发实体,在不同场景下,是触发 OnStart 还是 OnStartWithStreamInput (OnEnd/OnEndWithStreamOutput 同理),具体的规则,详见下面的“[触发方式](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)”一节。
+
+### Callback Handler
+
+```go
+type Handler interface {
+ OnStart(ctx context.Context, info *RunInfo, input CallbackInput) context.Context
+ OnEnd(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context
+ OnError(ctx context.Context, info *RunInfo, err error) context.Context
+ OnStartWithStreamInput(ctx context.Context, info *RunInfo,
+ input *schema.StreamReader[CallbackInput]) context.Context
+ OnEndWithStreamOutput(ctx context.Context, info *RunInfo,
+ output *schema.StreamReader[CallbackOutput]) context.Context
+}
+```
+
+一个 Handler 是一个实现了上面 5 个方法(对应 5 个触发时机)的结构体。每个方法都会接收三个信息:
+
+- Context: 用于**接收同一个 Handler 的前序触发时机**可能设置的定制信息。
+- RunInfo: 触发回调的实体元信息。
+- Input/Output/InputStream/OutputStream: 触发回调时的业务信息。
+
+并都会返回新的 Context:用于**同一个 Handler 的不同触发时机之间**传递信息。
+
+如果一个 Handler,不想关注所有的 5 个触发时机,只想关注一部分,比如只关注 OnStart,建议使用 `NewHandlerBuilder().OnStartFn(...).Build()`。如果不想关注所有的组件类型,只想关注特定组件,比如 ChatModel,建议使用 `NewHandlerHelper().ChatModel(...).Handler()`,可以只接收 ChatModel 的回调并拿到一个具体类型的 CallbackInput/CallbackOutput。具体见“[Handler 实现方式](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)”一节。
+
+不同 Handler 之间,触发顺序**没有**保证。
+
+### RunInfo
+
+描述了触发 Callback 的实体自身的元信息。
+
+```go
+// RunInfo contains information about the running component that triggers callbacks.
+type RunInfo struct {
+ Name string // the 'Name' with semantic meaning for the running component, specified by end-user
+ Type string // the specific implementation 'Type' of the component, e.g. 'OpenAI'
+ Component components.Component // the component abstract type, e.g. 'ChatModel'
+}
+```
+
+- Name:有业务含义的名称,需用户指定,不指定就是空字符串。对不同的触发实体:
+ - Component:在 Graph 中时,用 Node Name。在 Graph 外单独的使用时,用户手动设置。详见“注入 RunInfo” 和 “单独使用 Component”
+ - Graph Node:用 Node Name `func WithNodeName(n string) GraphAddNodeOpt`
+ - Graph 自身:
+ - 顶层图用 Graph Name `func WithGraphName(graphName string) GraphCompileOption`
+ - 内部嵌套图,会用加入到上级图时添加的 Node Name
+- Type:组件具体实现来规定:
+ - 有接口的 Component:如果实现了 Typer 接口,用 GetType() 方法的结果。否则用反射获取 Struct/Func 名。
+ - Lambda:如果用 `func WithLambdaType(t string) LambdaOpt` 指定了 Type,用这个,否则是空字符串。
+ - Graph Node:用内部 Component/Lambda/Graph 的值。
+ - Graph 自身:空字符串。
+- Component:
+ - 有接口的 Component:是啥接口,就是啥
+ - Lambda:固定值 Lambda
+ - Graph Node: 用内部的 Component/Lambda/Graph 的值。
+ - Graph 自身:固定值 Graph / Chain / Workflow. (之前曾有 StateGraph / StateChain ,现已整合到 Graph / Chain 中)
+
+### Callback Input & Output
+
+本质是任意类型,因为不同的 Component 的输入输出、内部状态完全不同。
+
+```go
+type CallbackInput any
+type CallbackOutput any
+```
+
+具体到某个组件,有更具体的类型,比如 Chat Model
+
+```go
+// CallbackInput is the input for the model callback.
+type CallbackInput struct {
+ // Messages is the messages to be sent to the model.
+ Messages []*schema.Message
+ // Tools is the tools to be used in the model.
+ Tools []*schema.ToolInfo
+ // Config is the config for the model.
+ Config *Config
+ // Extra is the extra information for the callback.
+ Extra map[string]any
+}
+
+// CallbackOutput is the output for the model callback.
+type CallbackOutput struct {
+ // Message is the message generated by the model.
+ Message *schema.Message
+ // Config is the config for the model.
+ Config *Config
+ // TokenUsage is the token usage of this request.
+ TokenUsage *TokenUsage
+ // Extra is the extra information for the callback.
+ Extra map[string]any
+}
+```
+
+在 Chat Model 的具体实现,比如 OpenAI Chat Model 中,建议组件作者向 Callback Handler 中传入具体的 Input/Output 类型,而不是 Any。这样可以透出更具体的、定制化的中间状态信息。
+
+如果是 Graph Node 来触发 Callback,因为 Node 拿不到组件内部中间状态信息,只能拿到组件接口中规定的输入和输出,所以给 Callback Handler 的只能是这些。对 Chat Model,就是 []*schema.Message 和 *schema.Message。
+
+Graph 自身触发 Callback 时,输入输出就是 Graph 整体的输入和输出。
+
+## 注入 Handler
+
+Handler 需要注入到 Context 中才能被触发。
+
+### 全局注入 Handler
+
+通过 `callbacks.AppendGlobalHandlers` 注入全局的 Handler。注入后,所有的触发回调行为,都会自动触发这些全局的 Handler。典型的场景是 tracing,logging 等全局一致、业务场景无关的功能。
+
+不是并发安全的。建议在服务初始化时注入一次。
+
+### 向 Graph 中注入 Handler
+
+通过 `compose.WithCallbacks` 在 graph 运行时注入 Handler,这些 Handler 会在 graph 的本次运行整体上生效,包括 Graph 内各 Node 和 Graph 自身(以及各内嵌的 graph)。
+
+通过 `compose.WithCallbacks(...).DesignateNode(...)` 向顶层 Graph 的某个 Node 注入 Handler。当这个 Node 自身是个内嵌的 Graph 时,会注入到这个内嵌 Graph 自身和其内部的各 Node。
+
+通过 `compose.WithCallbacks(...).DesignateNodeWithPath(...)` 向内部嵌套的 Graph 的某个 Node 注入 Handler。
+
+### 在 Graph 外注入 Handler
+
+不想使用 Graph,但却想使用 Callback,则:
+
+通过 `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` 获取一个新的 Context 并注入 Handlers 以及 RunInfo。
+
+### Handler 继承
+
+与子 Context 继承父 Context 中的所有 Values 相同,子 Context 也会继承父 Context 中的所有 Handlers。举个例子,Graph 运行时传入的 Context 中如果已经有了 Handler,则这些 Handlers 都会被整个 Graph 的这次运行继承和生效。
+
+## 注入 RunInfo
+
+RunInfo 也需要注入到 Context 中,才会在触发回调时给到 Handler。
+
+### Graph 托管 RunInfo
+
+Graph 会为内部所有的 Node 自动注入 RunInfo。机制是每个 Node 的运行,都是一个新的子 Context,Graph 向这个新的 Context 中注入对应 Node 的 RunInfo。
+
+### 在 Graph 外注入 RunInfo
+
+不想使用 Graph,但却想使用 Callback,则:
+
+通过 `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` 获取一个新的 Context 并注入 Handlers 以及 RunInfo。
+
+通过 `ReuseHandlers(ctx context.Context, info *RunInfo)` 来获取一个新的 Context,复用之前 Context 中的 Handler,并设置新的 RunInfo。
+
+## 触发方式
+
+
+
+### 组件实现内部触发(Component Callback)
+
+在组件实现的代码中,调用 callbacks 包中的 `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), ``OnEndWithStreamOutput``()`。以 Ark 的 ChatModel 实现为例,在 Generate 方法中:
+
+```go
+func (cm *ChatModel) Generate(ctx context.Context, in []*schema.Message, opts ...fmodel.Option) (
+ outMsg *schema.Message, err error) {
+
+ defer func() {
+ if err != nil {
+ _ = callbacks.OnError(ctx, err)
+ }
+ }()
+
+ // omit multiple lines... instantiate req conf
+
+ ctx = callbacks.OnStart(ctx, &fmodel.CallbackInput{
+ Messages: in,
+ Tools: append(cm.rawTools), // join tool info from call options
+ ToolChoice: nil, // not support in api
+ Config: reqConf,
+ })
+
+ // omit multiple lines... invoke Ark chat API and get the response
+
+ _ = callbacks.OnEnd(ctx, &fmodel.CallbackOutput{
+ Message: outMsg,
+ Config: reqConf,
+ TokenUsage: toModelCallbackUsage(outMsg.ResponseMeta),
+ })
+
+ return outMsg, nil
+}
+```
+
+在 Stream 方法中:
+
+```go
+func (cm *ChatModel) Stream(ctx context.Context, in []*schema.Message, opts ...fmodel.Option) ( // byted_s_too_many_lines_in_func
+ outStream *schema.StreamReader[*schema.Message], err error) {
+
+ defer func() {
+ if err != nil {
+ _ = callbacks.OnError(ctx, err)
+ }
+ }()
+
+ // omit multiple lines... instantiate req conf
+
+ ctx = callbacks.OnStart(ctx, &fmodel.CallbackInput{
+ Messages: in,
+ Tools: append(cm.rawTools), // join tool info from call options
+ ToolChoice: nil, // not support in api
+ Config: reqConf,
+ })
+
+ // omit multiple lines... make request to Ark API and convert response stream to StreamReader[model.*CallbackOutput]
+
+ _, sr = callbacks.OnEndWithStreamOutput(ctx, sr)
+
+ return schema.StreamReaderWithConvert(sr,
+ func(src *fmodel.CallbackOutput) (*schema.Message, error) {
+ if src.Message == nil {
+ return nil, schema.ErrNoValue
+ }
+
+ return src.Message, nil
+ },
+ ), nil
+}
+```
+
+可以看到 Generate 调用时,触发的是 OnEnd,而 Stream 调用时,触发的是 OneEndWithStreamOutput:
+
+组件实现内部触发 Callbacks 时:
+
+- **当组件输入为 StreamReader 时,触发 OnStartWithStreamInput,否则触发 OnStart**
+- **当组件输出为 StreamReader 时,触发 OnEndWithStreamOutput,否则触发 OnEnd**
+
+内部实现了 callback 触发的组件,应当实现 Checker 接口,IsCallbacksEnabled 返回 true,向外部传达“我内部实现了 callback 触发”的信息:
+
+```go
+// Checker tells callback aspect status of component's implementation
+// When the Checker interface is implemented and returns true, the framework will not start the default aspect.
+// Instead, the component will decide the callback execution location and the information to be injected.
+type Checker interface {
+ IsCallbacksEnabled() bool
+}
+```
+
+如果一个组件实现,没有实现 Checker 接口,或者 IsCallbacksEnabled 返回 false,可以认为该组件内部没有触发回调,需要 Graph Node 来负责注入和触发(在 Graph 内使用时)。
+
+### Graph Node 触发(Node Callback)
+
+当一个 Component 被编排入 Graph 时,成为一个 Node。这时,如果 Component 自身会触发 callback,Node 就复用 Component 的 callback 处理。否则,Node 会在 Component 外面埋上 callback handler 触发点位。这些点位与 Component 自身的流式范式对应。比如一个 ChatModelNode,会在 Generate 方法外面埋上 OnStart/OnEnd/OnError,同时会在 Stream 方法外面埋上 OnStart/OnEndWithStreamOutput/OnError。
+
+在 Graph 运行时,各组件会以 Invoke 或 Transform 范式运行,又会根据组件具体实现的业务流式范式,调用对应的组件方法。比如 Graph 以 Invoke 运行,Chat Model Node 会以 Invoke 运行,调用 Generate 方法。而当 Graph 以 Stream 运行,Chat Model Node 会以 Transform 运行,但 Chat Model 的业务流式范式中没有 Transform,会自动降级成调用 Stream 方法。因此:
+
+**Graph Node 具体触发哪个位点(OnStart 还是 OnStartWithStreamInput),取决于组件实现的业务流式范式和 Graph 运行方式两个因素。**
+
+关于 Eino 流式编程的详细介绍,参见 [Eino 流式编程要点](/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+
+### Graph 自身触发(Graph Callback)
+
+Graph 在自身的开始、结束、err 的时机触发 Callback Handler。如果 Graph 以 Invoke 形式调用,触发 OnStart/OnEnd/OnError。如果以 Stream/Collect/Transform 形式调用,触发 OnStartWithStreamInput/OnEndWithStreamOutput/OnError。这是因为 **Graph 内部会始终以 Invoke 或 Transform 执行**。参见 [Eino 流式编程要点](/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+
+值得注意的是:graph 也是 component 的一种,因此 graph callback 也是 component callback 的一种特殊形式。根据 Node Callback 的定义,当 Node 内部的 component 实现了对触发时机的感知和处理时,Node 会直接复用 Component 的实现,不会再实现 Node Callback。这意味着当一个 graph 通过 AddGraphNode 的方式加入到另外一个 Graph 中作为一个 Node 时,这个 Node 会复用内部 graph 的 graph callback。
+
+## 解析 Callback Input & Output
+
+从上文得知,Callback Input & Output 的底层是 Any,只是不同组件类型在具体触发回调时,可能会传入自己特定的类型。并且 Callback Handler 的接口定义中,各方法的入参也是 Any 类型的 Callback Input & Output。
+
+因此,具体的 Handler 实现中,需要做两个事情:
+
+1. 根据 RunInfo 判断当前触发回调的是哪个组件类型,比如 RunInfo.Component == "ChatModel",或者 RunInfo.Type == "xxx Chat Model"。
+2. 把 any 类型的 Callback Input & Output 转成对应的具体类型,以 RunInfo.Component == "ChatModel" 为例:
+
+```go
+// ConvCallbackInput converts the callback input to the model callback input.
+func ConvCallbackInput(src callbacks.CallbackInput) *CallbackInput {
+ switch t := src.(type) {
+ case *CallbackInput: // when callback is triggered within component implementation, the input is usually already a typed *model.CallbackInput
+ return t
+ case []*schema.Message: // when callback is injected by graph node, not the component implementation itself, the input is the input of Chat Model interface, which is []*schema.Message
+ return &CallbackInput{
+ Messages: t,
+ }
+ default:
+ return nil
+ }
+}
+
+// ConvCallbackOutput converts the callback output to the model callback output.
+func ConvCallbackOutput(src callbacks.CallbackOutput) *CallbackOutput {
+ switch t := src.(type) {
+ case *CallbackOutput: // when callback is triggered within component implementation, the output is usually already a typed *model.CallbackOutput
+ return t
+ case *schema.Message: // when callback is injected by graph node, not the component implementation itself, the output is the output of Chat Model interface, which is *schema.Message
+ return &CallbackOutput{
+ Message: t,
+ }
+ default:
+ return nil
+ }
+}
+```
+
+如果 Handler 里面需要增加 switch case 来判断 RunInfo.Component,并且对每一个 case,需要调对应的转换函数把 Any 转成具体类型,确实有些复杂。为了减少写胶水代码的重复劳动,我们提供了两种实现 Handler 的便捷工具函数。
+
+## Handler 实现方式
+
+除了直接实现 Handler 接口外,Eino 提供了两种 Handler 的便捷实现工具。
+
+### HandlerHelper
+
+如果用户的 Handler 只关注特定类型的组件,比如 ReactAgent 的场景,只关注 ChatModel 和 Tool,建议使用 HandlerHelper 来快速创建具体类型的 Callback Handler:
+
+```go
+import ucb "github.com/cloudwego/eino/utils/callbacks"
+
+handler := ucb.NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler()
+```
+
+其中 modelHandler 是 Chat Model 组件对 callback handler 的进一步封装:
+
+```go
+// from package utils/callbacks
+
+// ModelCallbackHandler is the handler for the model callback.
+type ModelCallbackHandler struct {
+ OnStart func(ctx context.Context, runInfo *callbacks.RunInfo, input *model.CallbackInput) context.Context
+ OnEnd func(ctx context.Context, runInfo *callbacks.RunInfo, output *model.CallbackOutput) context.Context
+ OnEndWithStreamOutput func(ctx context.Context, runInfo *callbacks.RunInfo, output *schema.StreamReader[*model.CallbackOutput]) context.Context
+ OnError func(ctx context.Context, runInfo *callbacks.RunInfo, err error) context.Context
+}
+```
+
+上面的 ModelCallbackHandler,封装了三个操作:
+
+1. 不再需要判断 RunInfo.Component 来选择属于 ChatModel 触发的回调,而是已经自动做了过滤。
+2. 只要求实现 Chat Model 这个组件支持的触发时机,这里去掉了不支持的 OnStartWithStreamInput。同时,如果用户只关注 Chat Model 支持的四个时机的某几个,比如只有 OnStart,也可以只实现 OnStart。
+3. Input / Output 不再是 Any 类型,而是已经转化好的 model.CallbackInput, model.CallbackOutput。
+
+HandlerHelper 支持全部的官方组件,目前的列表是:ChatModel, ChatTemplate, Retriever, Indexer, Embedding, Document.Loader, Document.Transformer, Tool, ToolsNode.
+
+针对 Lambda,Graph,Chain 这些输入输出类型不确定的“组件”,也可以使用 HandlerHelper,但是只能做到上面的第 1 点,即按照组件类型做自动的过滤,2、3 点依然需要用户自己实现:
+
+```go
+import ucb "github.com/cloudwego/eino/utils/callbacks"
+
+handler := ucb.NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)...Handler()
+```
+
+这时,NewHandlerHelper().Lambda() 需要传入 callbacks.Handler 可以用下面的 HandlerBuilder 来实现。
+
+### HandlerBuilder
+
+如果用户的 Handler 需要关注多个组件类型,但却只需要关注部分的触发时机,可以使用 HandlerBuilder:
+
+```go
+import "github.com/cloudwego/eino/callbacks"
+
+handler := callbacks.NewHandlerBuilder().OnStartFn(fn)...Build()
+```
+
+## 最佳实践
+
+### 在 Graph 中使用
+
+- 积极使用 Global Handlers,注册始终生效的 Handlers。
+
+```go
+package main
+
+import (
+ "context"
+ "log"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/compose"
+)
+
+func main() {
+ // Build a simple global handler
+ handler := callbacks.NewHandlerBuilder().
+ OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
+ log.Printf("[Global Start] component=%s name=%s input=%T", info.Component, info.Name, input)
+ return ctx
+ }).
+ OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
+ log.Printf("[Global End] component=%s name=%s output=%T", info.Component, info.Name, output)
+ return ctx
+ }).
+ OnErrorFn(func(ctx context.Context, info *callbacks.RunInfo, err error) context.Context {
+ log.Printf("[Global Error] component=%s name=%s err=%v", info.Component, info.Name, err)
+ return ctx
+ }).
+ Build()
+
+ // Register as global callbacks (applies to all subsequent runs)
+ callbacks.AppendGlobalHandlers(handler)
+
+ // Example graph usage; the global handler will be invoked automatically
+ g := compose.NewGraph[string, string]()
+ // ... add nodes/edges ...
+ r, _ := g.Compile(context.Background())
+ _, _ = r.Invoke(context.Background(), "hello") // triggers global callbacks
+}
+```
+
+- 通过 WithHandlers option 在运行时注入 Handler,通过 DesignateNode 或 DesignateNodeByPath 指定生效的 Node / 嵌套的内部 Graph / 内部 Graph 的 Node。
+
+```go
+package main
+
+import (
+ "context"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ top := compose.NewGraph[map[string]any, []*schema.Message]()
+ sub := compose.NewGraph[map[string]any, []*schema.Message]()
+ _ = sub.AddChatTemplateNode("tmpl_nested", prompt.FromMessages(schema.FString, schema.UserMessage("Hello, {name}!")))
+ _ = sub.AddEdge(compose.START, "tmpl_nested")
+ _ = sub.AddEdge("tmpl_nested", compose.END)
+ _ = top.AddGraphNode("sub_graph", sub)
+ _ = top.AddEdge(compose.START, "sub_graph")
+ _ = top.AddEdge("sub_graph", compose.END)
+ r, _ := top.Compile(ctx)
+
+ optGlobal := compose.WithCallbacks(
+ callbacks.NewHandlerBuilder().OnEndFn(func(ctx context.Context, _ *callbacks.RunInfo, _ callbacks.CallbackOutput) context.Context { return ctx }).Build(),
+ )
+ optNode := compose.WithCallbacks(
+ callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, _ *callbacks.RunInfo, _ callbacks.CallbackInput) context.Context { return ctx }).Build(),
+ ).DesignateNode("sub_graph")
+ optNested := compose.WithChatTemplateOption(
+ prompt.WrapImplSpecificOptFn(func(_ *struct{}) {}),
+ ).DesignateNodeWithPath(
+ compose.NewNodePath("sub_graph", "tmpl_nested"),
+ )
+
+ _, _ = r.Invoke(ctx, map[string]any{"name": "Alice"}, optGlobal, optNode, optNested)
+}
+```
+
+### 在 Graph 外使用
+
+这个场景是:不使用 Graph/Chain/Workflow 等编排能力,单独用代码去调用 ChatModel/Tool/Lambda 等各种组件,且希望这些组件能成功触发 Callback Handlers。
+
+此场景需要用户解决的问题是:手动设置正确的 RunInfo 和 Handlers,因为没有 Graph 来帮助用户自动设置 RunInfo 和 Handlers 了。
+
+完整示例:
+
+```go
+package main
+
+import (
+ "context"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/compose"
+)
+
+func innerLambda(ctx context.Context, input string) (string, error) {
+ // 作为 ComponentB 的实现方:进入组件时补默认 RunInfo(Name 无法给默认值)
+ ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda)
+ ctx = callbacks.OnStart(ctx, input)
+ out := "inner:" + input
+ ctx = callbacks.OnEnd(ctx, out)
+ return out, nil
+}
+
+func outerLambda(ctx context.Context, input string) (string, error) {
+ // 作为 ComponentA 的实现方:进入组件时补默认 RunInfo
+ ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda)
+ ctx = callbacks.OnStart(ctx, input)
+
+ // 推荐:调用前替换 RunInfo,确保内层组件拿到正确的 name/type/component
+ ctxInner := callbacks.ReuseHandlers(ctx,
+ &callbacks.RunInfo{Name: "ComponentB", Type: "Lambda", Component: compose.ComponentOfLambda},
+ )
+ out1, _ := innerLambda(ctxInner, input) // 内层 RunInfo.Name = "ComponentB"
+
+ // 未替换:框架清空 RunInfo,只能靠 EnsureRunInfo 补默认值(Name 为空)
+ out2, _ := innerLambda(ctx, input) // 内层 RunInfo.Name == ""
+
+ final := out1 + "|" + out2
+ ctx = callbacks.OnEnd(ctx, final)
+ return final, nil
+}
+
+func main() {
+ // 在 graph 外单独使用组件:初始化 RunInfo 与 Handlers
+ h := callbacks.NewHandlerBuilder().Build()
+ ctx := callbacks.InitCallbacks(context.Background(),
+ &callbacks.RunInfo{Name: "ComponentA", Type: "Lambda", Component: compose.ComponentOfLambda},
+ h,
+ )
+
+ _, _ = outerLambda(ctx, "ping")
+}
+```
+
+对上面的样例代码做下说明:
+
+- 初始化:在 graph/chain 外使用组件时,用 InitCallbacks 设置首个 RunInfo 与 Handlers ,让后续组件执行能拿到完整回调上下文。
+- 内部调用:在组件 A 内部调用组件 B 前,用 ReuseHandlers 替换 RunInfo (保留原有 handlers),确保 B 的回调拿到正确的 Type/Component/Name 。
+- 不替换的后果:Eino 在一组 Callbacks 完整触发后,会清空当前 ctx 中的 RunInfo,此时因为 RunInfo 为空,Eino 就不再会触发 Callbacks;组件 B 的开发者只能在自身实现里用 EnsureRunInfo 补 Type/Component 的默认值,来确保 RunInfo 非空且大致正确,从而能成功触发 Callbacks。但无法给出合理 Name ,因此 RunInfo.Name 会是空字符串。
+
+### 组件嵌套使用
+
+场景:在一个组件,比如 Lambda 内,手动调用另外一个组件,比如 ChatModel。
+
+这时,如果外层的组件的 ctx 中有 callback handler,因为这个 ctx 也会传入内部的组件,所以内部的组件也会收到同样的 callback handler。
+
+按“是否希望内部组件触发 callback”区分:
+
+1. 希望触发:基本等同于上面一小节的情况,建议通过 `ReuseHandlers` 来手动为内部组件设置 `RunInfo`。
+
+```go
+package main
+
+import (
+ "context"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/components"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+)
+
+// 外层 Lambda,在内部手动调用 ChatModel
+func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda {
+ return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ // 1) 复用外层 handlers,并为内部组件显式设置 RunInfo
+ innerCtx := callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{
+ Type: "InnerCM", // 可自定义
+ Component: components.ComponentOfChatModel, // 标注组件类型
+ Name: "inner-chat-model", // 可自定义
+ })
+
+ // 2) 构造输入消息
+ msgs := []*schema.Message{{Role: schema.User, Content: input}}
+
+ // 3) 调用 ChatModel(内部会触发相应的回调)
+ out, err := cm.Generate(innerCtx, msgs)
+ if err != nil {
+ return "", err
+ }
+ return out.Content, nil
+ })
+}
+```
+
+上面的代码假设了“内部的 ChatModel 的 Generate 方法内部,已经调用了 OnStart,OnEnd,OnError 这些方法”。如果没有,则需要在外部组件内部“替内部组件”调用这些方法:
+
+```go
+func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda {
+ return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ // 复用外层 handlers,并为内部组件显式设置 RunInfo
+ ctx = callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{
+ Type: "InnerCM",
+ Component: components.ComponentOfChatModel,
+ Name: "inner-chat-model",
+ })
+
+ // 构造输入消息
+ msgs := []*schema.Message{{Role: schema.User, Content: input}}
+
+ // 显式触发 OnStart
+ ctx = callbacks.OnStart(ctx, msgs)
+
+ // 调用 ChatModel
+ resp, err := cm.Generate(ctx, msgs)
+ if err != nil {
+ // 显式触发 OnError
+ ctx = callbacks.OnError(ctx, err)
+ return "", err
+ }
+
+ // 显式触发 OnEnd
+ ctx = callbacks.OnEnd(ctx, resp)
+
+ return resp.Content, nil
+ })
+}
+```
+
+1. 不希望触发:这里假定内部组件实现了 `IsCallbacksEnabled()` 且返回 true,并且在内部调用了 `EnsureRunInfo`。这时默认内部 callbacks 会触发。如不希望触发,最简单的办法是去掉 ctx 中的 handler,比如为内部组件传一个新的 ctx:
+
+ ```go
+ package main
+
+ import (
+ "context"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+ )
+
+ func OuterLambdaNoCallbacks(cm model.BaseChatModel) *compose.Lambda {
+ return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ // 使用一个全新的 ctx,不复用外层的 handlers
+ innerCtx := context.Background()
+
+ msgs := []*schema.Message{{Role: schema.User, Content: input}}
+ out, err := cm.Generate(innerCtx, msgs)
+ if err != nil {
+ return "", err
+ }
+ return out.Content, nil
+ })
+ }
+ ```
+
+ 1. 但有时用户可能希望“只不触发某个特定的 callback handlers,但是还触发其他的 callback handlers”。建议的使用姿势是在这个 callback handler 中加代码,按 RunInfo 过滤掉内部组件:
+
+```go
+package main
+
+import (
+ "context"
+ "log"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/components"
+ "github.com/cloudwego/eino/compose"
+)
+
+// 一个按 RunInfo 过滤的 handler:对内部 ChatModel(Type=InnerCM,Name=inner-chat-model)不做任何处理
+func newSelectiveHandler() callbacks.Handler {
+ return callbacks.
+ NewHandlerBuilder().
+ OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
+ if info != nil && info.Component == components.ComponentOfChatModel &&
+ info.Type == "InnerCM" && info.Name == "inner-chat-model" {
+ // 过滤目标:内部 ChatModel,直接返回,不做处理
+ return ctx
+ }
+ log.Printf("[OnStart] %s/%s (%s)", info.Type, info.Name, info.Component)
+ return ctx
+ }).
+ OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
+ if info != nil && info.Component == components.ComponentOfChatModel &&
+ info.Type == "InnerCM" && info.Name == "inner-chat-model" {
+ // 过滤目标:内部 ChatModel,直接返回,不做处理
+ return ctx
+ }
+ log.Printf("[OnEnd] %s/%s (%s)", info.Type, info.Name, info.Component)
+ return ctx
+ }).
+ Build()
+}
+
+// 组合示例:外层调用希望触发,特定 handler 通过 RunInfo 过滤掉内部 ChatModel
+func Example(cm model.BaseChatModel) (compose.Runnable[string, string], error) {
+ handler := newSelectiveHandler()
+
+ chain := compose.NewChain[string, string]().
+ AppendLambda(OuterLambdaCallsChatModel(cm)) // 内部会 ReuseHandlers + RunInfo
+
+ return chain.Compile(
+ context.Background(),
+ // 挂载 handler(也可结合全局 handlers)
+ compose.WithCallbacks(handler),
+ )
+}
+```
+
+### Handler 内读写 input & output
+
+Input & output 在 graph 中流转时,是直接变量赋值。如下图所示,NodeA.Output, NodeB.Input, NodeC.Input, 以及各个 Handler 中拿到的 input & output,如果是结构体指针或 Map 等引用类型,则都是同一份数据。因此,无论在 Node 内还是 Handler 内,都不建议修改 Input & Output,会产生并发问题:即使同步情况下,Node B 和 Node C 有并发,导致内部的 handler1 和 handler2 有并发。存在异步处理逻辑时,并发的可能场景更多。
+
+
+
+在流传递的场景,所有下游节点和 handler 中的输入流,都是 StreamReader.Copy(n) 得到的流,可相互独立的读取流。但是,流中的每个 chunk,是直接变量赋值,如果 chunk 是结构体指针或 Map 等引用类型,各个 Copy 后的流读到的是同一份数据。因此,在 Node 和 Handler 内,同样不建议修改流的 chunk,有并发问题。
+
+
+
+### Handler 间传递信息
+
+同一个 Handler 的不同时机之间,可通过 ctx 传递信息,如 OnStart 中通过 context.WithValue 返回一个新的 context,在 OnEnd 中从 context 中再取出这个 value。
+
+不同 Handler 之间,没有执行顺序的保证,因此不建议通过上面的机制在不同 Handler 间传递信息。本质上是无法保证某一个 Handler 返回的 context,一定会进入下一个 Handler 的函数执行中。
+
+如果需要在不同 Handler 之间传递信息,建议的方式是在最外层的 context(如 graph 执行时传入的 context)中,设置一个全局的、请求维度的变量作为公共信息的存取空间,在各个 Handler 中按需读取和更新这个公共变量。用户需要自行保证这个公共变量的并发安全。
+
+### 流切记要 Close
+
+以存在 ChatModel 这种具有真流输出的节点为例,当存在 Callback 切面时,ChatModel 的输出流:
+
+- 既要被下游节点作为输入来消费,又要被 Callback 切面来消费
+- 一个流中的一个帧(Chunk),只能被一个消费方消费到,即流不是广播模型
+
+所以此时需要将流进行复制,其复制关系如下:
+
+
+
+- 如果其中一个 Callback n 没有 Close 对应的流,可能导致原始 Stream 无法 Close 和释放资源。
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
new file mode 100644
index 0000000..24d85db
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
@@ -0,0 +1,675 @@
+---
+Description: ""
+date: "2026-01-20"
+lastmod: ""
+tags: []
+title: Chain/Graph 编排介绍
+weight: 1
+---
+
+> 本文所有代码样例都在:[https://github.com/cloudwego/eino-examples/tree/main/compose](https://github.com/cloudwego/eino-examples/tree/main/compose)
+
+## Graph 编排
+
+### Graph
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+)
+
+const (
+ nodeOfModel = "model"
+ nodeOfPrompt = "prompt"
+)
+
+func main() {
+ ctx := context.Background()
+ g := compose.NewGraph[map[string]any, *schema.Message]()
+
+ pt := prompt.FromMessages(
+ schema.FString,
+ schema.UserMessage("what's the weather in {location}?"),
+ )
+
+ _ = g.AddChatTemplateNode(nodeOfPrompt, pt)
+ _ = g.AddChatModelNode(nodeOfModel, &mockChatModel{}, compose.WithNodeName("ChatModel"))
+ _ = g.AddEdge(compose.START, nodeOfPrompt)
+ _ = g.AddEdge(nodeOfPrompt, nodeOfModel)
+ _ = g.AddEdge(nodeOfModel, compose.END)
+
+ r, err := g.Compile(ctx)
+ if err != nil {
+ panic(err)
+ }
+
+ in := map[string]any{"location": "beijing"}
+ ret, err := r.Invoke(ctx, in)
+ fmt.Println("invoke result: ", ret)
+
+ // stream
+ s, err := r.Stream(ctx, in)
+ if err != nil {
+ panic(err)
+ }
+
+ defer s.Close()
+ for {
+ chunk, err := s.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ panic(err)
+ }
+
+ fmt.Println("stream chunk: ", chunk)
+ }
+}
+
+type mockChatModel struct{}
+
+func (m *mockChatModel) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) {
+ return schema.AssistantMessage("the weather is good", nil), nil
+}
+
+func (m *mockChatModel) Stream(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) {
+ sr, sw := schema.Pipe[*schema.Message](0)
+ go func() {
+ defer sw.Close()
+ sw.Send(schema.AssistantMessage("the weather is", nil), nil)
+ sw.Send(schema.AssistantMessage("good", nil), nil)
+ }()
+ return sr, nil
+}
+
+func (m *mockChatModel) BindTools(tools []*schema.ToolInfo) error {
+ panic("implement me")
+}
+```
+
+### ToolCallAgent
+
+```bash
+go get github.com/cloudwego/eino-ext/components/model/openai@latest
+go get github.com/cloudwego/eino@latest
+```
+
+```go
+package main
+
+import (
+ "context"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+
+ "github.com/cloudwego/eino-examples/internal/gptr"
+ "github.com/cloudwego/eino-examples/internal/logs"
+)
+
+func main() {
+
+ openAIBaseURL := os.Getenv("OPENAI_BASE_URL")
+ openAIAPIKey := os.Getenv("OPENAI_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+
+ ctx := context.Background()
+
+ callbacks.AppendGlobalHandlers(&loggerCallbacks{})
+
+ // 1. create an instance of ChatTemplate as 1st Graph Node
+ systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的`
+ chatTpl := prompt.FromMessages(schema.FString,
+ schema.SystemMessage(systemTpl),
+ schema.MessagesPlaceholder("message_histories", true),
+ schema.UserMessage("{user_query}"),
+ )
+
+ modelConf := &openai.ChatModelConfig{
+ BaseURL: openAIBaseURL,
+ APIKey: openAIAPIKey,
+ ByAzure: true,
+ Model: modelName,
+ Temperature: gptr.Of(float32(0.7)),
+ APIVersion: "2024-06-01",
+ }
+
+ // 2. create an instance of ChatModel as 2nd Graph Node
+ chatModel, err := openai.NewChatModel(ctx, modelConf)
+ if err != nil {
+ logs.Errorf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ // 3. create an instance of tool.InvokableTool for Intent recognition and execution
+ userInfoTool := utils.NewTool(
+ &schema.ToolInfo{
+ Name: "user_info",
+ Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "用户的姓名",
+ },
+ "email": {
+ Type: "string",
+ Desc: "用户的邮箱",
+ },
+ }),
+ },
+ func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
+ return &userInfoResponse{
+ Name: input.Name,
+ Email: input.Email,
+ Company: "Bytedance",
+ Position: "CEO",
+ Salary: "9999",
+ }, nil
+ })
+
+ info, err := userInfoTool.Info(ctx)
+ if err != nil {
+ logs.Errorf("Get ToolInfo failed, err=%v", err)
+ return
+ }
+
+ // 4. bind ToolInfo to ChatModel. ToolInfo will remain in effect until the next BindTools.
+ err = chatModel.BindForcedTools([]*schema.ToolInfo{info})
+ if err != nil {
+ logs.Errorf("BindForcedTools failed, err=%v", err)
+ return
+ }
+
+ // 5. create an instance of ToolsNode as 3rd Graph Node
+ toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{userInfoTool},
+ })
+ if err != nil {
+ logs.Errorf("NewToolNode failed, err=%v", err)
+ return
+ }
+
+ const (
+ nodeKeyOfTemplate = "template"
+ nodeKeyOfChatModel = "chat_model"
+ nodeKeyOfTools = "tools"
+ )
+
+ // 6. create an instance of Graph
+ // input type is 1st Graph Node's input type, that is ChatTemplate's input type: map[string]any
+ // output type is last Graph Node's output type, that is ToolsNode's output type: []*schema.Message
+ g := compose.NewGraph[map[string]any, []*schema.Message]()
+
+ // 7. add ChatTemplate into graph
+ _ = g.AddChatTemplateNode(nodeKeyOfTemplate, chatTpl)
+
+ // 8. add ChatModel into graph
+ _ = g.AddChatModelNode(nodeKeyOfChatModel, chatModel)
+
+ // 9. add ToolsNode into graph
+ _ = g.AddToolsNode(nodeKeyOfTools, toolsNode)
+
+ // 10. add connection between nodes
+ _ = g.AddEdge(compose.START, nodeKeyOfTemplate)
+
+ _ = g.AddEdge(nodeKeyOfTemplate, nodeKeyOfChatModel)
+
+ _ = g.AddEdge(nodeKeyOfChatModel, nodeKeyOfTools)
+
+ _ = g.AddEdge(nodeKeyOfTools, compose.END)
+
+ // 9. compile Graph[I, O] to Runnable[I, O]
+ r, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("Compile failed, err=%v", err)
+ return
+ }
+
+ out, err := r.Invoke(ctx, map[string]any{
+ "message_histories": []*schema.Message{},
+ "user_query": "我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产",
+ })
+ if err != nil {
+ logs.Errorf("Invoke failed, err=%v", err)
+ return
+ }
+ logs.Infof("Generation: %v Messages", len(out))
+ for _, msg := range out {
+ logs.Infof(" %v", msg)
+ }
+}
+
+type userInfoRequest struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+}
+
+type userInfoResponse struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+ Company string `json:"company"`
+ Position string `json:"position"`
+ Salary string `json:"salary"`
+}
+
+type loggerCallbacks struct{}
+
+func (l *loggerCallbacks) OnStart(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
+ logs.Infof("name: %v, type: %v, component: %v, input: %v", info.Name, info.Type, info.Component, input)
+ return ctx
+}
+
+func (l *loggerCallbacks) OnEnd(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
+ logs.Infof("name: %v, type: %v, component: %v, output: %v", info.Name, info.Type, info.Component, output)
+ return ctx
+}
+
+func (l *loggerCallbacks) OnError(ctx context.Context, info *callbacks.RunInfo, err error) context.Context {
+ logs.Infof("name: %v, type: %v, component: %v, error: %v", info.Name, info.Type, info.Component, err)
+ return ctx
+}
+
+func (l *loggerCallbacks) OnStartWithStreamInput(ctx context.Context, info *callbacks.RunInfo, input *schema.StreamReader[callbacks.CallbackInput]) context.Context {
+ return ctx
+}
+
+func (l *loggerCallbacks) OnEndWithStreamOutput(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[callbacks.CallbackOutput]) context.Context {
+ return ctx
+}
+```
+
+### Graph with state
+
+Graph 可以有 graph 自身的“全局”状态,在创建 Graph 时传入 WithGenLocalState Option 开启此功能:
+
+```go
+// compose/generic_graph.go
+
+// type GenLocalState[S any] func(ctx context.Context) (state S)
+
+func WithGenLocalState[S any](gls GenLocalState[S]) NewGraphOption {
+ // --snip--
+}
+```
+
+Add node 时添加 Pre/Post Handler 来处理 State:
+
+```go
+// compose/graph_add_node_options.go
+
+// type StatePreHandler[I, S any] func(ctx context.Context, in I, state S) (I, error)
+// type StatePostHandler[O, S any] func(ctx context.Context, out O, state S) (O, error)
+
+func WithStatePreHandler[I, S any](pre StatePreHandler[I, S]) GraphAddNodeOpt {
+ // --snip--
+}
+
+func WithStatePostHandler[O, S any](post StatePostHandler[O, S]) GraphAddNodeOpt {
+ // --snip--
+}
+```
+
+在 Node 内部,用 `ProcessState`,传入一个读写 State 的 函数:
+
+```go
+// flow/agent/react/react.go
+
+var msg *schema.Message
+err = compose.ProcessState[*state](ctx, func(_ context.Context, state *state) error {
+ for i := range msgs {
+ if msgs[i] != nil && msgs[i].ToolCallID == state.ReturnDirectlyToolCallID {
+ msg = msgs[i]
+ return nil
+ }
+ }
+ return nil
+})
+```
+
+完整使用例子:
+
+```go
+package main
+
+import (
+ "context"
+ "errors"
+ "io"
+ "runtime/debug"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/utils/safe"
+
+ "github.com/cloudwego/eino-examples/internal/logs"
+)
+
+func main() {
+ ctx := context.Background()
+
+ const (
+ nodeOfL1 = "invokable"
+ nodeOfL2 = "streamable"
+ nodeOfL3 = "transformable"
+ )
+
+ type testState struct {
+ ms []string
+ }
+
+ gen := func(ctx context.Context) *testState {
+ return &testState{}
+ }
+
+ sg := compose.NewGraph[string, string](compose.WithGenLocalState(gen))
+
+ l1 := compose.InvokableLambda(func(ctx context.Context, in string) (out string, err error) {
+ return "InvokableLambda: " + in, nil
+ })
+
+ l1StateToInput := func(ctx context.Context, in string, state *testState) (string, error) {
+ state.ms = append(state.ms, in)
+ return in, nil
+ }
+
+ l1StateToOutput := func(ctx context.Context, out string, state *testState) (string, error) {
+ state.ms = append(state.ms, out)
+ return out, nil
+ }
+
+ _ = sg.AddLambdaNode(nodeOfL1, l1,
+ compose.WithStatePreHandler(l1StateToInput), compose.WithStatePostHandler(l1StateToOutput))
+
+ l2 := compose.StreamableLambda(func(ctx context.Context, input string) (output *schema.StreamReader[string], err error) {
+ outStr := "StreamableLambda: " + input
+
+ sr, sw := schema.Pipe[string](utf8.RuneCountInString(outStr))
+
+ // nolint: byted_goroutine_recover
+ go func() {
+ for _, field := range strings.Fields(outStr) {
+ sw.Send(field+" ", nil)
+ }
+ sw.Close()
+ }()
+
+ return sr, nil
+ })
+
+ l2StateToOutput := func(ctx context.Context, out string, state *testState) (string, error) {
+ state.ms = append(state.ms, out)
+ return out, nil
+ }
+
+ _ = sg.AddLambdaNode(nodeOfL2, l2, compose.WithStatePostHandler(l2StateToOutput))
+
+ l3 := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (
+ output *schema.StreamReader[string], err error) {
+
+ prefix := "TransformableLambda: "
+ sr, sw := schema.Pipe[string](20)
+
+ go func() {
+
+ defer func() {
+ panicErr := recover()
+ if panicErr != nil {
+ err := safe.NewPanicErr(panicErr, debug.Stack())
+ logs.Errorf("panic occurs: %v\n", err)
+ }
+
+ }()
+
+ for _, field := range strings.Fields(prefix) {
+ sw.Send(field+" ", nil)
+ }
+
+ for {
+ chunk, err := input.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ // TODO: how to trace this kind of error in the goroutine of processing sw
+ sw.Send(chunk, err)
+ break
+ }
+
+ sw.Send(chunk, nil)
+
+ }
+ sw.Close()
+ }()
+
+ return sr, nil
+ })
+
+ l3StateToOutput := func(ctx context.Context, out string, state *testState) (string, error) {
+ state.ms = append(state.ms, out)
+ logs.Infof("state result: ")
+ for idx, m := range state.ms {
+ logs.Infof(" %vth: %v", idx, m)
+ }
+ return out, nil
+ }
+
+ _ = sg.AddLambdaNode(nodeOfL3, l3, compose.WithStatePostHandler(l3StateToOutput))
+
+ _ = sg.AddEdge(compose.START, nodeOfL1)
+
+ _ = sg.AddEdge(nodeOfL1, nodeOfL2)
+
+ _ = sg.AddEdge(nodeOfL2, nodeOfL3)
+
+ _ = sg.AddEdge(nodeOfL3, compose.END)
+
+ run, err := sg.Compile(ctx)
+ if err != nil {
+ logs.Errorf("sg.Compile failed, err=%v", err)
+ return
+ }
+
+ out, err := run.Invoke(ctx, "how are you")
+ if err != nil {
+ logs.Errorf("run.Invoke failed, err=%v", err)
+ return
+ }
+ logs.Infof("invoke result: %v", out)
+
+ stream, err := run.Stream(ctx, "how are you")
+ if err != nil {
+ logs.Errorf("run.Stream failed, err=%v", err)
+ return
+ }
+
+ for {
+
+ chunk, err := stream.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ logs.Infof("stream.Recv() failed, err=%v", err)
+ break
+ }
+
+ logs.Tokenf("%v", chunk)
+ }
+ stream.Close()
+
+ sr, sw := schema.Pipe[string](1)
+ sw.Send("how are you", nil)
+ sw.Close()
+
+ stream, err = run.Transform(ctx, sr)
+ if err != nil {
+ logs.Infof("run.Transform failed, err=%v", err)
+ return
+ }
+
+ for {
+
+ chunk, err := stream.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ logs.Infof("stream.Recv() failed, err=%v", err)
+ break
+ }
+
+ logs.Infof("%v", chunk)
+ }
+ stream.Close()
+}
+```
+
+## Chain
+
+> Chain 可以视为是 Graph 的简化封装
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "math/rand"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+
+ "github.com/cloudwego/eino-examples/internal/gptr"
+ "github.com/cloudwego/eino-examples/internal/logs"
+)
+
+func main() {
+ openAPIBaseURL := os.Getenv("OPENAI_BASE_URL")
+ openAPIAK := os.Getenv("OPENAI_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+
+ ctx := context.Background()
+ // build branch func
+ const randLimit = 2
+ branchCond := func(ctx context.Context, input map[string]any) (string, error) { // nolint: byted_all_nil_return
+ if rand.Intn(randLimit) == 1 {
+ return "b1", nil
+ }
+
+ return "b2", nil
+ }
+
+ b1 := compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) {
+ logs.Infof("hello in branch lambda 01")
+ if kvs == nil {
+ return nil, fmt.Errorf("nil map")
+ }
+
+ kvs["role"] = "cat"
+ return kvs, nil
+ })
+
+ b2 := compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) {
+ logs.Infof("hello in branch lambda 02")
+ if kvs == nil {
+ return nil, fmt.Errorf("nil map")
+ }
+
+ kvs["role"] = "dog"
+ return kvs, nil
+ })
+
+ // build parallel node
+ parallel := compose.NewParallel()
+ parallel.
+ AddLambda("role", compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (string, error) {
+ // may be change role to others by input kvs, for example (dentist/doctor...)
+ role, ok := kvs["role"].(string)
+ if !ok || role == "" {
+ role = "bird"
+ }
+
+ return role, nil
+ })).
+ AddLambda("input", compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (string, error) {
+ return "你的叫声是怎样的?", nil
+ }))
+
+ modelConf := &openai.ChatModelConfig{
+ BaseURL: openAPIBaseURL,
+ APIKey: openAPIAK,
+ ByAzure: true,
+ Model: modelName,
+ Temperature: gptr.Of(float32(0.7)),
+ APIVersion: "2024-06-01",
+ }
+
+ // create chat model node
+ cm, err := openai.NewChatModel(context.Background(), modelConf)
+ if err != nil {
+ log.Panic(err)
+ return
+ }
+
+ rolePlayerChain := compose.NewChain[map[string]any, *schema.Message]()
+ rolePlayerChain.
+ AppendChatTemplate(prompt.FromMessages(schema.FString, schema.SystemMessage(`You are a {role}.`), schema.UserMessage(`{input}`))).
+ AppendChatModel(cm)
+
+ // =========== build chain ===========
+ chain := compose.NewChain[map[string]any, string]()
+ chain.
+ AppendLambda(compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) {
+ // do some logic to prepare kv as input val for next node
+ // just pass through
+ logs.Infof("in view lambda: %v", kvs)
+ return kvs, nil
+ })).
+ AppendBranch(compose.NewChainBranch(branchCond).AddLambda("b1", b1).AddLambda("b2", b2)). // nolint: byted_use_receiver_without_nilcheck
+ AppendPassthrough().
+ AppendParallel(parallel).
+ AppendGraph(rolePlayerChain).
+ AppendLambda(compose.InvokableLambda(func(ctx context.Context, m *schema.Message) (string, error) {
+ // do some logic to check the output or something
+ logs.Infof("in view of messages: %v", m.Content)
+ return m.Content, nil
+ }))
+
+ // compile
+ r, err := chain.Compile(ctx)
+ if err != nil {
+ log.Panic(err)
+ return
+ }
+
+ output, err := r.Invoke(context.Background(), map[string]any{})
+ if err != nil {
+ log.Panic(err)
+ return
+ }
+
+ logs.Infof("output is : %v", output)
+}
+```
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
new file mode 100644
index 0000000..01ef037
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
@@ -0,0 +1,441 @@
+---
+Description: ""
+date: "2026-03-24"
+lastmod: ""
+tags: []
+title: Interrupt & CheckPoint使用手册
+weight: 7
+---
+
+> 💡
+> 注意:v0.3.26 版本中因为代码编写错误导致 CheckPoint 的序列化内容产生 break,新接入 CheckPoint 使用 v0.3.26 以后的版本,建议直接使用最新。
+>
+> eino 提供了兼容分支,使用了 checkpoint 且版本低于 v0.3.26 的业务在升级 eino 时可以先升级到兼容分支,老数据淘汰后再升级到主干。
+>
+> 因为兼容分支会引入额外的性能开销并且一般来说业务 agent checkpoint 有不太长的有效期,所以分支没有合入主干。
+
+## 介绍
+
+使用 Interrupt & CheckPoint 功能,可以实现在指定位置暂停 Graph 执行并在之后断点续传,如果是 StateGraph,还可以在断点续传前修改 State。
+
+> 💡
+> 断点续传仅能复原输入和运行时各节点产生的数据,需要确保 Graph 编排完全相同,以及重新完整传入 CallOption(没有特殊情况应当保持一致,除非依赖 CallOption 在 Resume 时传递数据等)。
+
+## 使用静态 Interrupt
+
+静态 Interrupt 支持在指定 Node 执行前或执行后暂停 Graph,Compile 时传入 WithInterruptAfterNodes 与 WithInterruptBeforeNodes Option 来设置 Interrupt:
+
+```go
+import (
+ "github.com/cloudwego/eino/compose"
+)
+
+func main() {
+ g := NewGraph[string, string]()
+ err := g.AddLambdaNode("node1", compose.InvokableLambda(func(ctx **context**._Context_, input string) (output string, err error) {/*invokable func*/})
+ if err != nil {/* error handle */}
+ err = g.AddLambdaNode("node2", compose.InvokableLambda(func(ctx **context**._Context_, input string) (output string, err error) {/*invokable func*/})
+ if err != nil {/* error handle */}
+
+ /** other graph composed code
+ xxx
+ */
+
+ err = g.Compile(ctx, compose.WithInterruptAfterNodes([]string{"node1"}), compose.WithInterruptBeforeNodes([]string{"node2"}))
+ if err != nil {/* error handle */}
+}
+```
+
+> 💡
+> 目前仅支持 Compile 时设置静态断点,如果需要请求时设置,欢迎提出~
+
+可以从运行返回的 error 中获得本次运行是否 Interrupt 以及 Interrupt 信息:
+
+```go
+// compose/checkpoint.go
+
+**type **InterruptInfo **struct **{
+ State any
+ BeforeNodes []string
+ AfterNodes []string
+ RerunNodes []string
+ RerunNodesExtra **map**[string]any
+ SubGraphs **map**[string]*InterruptInfo
+ InterruptContexts []*InterruptCtx
+}
+
+func ExtractInterruptInfo(err error) (info *InterruptInfo, existed bool) {}
+```
+
+例如:
+
+```go
+import "github.com/cloudwego/eino/compse"
+
+/***graph compose code
+* g := NewGraph
+* xxx
+* runner := g.Compile
+*/
+
+result, err := runner.Invoke(ctx, input)
+if info, ok := ExtractInterruptInfo(err); ok {
+ // handler info
+}
+if err != nil {
+ // handle error
+}
+```
+
+> 💡
+> Interrupt 时 output 为空值,没有意义。
+
+## 使用 CheckPoint
+
+CheckPoint 记录 Graph 运行状态,使用 CheckPoint 可以在 Interrupt 后恢复运行。
+
+### 实现 CheckPointerStore
+
+CheckPointStore 是一个 key 类型为 string、value 类型为[]byte 的 KV 存储接口,我们没有提供封装和默认实现,需要用户自行实现,用来存储 checkpoint。
+
+```go
+// compose/checkpoint.go
+
+type CheckpointStore interface {
+ Get(ctx **context**._Context_, key string) (value []byte, existed bool,err error)
+ Set(ctx **context**._Context_, key string, value []byte) (err error)
+}
+```
+
+### 注册序列化方法
+
+CheckPoint 的保存和读取涉及对 Graph 节点输入输出以及 State 的序列化和反序列化,在仅使用简单类型或 eino 内置类型(比如 Message 或 Document)时,用户无需额外操作;当引入自定义 struct 时,需要提前注册类型,Eino 提供了注册方法 `schema.``RegisterName`:
+
+```go
+package main
+
+import "github.com/cloudwego/eino/schema"
+
+type MyState struct {
+ Counter int
+ Note string
+}
+
+func init() {
+ // Register the type with a stable name for serialization/persistence.
+ // Use the pointer form if you persist pointers to this type.
+ // It's recommended to register types within the `init()` function
+ // within the same file your type is declared.
+ schema.RegisterName[*MyState]("my_state_v1")
+}
+```
+
+注册后的类型在序列化时将被额外记录类型信息,因此在反序列化时,即使不指明类型(比如反序列化到 interface{}),Eino 也可以反序列化出正确的类型。注册方法中的 key 唯一标识了这个类型,一旦确定了 key 需要保证其不能改变,否则已持久化的 checkpoint 将不能被正确恢复。
+
+> 💡
+> 结构体的未导出字段无法访问,因此不会被存储/恢复
+
+默认情况下,会使用 eino 内置的序列化功能,此时,如果注册的类型实现了 json Marshaler 和 Unmarshaler,此类型的序列化和反序列化会使用自定义方法。
+
+```
+// encoding/json
+
+type Marshaler interface {
+ MarshalJSON() ([]byte, error)
+}
+
+type Unmarshaler interface {
+ UnmarshalJSON([]byte) error
+}
+```
+
+Eino 同时提供了将序列化方式改为 gob 的选项:
+
+```go
+r, err := compose.NewChain[*AgentInput, Message]().
+ AppendLambda(compose.InvokableLambda(func(ctx context.Context, input *AgentInput) ([]Message, error) {
+ return a.genModelInput(ctx, instruction, input)
+ })).
+ AppendChatModel(a.model).
+ Compile(ctx, compose.WithGraphName(a.name),
+ compose.WithCheckPointStore(store),
+ compose.WithSerializer(&gobSerializer{}))
+```
+
+用户可以按偏好选择,选择后不建议轻易变更,历史数据不兼容。
+
+### 开启 CheckPoint
+
+创建 CheckPointStore 后在 Compile Graph 时作为 Option 传入,把 CheckPointer 绑定到 Graph:
+
+```go
+import (
+ "github.com/cloudwego/eino/compose"
+)
+
+func main() {
+ /** graph composed code
+ xxx
+ */
+
+ err = g.Compile(ctx, compose.WithCheckPointStore(store), compose.WithInterruptBeforeNodes([]string{"node2"}))
+ if err != nil {/* error handle */}
+}
+```
+
+之后可以在请求时通过 CallOption 引入 CheckPoint:
+
+```
+// compose/checkpoint.go
+
+func WithCheckPointID(checkPointID string) Option
+```
+
+Checkpoint id 会被作为 CheckPointStore 的 key 使用,graph 运行时会检查 CheckPointStore 是否存在此 id,如果存在则从 checkpoint 中恢复运行;interrupt 是会把 graph 状态保存到此 id 中。
+
+## 动态 Interrupt
+
+节点返回特殊错误可以动态地触发 Interrupt:
+
+### 在 eino v0.7.0 之前
+
+```
+// eino/compose/interrupt.go
+
+// emit a plain interrupt signal
+var InterruptAndRerun = errors.New("interrupt and rerun")
+
+// emit an interrupt signal with extra info
+**func **NewInterruptAndRerunErr(extra any) error
+```
+
+Eino Graph 接收到节点返回此错误后会发生 interrupt,恢复运行时,会再次运行此节点,再次运行前会调用 StateModifier 修改 state(如果已配置)。
+
+这种情况下,再次运行节点时输入会替换为空值,而不是原本的输入,如果再次运行时需要仍需要原本输入,需要提前保存到 State 中。
+
+### 在 eino v0.7.0 及之后
+
+增加了对“保存本地状态”、“透出内部中断信号”、“并行中断”的支持:
+
+```
+// eino/compose/interrupt.go
+
+// emit an interrupt signal with user-facing info
+func Interrupt(ctx context.Context, info any) error
+
+// emit an interrupt signal with user-facing info AS WELL AS
+// persistent LOCALLY-DEFINED state
+func StatefulInterrupt(ctx context.Context, info any, state any) error
+
+// emit an interrupt signal WRAPPING other interrupt signals
+// emitted from inner processes,
+// such as ToolsNode wrapping Tools.
+func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error)
+```
+
+详细设计参见:[Eino human-in-the-loop 框架:技术架构指南](/zh/docs/eino/core_modules/eino_adk/agent_hitl)
+
+## 外部主动 Interrupt
+
+有时,我们希望能在 Graph 外部主动触发中断,保存现场,之后择机恢复。这些场景可能包括实例优雅退出等。这时,可以通过调用 `WithGraphInterrupt` 获取一个 ctx 和一个 interrupt function。其中 ctx 用于传递给 `graph.Invoke()` 等运行方法,interrupt function 用于在用户希望主动中断时调用:
+
+```go
+// from compose/graph_call_options.go
+
+_// WithGraphInterrupt creates a context with graph cancellation support._
+_// When the returned context is used to invoke a graph or workflow, calling the interrupt function will trigger an interrupt._
+_// The graph will wait for current tasks to complete by default._
+**func **WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt **func**(opts ...GraphInterruptOption)) {}
+```
+
+在主动调用 interrupt function 时,可以传递超时等参数:
+
+```go
+// from compose/graph_call_options.go
+
+_// WithGraphInterruptTimeout specifies the max waiting time before generating an interrupt._
+_// After the max waiting time, the graph will force an interrupt. Any unfinished tasks will be re-run when the graph is resumed._
+**func **WithGraphInterruptTimeout(timeout time.Duration) GraphInterruptOption {
+ **return func**(o *graphInterruptOptions) {
+ o.timeout = &timeout
+ }
+}
+```
+
+当外部触发中断时,节点内部没有机会保存局部状态(包括节点的 input),所以 eino 会自动保存被外部中断的节点的 input,在下次执行时自动恢复。非外部触发中断的场景,节点内部发起中断时,保存 input 是每个节点的职责,可通过保存到 graph state 中或使用 `compose.StatefulInterrupt` 保存局部状态。
+
+## 流式传输中的 CheckPoint
+
+流式传输在保存 CheckPoint 时需要拼接数据流,因此需要注册拼接方法:
+
+```go
+// compose/stream_concat.go
+func RegisterStreamChunkConcatFunc[T any](fn func([]T) (T, error))
+
+// example
+type TestStruct struct {
+ Body string
+}
+
+// RegisterStreamChunkConcatFunc非线程安全,需要在初始化阶段使用
+RegisterStreamChunkConcatFunc(func(ss []TestStruct)(TestStruct, error){
+ ret := TestStruct{Body:""}
+ for i := range ss {
+ ret.Body += ss[i].Body
+ }
+ return ret, nil
+})
+```
+
+eino 默认提供了*schema.Message、[]*schema.Message 和 string 的 concat 方法。
+
+## 嵌套图中的 Interrupt&CheckPoint
+
+父图传入 CheckPointer 的前提下,AddGraphNode 时使用 WithGraphCompileOptions 传入 InterruptNodes 可以开启子图的 Interrupt&CheckPoint,父图未设置 CheckPointer 时会在 Compile 时报错。
+
+```go
+/* graph compose code
+xxx
+*/
+g.AddGraphNode("node1", subGraph, WithGraphCompileOptions(
+ WithInterruptAfterNodes([]string{"node2"}),
+))
+
+g.Compile(ctx, WithCheckPointStore(cp))
+```
+
+如果在子图中 interrupt,resume 时修改的 state 应为子图 state。TODO,说明下 StateModifier 中 Path 使用
+
+## 恢复
+
+恢复:Interrupt 并保存 checkpoint 后,后续的 graph 运行。
+
+### 在 eino v0.7.0 之前
+
+通过修改 State 来影响恢复时的行为。
+
+```go
+// compose/checkpoint.go
+
+type StateModifier func(ctx context.Context, path NodePath, state any) error
+func WithStateModifier(sm StateModifier) GraphCompileOption
+```
+
+StateModifier 在 Graph 恢复运行时生效,可以在运行前修改 State,path 在嵌套图中生效,非嵌套视为空数组。
+
+```go
+/* graph compose and compile
+xxx
+*/
+
+// first run interrupt
+id := GenUUID()
+_, err := runner.Invoke(ctx, input, WithCheckPointID(id))
+
+// resume from id
+_, err = runner.Invoke(ctx, input/*unused*/,
+ WithCheckPointID(id),
+ WithStateModifier(func(ctx context.Context, path NodePath, state any) error{
+ state.(*testState).Field1 = "hello"
+ return nil
+ }),
+)
+```
+
+> 💡
+> Resume 时 input 不会被读取,此时 input 传空即可。
+
+### 在 eino v0.7.0 及之后
+
+除了 StateModifier 之外,还可以选择性的恢复某个中断点,以及直接给指定的“中断点位”传递“恢复数据”:
+
+```go
+// specifically resume particular interrupt point(s),
+// without specifying resume data
+func Resume(ctx context.Context, interruptIDs ...string) context.Context
+
+// specifically resume one interrupt point, with custom resume data
+func ResumeWithData(ctx context.Context, interruptID string, data any) context.Context
+
+// specifically resume multiple interrupt points, each with custom resume data
+func BatchResumeWithData(ctx context.Context, resumeData map[string]any) context.Context
+```
+
+其中,`InterruptID` 是从 interrupt error 中获取的:
+
+```go
+interruptInfo, isInterrupt := ExtractInterruptInfo(err)
+if isInterrupt {
+ // maybe multiple interrupt points exist here,
+ // we only take the first one for illustration purpose
+ interruptID = interruptInfo.InterruptContexts[0].ID
+}
+```
+
+`resumeData` 是发生中断的点位定义的类型,比如一个 Tool 发生了中断并要求用户“审批”是否执行这个 Tool,自定义了一个 `ApprovalResult` 作为 resumeData:
+
+```go
+func (i InvokableApprovableTool) InvokableRun(ctx context.Context, argumentsInJSON string,
+ opts ...tool.Option) (string, error) {
+
+ toolInfo, err := i.Info(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ wasInterrupted, _, storedArguments := compose.GetInterruptState[string](ctx)
+ if !wasInterrupted { // initial invocation, interrupt and wait for approval
+ return "", compose.StatefulInterrupt(ctx, &ApprovalInfo{
+ ToolName: toolInfo.Name,
+ ArgumentsInJSON: argumentsInJSON,
+ ToolCallID: compose.GetToolCallID(ctx),
+ }, argumentsInJSON)
+ }
+
+ isResumeTarget, hasData, data := compose.GetResumeContext[*ApprovalResult](ctx)
+ if !isResumeTarget { // was interrupted but not explicitly resumed, reinterrupt and wait for approval again
+ return "", compose.StatefulInterrupt(ctx, &ApprovalInfo{
+ ToolName: toolInfo.Name,
+ ArgumentsInJSON: storedArguments,
+ ToolCallID: compose.GetToolCallID(ctx),
+ }, storedArguments)
+ }
+ if !hasData {
+ return "", fmt.Errorf("tool '%s' resumed with no data", toolInfo.Name)
+ }
+
+ if data.Approved {
+ return i.InvokableTool.InvokableRun(ctx, storedArguments, opts...)
+ }
+
+ if data.DisapproveReason != nil {
+ return fmt.Sprintf("tool '%s' disapproved, reason: %s", toolInfo.Name, *data.DisapproveReason), nil
+ }
+
+ return fmt.Sprintf("tool '%s' disapproved", toolInfo.Name), nil
+}
+```
+
+# 例子
+
+### 在 eino v0.7.0 之前
+
+[https://github.com/cloudwego/eino-examples/tree/main/compose/graph/react_with_interrupt](https://github.com/cloudwego/eino-examples/tree/main/compose/graph/react_with_interrupt)
+
+### 在 eino v0.7.0 之后
+
+[https://github.com/cloudwego/eino/blob/main/compose/resume_test.go](https://github.com/cloudwego/eino/blob/main/compose/resume_test.go)
+
+其中
+
+`TestInterruptStateAndResumeForRootGraph`: 简单动态中断
+
+`TestInterruptStateAndResumeForSubGraph`: 子图中断
+
+`TestInterruptStateAndResumeForToolInNestedSubGraph`: 嵌套子图内部 tool 中断
+
+`TestMultipleInterruptsAndResumes`: 并行中断
+
+`TestReentryForResumedTools`: ReAct Agent 内 tool 中断,恢复后多次循环执行
+
+`TestGraphInterruptWithinLambda`: Lambda 节点内包含独立 Graph 且内部中断
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
new file mode 100644
index 0000000..731c634
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
@@ -0,0 +1,477 @@
+---
+Description: ""
+date: "2026-03-09"
+lastmod: ""
+tags: []
+title: 编排的设计理念
+weight: 2
+---
+
+大模型应用编排框架的主流语言是 python,这门语言以其灵活性著称,灵活性给 sdk 的开发带来便利,但同时也给 sdk 的使用者带来了心智负担。
+
+基于 golang 的 eino 则是 `静态类型` ,在 Compile 时做类型检查,避免了 python 等动态语言的运行时类型问题。
+
+## 以上下游 `类型对齐` 为基本准则
+
+eino 的最基础编排方式为 graph,以及简化的封装 chain。不论是哪种编排方式,其本质都是 `逻辑节点` + `上下游关系` 。在编排的产物运行时,都是从一个逻辑节点运行,然后下一步运行和这个节点相连的下一个节点。
+
+这之间蕴含了一个基本假设:**前一个运行节点的输出值,可以作为下一个节点的输入值。**
+
+在 golang 中,要实现这个假设,有两个基本方案:
+
+1. 把不同节点的输入输出都变成一种更泛化的类型,例如 `any` 、`map[string]any` 等。
+ 1. 采用泛化成 any 的方案,但对应的代价是: 开发者在写代码时,需要显式转换成具体类型才能使用。这会极大增加开发者的心智负担,因此最终放弃此方案。
+ 2. langchain 的方案可以看做是全程传递 `map[string]any`,各个逻辑节点根据自己的需要,用对应的 key 去取对应的 value。在 langchaingo 的实现中,即是按照这种方式实现,但同样,golang 中的 any 要被使用依然要使用 `类型断言` 才可使用。这种方案在开发者使用时依然有很大的心智负担。
+2. 每一个节点的输入输出类型保持开发者的预期,在 Compile 阶段保证上下游的类型是一致的。
+
+方案 2 即是 eino 最终选定的方案。这种方案是编排时最容易被理解的,整个过程就像是 `搭积木` 一样,每一个积木突出的部分和凹陷的部分有各自的规格,仅有规格匹配了才能成为上下游关系。
+
+就如下图:
+
+
+
+对于一个编排而言,只有下游能识别和处理上游的输出,这个编排才能正常运行。 这个基本假设在 eino 中被清晰地表达了出来,让开发者在用 eino 做编排时,能够有十足的信心清楚编排的逻辑是如何运行和流转的,而不是从一系列的 any 中去猜测传过来的值是否正确。
+
+### graph 中的类型对齐
+
+#### edge
+
+在 graph 中,一个节点的输出将顺着 `边(edge)` 流向下一节点,因此,用边连接的节点间必须要类型对齐。
+
+如下图:
+
+> 这是一个模拟 ① 直接和大模型对话 ② 使用 RAG 模式 的场景,最后结果可用于对比两种模式的效果
+
+
+
+图中绿色的部分,就是普通的 Edge 连接,其要求上游的输出必须能 `assign` 给下游,可以接收的类型有:
+
+① 上下游类型相同: 例如上游输出 *schema.Message 下游输入也是 *schema.Message
+
+② 下游接收接口,上游实现了该接口: 例如上游结构体实现了 Format() 接口,下游接收的是一个 interface{ Format() }。特殊情况是下游是 any(空接口),上游一定实现了 any,因此一定可以连接。
+
+③ 上游是 interface,下游是具体类型: 当下游具体类型 implements 上游的 interface 类型时,有可能可以,有可能不行,在 compile 时无法确定,只有在运行时,等上游的具体类型确定了,才能最终确定。时,详细描述可见: [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+
+图中黄色的部分,则是 eino 提供的另一个类型转换的机制,即: 若下游接收的类型是 `map[string]any`,但是上游输出的类型并不是 map[string]any,可以使用 `graph.AddXXXNode(node_key, xxx, compose.WithOutputKey("outkey")` 的方式将上游输出的类型转化为 map[string]any,其中 map 的 key 是 option 中指定的 OutputKey。 一般在多条边汇聚到某一个节点时,这种机制使用起来较为方便。
+
+同理,若上游是 `map[string]any` ,但是下游输入的类型并不是 map[string]any,则可以使用 `graph.AddXXXNode(node_key, xxx, compose.WithInputKey("inkey")` 来获取上游输出的其中一个 key 的 value,作为下游的输入。
+
+#### branch
+
+如果一个节点后面连接了多个 edge,则每条 edge 的下游节点都会运行一次。branch 则是另一种机制: 一个 branch 后接了 n 个节点,但仅会运行 condition 返回的那个 node key 对应的节点。同一个 branch 后的节点,必须要类型对齐。
+
+如下图:
+
+> 这是一个模拟 react agent 的运行逻辑
+
+
+
+可以看到,一个 branch 本身拥有一个 `condition`, 这个 function 的输入必须和上游类型对齐。同时,一个 branch 后所接的各个节点,也必须和 condition 一样,要能接收上游的输出。
+
+### chain 中的类型对齐
+
+#### chain
+
+从抽象角度看,chain 就是一个 `链条`,如下所示:
+
+
+
+逻辑节点的类型可以分为 3 类:
+
+- 可编排组件 (例如 chat model、 chat template、 retriever、 lambda、graph 等等)
+- branch 节点
+- parallel 节点
+
+可以看到,在 chain 的视角下,不论是简单的节点(eg: chat model) 还是复杂的节点 (eg: graph、branch、parallel),都是一样的,在运行过程中,一步的执行就是一个节点的运行。
+
+也因此,chain 的上下游节点间,类型必须是对齐的,如下:
+
+```go
+func TestChain() {
+ chain := compose.NewChain[map[string]interface,string]()
+
+ nodeTemplate := &fakeChatTemplate{} // input: map[string]any, output: []*schema.Message
+
+ nodeHistoryLambda := &fakeLambda{} // input: []*schema.Message, output: []*schema.Message
+
+ nodeChatModel := &fakeChatModel{} // input: []*schema.Message, output: *schema.Message
+
+ nodeConvertResLambda := &fakeLambda{} // input: *schema.Message, output: string
+
+ chain.
+ AppendChatTemplate(nodeTemplate).
+ AppendLambda(nodeHistoryLambda).
+ AppendChatModel(nodeChatModel).
+ AppendLambda(nodeConvertResLambda)
+}
+```
+
+上面的逻辑用图来表示如下:
+
+
+
+若上下游的类型没有对齐,chain 会在 chain.Compile() 时返回错误。而 graph 会在 graph.AddXXXNode() 时就报错。
+
+#### parallel
+
+parallel 在 chain 中是一类特殊的节点,从 chain 的角度看 parallel 和其他的节点没啥区别。在 parallel 内部,其基本拓扑结构如下:
+
+
+
+graph 中的多 edge 形成的结构其中一种就是这个,这里的基本假设是: 一个 parallel 的每一条边上有且仅有一个节点。当然,这一个节点也可以是 graph。但注意,目前框架没有直接提供在 parallel 中嵌套 branch 或 parallel 的能力。
+
+在 parallel 中的每个节点,由于其上游节点是同一个,因此他们都要和上游节点的输出类型对齐,比如图中上游节点输出了 `*schema.Message` ,则每个节点都要能接收这个类型。接收的方式和 graph 中的一致,通常可以用 `相同类型` 、`接口定义` 、`any`、`input key option` 的方式。
+
+parallel 节点的输出一定是一个 `map[string]any`,其中的 key 则是在 `parallel.AddXXX(output_key, xxx, opts...)` 时指定的 output_key,value 是节点内部的实际输出。
+
+一个 parallel 的构建例子如下:
+
+```go
+func TestParallel() {
+ chain := compose.NewChain[map[string]any, map[string]*schema.Message]()
+
+ parallel := compose.NewParallel()
+ model01 := &fakeChatModel{} // input: []*schema.Message, output: *schema.Message
+ model02 := &fakeChatModel{} // input: []*schema.Message, output: *schema.Message
+ model03 := &fakeChatModel{} // input: []*schema.Message, output: *schema.Message
+
+ parallel.
+ AddChatModel("outkey_01", model01).
+ AddChatModel("outkey_02", model02).
+ AddChatModel("outkey_03", model03)
+
+ lambdaNode := &fakeLambdaNode{} // input: map[string]any, output: map[string]*schema.Message
+
+ chain.
+ AppendParallel(parallel).
+ AppendLambda(lambdaNode)
+}
+```
+
+一个 parallel 在 chain 中的视角如下:
+
+> 图中是模拟同一个提问,由不同的大模型去回答,结果可用于对比效果
+
+
+
+> 需要注意的是,这个结构只是逻辑上的视角,由于 chain 本身也是用 graph 实现的,parallel 在底层 graph 中会平铺到图中。
+
+#### branch
+
+chain 的 branch 和 graph 中的 branch 类似,branch 中的所有节点都要和上游节点的类型对齐,此处不再赘述。chain branch 的特殊之处是,branch 的所有可能的分支节点,都会连到 chain 中的同一个节点,或者都会连到 END。
+
+### Workflow 中的类型对齐
+
+Workflow 的类型对齐的维度,由整体的 Input & Output 改成了字段级别。具体可分为:
+
+- 上游输出的整体,类型对齐到下游的某个具体字段。
+- 上游输出的某个具体字段,类型对齐到下游的整体。
+- 上游输出的某个具体字段,类型对齐到下游输入的某个具体字段。
+
+原理和规则与整体的类型对齐相同。
+
+### StateHandler 的类型对齐
+
+StatePreHandler: 输入类型需要对齐对应节点的非流式输入类型。
+
+```go
+// input 类型为 []*schema.Message,对齐 ChatModel 的非流式输入类型
+preHandler := func(ctx context.Context, input []*schema.Message, state *state) ([]*schema.Message, error) {
+ // your handler logic
+}
+
+AddChatModelNode("xxx", model, WithStatePreHandler(preHandler))
+```
+
+StatePostHandler: 输入类型需要对齐对应节点的非流式输出类型。
+
+```go
+// input 类型为 *schema.Message,对齐 ChatModel 的非流式输出类型
+postHandler := func(ctx context.Context, input *schema.Message, state *state) (*schema.Message, error) {
+ // your handler logic
+}
+
+AddChatModelNode("xxx", model, WithStatePostHandler(postHandler))
+```
+
+StreamStatePreHandler: 输入类型需要对齐对应节点的流式输入类型。
+
+```go
+// input 类型为 *schema.StreamReader[[]*schema.Message],对齐 ChatModel 的流式输入类型
+preHandler := func(ctx context.Context, input *schema.StreamReader[[]*schema.Message], state *state) (*schema.StreamReader[[]*schema.Message], error) {
+ // your handler logic
+}
+
+AddChatModelNode("xxx", model, WithStreamStatePreHandler(preHandler))
+```
+
+StreamStatePostHandler: 输入类型需要对齐对应节点的流式输出类型。
+
+```go
+// input 类型为 *schema.StreamReader[*schema.Message],对齐 ChatModel 的流式输出类型
+postHandler := func(ctx context.Context, input *schema.StreamReader[*schema.Message], state *state) (*schema.StreamReader[*schema.Message], error) {
+ // your handler logic
+}
+
+AddChatModelNode("xxx", model, WithStreamStatePostHandler(postHandler))
+```
+
+### invoke 和 stream 下的类型对齐方式
+
+在 Eino 中,编排的结果是 graph 或 chain,若要运行,则需要使用 `Compile()` 来生成一个 `Runnable` 接口。
+
+Runnable 的一个重要作用就是提供了 「Invoke」、「Stream」、「Collect」、「Transform」 四种调用方式。
+
+> 上述几种调用方式的介绍以及详细的 Runnable 介绍可以查看: [Eino 流式编程要点](/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+
+假设我们有一个 `Graph[[]*schema.Message, []*schema.Message]`,里面有一个 ChatModel 节点,一个 Lambda 节点,Compile 之后是一个 `Runnable[[]*schema.Message, []*schema.Message]`。
+
+```go
+package main
+
+import (
+ "context"
+ "io"
+ "testing"
+
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTypeMatch(t *testing.T) {
+ ctx := context.Background()
+
+ g1 := compose.NewGraph[[]*schema.Message, string]()
+ _ = g1.AddChatModelNode("model", &mockChatModel{})
+ _ = g1.AddLambdaNode("lambda", compose.InvokableLambda(func(_ context.Context, msg *schema.Message) (string, error) {
+ return msg.Content, nil
+ }))
+ _ = g1.AddEdge(compose.START, "model")
+ _ = g1.AddEdge("model", "lambda")
+ _ = g1.AddEdge("lambda", compose.END)
+
+ runner, err := g1.Compile(ctx)
+ assert.NoError(t, err)
+
+ c, err := runner.Invoke(ctx, []*schema.Message{
+ schema.UserMessage("what's the weather in beijing?"),
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, "the weather is good", c)
+
+ s, err := runner.Stream(ctx, []*schema.Message{
+ schema.UserMessage("what's the weather in beijing?"),
+ })
+ assert.NoError(t, err)
+
+ var fullStr string
+ for {
+ chunk, err := s.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ panic(err)
+ }
+
+ fullStr += chunk
+ }
+ assert.Equal(t, c, fullStr)
+}
+```
+
+当我们以 Stream 方式调用上面编译好的 Runnable 时,model 节点会输出 `*schema.StreamReader[*Message]`,但是 lambda 节点是 InvokableLambda,只接收非流式的 `*schema.Message` 作为输入。这也符合类型对齐规则,因为 Eino 框架会自动把流式的 Message 拼接成完整的 Message。
+
+在 stream 模式下,拼接帧 是一个非常常见的操作,拼接时,会先把 `*StreamReader[T] ` 中的所有元素取出来转成 `[]T`,再尝试把 `[]T` 拼接成一个完整的 `T`。框架内已经内置支持了如下类型的拼接:
+
+- `*schema.Message`: 详情见 `schema.``ConcatMessages``()`
+- `string`: 实现逻辑等同于 `+=`
+- `[]*schema.Message`: 详情见 `compose.concatMessageArray()`
+- `Map`: 把相同 key 的 val 进行合并,合并逻辑同上,若存在无法合并的类型,则失败 (ps: 不是覆盖)
+- 其他 slice:只有当 slice 中只有一个元素是非零值时,才能合并。
+
+对其他场景,或者当用户想用定制逻辑覆盖掉上面的默认行为时,开发者可自行实现 concat 方法,并使用 `compose.RegisterStreamChunkConcatFunc()` 注册到全局的拼接函数中。
+
+示例如下:
+
+```go
+// 假设我们自己的结构体如下
+type tStreamConcatItemForTest struct {
+ s string
+}
+
+// 实现一个拼接的方法
+func concatTStreamForTest(items []*tStreamConcatItemForTest) (*tStreamConcatItemForTest, error) {
+ var s string
+ for _, item := range items {
+ s += item.s
+ }
+
+ return &tStreamConcatItemForTest{s: s}, nil
+}
+
+func Init() {
+ // 注册到全局的拼接方法中
+ compose.RegisterStreamChunkConcatFunc(concatTStreamForTest)
+}
+```
+
+### 类型对齐在运行时检查的场景
+
+eino 的 Graph 类型对齐检查,会在 `err = graph.AddEdge("node1", "node2")` 时检查两个节点类型是否匹配,也就能在 `构建 graph 的过程`,或 `Compile 的过程` 发现类型不匹配的错误,这适用于 [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) 中所列举的 ① ② ③ 条规则。
+
+当上游节点的输出为 `interface` 时,若下游节点类型实现了该 `interface`,则上游有可能可以转成下游类型 (类型断言),但只能在 `运行过程` 才能清楚能否转换成功,该场景的类型检查移到了运行过程中。
+
+其结构可见下图:
+
+
+
+这种场景适用于开发者能自行处理好上下游类型对齐的情况,可根据不同类型选择下游执行节点。
+
+## 带有明确倾向性的设计选择
+
+### 外部变量只读原则
+
+Eino 的 Graph 中的数据在 Node、Branch、Handler 间流转时,一律是变量赋值,不是 Copy。当 Input 是引用类型,如 Struct 指针、map、slice 时,在 Node、Branch、Handler 内部对 Input 的修改,会对外部有副作用,可能导致并发问题。因此,Eino 遵循外部变量只读原则:Node、Branch、Handler 内部不对 Input 做修改,如需修改,先自行 Copy。
+
+这个原则对 StreamReader 中的 Chunk 同样生效。
+
+### 扇入与合并
+
+**扇入**:多个上游的数据汇入到下游,一起作为下游的输入。需要明确定义多个上游的输出,如何**合并(Merge)**起来。
+
+默认情况下,首先要求多个上游输出的**实际类型**必须相同且为 Map,且相互间 key 不可重复。其次:
+
+- 在非流式场景下,合并后成为一个 Map,包含所有上游的所有键值对。
+- 在流式场景下,将类型相同的多个上游 StreamReader 合并为一个 StreamReader。实际 Recv 时效果为从多个上游 StreamReader 中公平读取。
+
+在 AddNode 时,可以通过添加 WithOutputKey 这个 Option 来把节点的输出转成 Map:
+
+```go
+// 这个节点的输出,会从 string 改成 map[string]any,
+// 且 map 中只有一个元素,key 是 your_output_key,value 是实际的的节点输出的 string
+graph.AddLambdaNode("your_node_key", compose.InvokableLambda(func(ctx context.Context, input []*schema.Message) (str string, err error) {
+ // your logic
+ return
+}), compose.WithOutputKey("your_output_key"))
+```
+
+也可以通过注册 Merge 方法来实现任意类型的 merge:
+
+```go
+// eino/compose/values_merge.go
+func RegisterValuesMergeFunc[T any](fn func([]T) (T, error))
+```
+
+Workflow 可以做到多个上游的多个输出字段映射到下游节点的不同字段。这并不属于合并场景,而是点对点的字段映射。事实上,eino workflow 目前不支持“多个上游字段同时映射到相同的下游字段”。
+
+### 流式处理
+
+Eino 认为,组件应当只需要实现业务场景中真实的流式范式,比如 ChatModel 不需要实现 Collect。因此,在编排场景中,Eino 自动帮助所有的节点**补全缺失的流式范式**。
+
+以 Invoke 方式运行 Graph,内部各节点均以 Invoke 范式运行,以 Stream, Collect 或 Transform 方式运行 Graph,内部各节点均以 Transform 范式运行。
+
+**自动拼接(Concatenate)**:Stream chunk 拼接为完整内容的场景,优先使用用户注册的自定义拼接函数,其次执行框架提供的默认行为,包括 Message, Message 数组,String,Map 和 Struct 及 Struct 指针。
+
+**自动流化(Box)**:需要将非流式的 T 变成 StreamReader[T] 的场景,框架自动执行。
+
+**自动合并(Merge)**:见上文“扇入与合并”环节。
+
+**自动复制(Copy)**:在需要做流的复制的场景自动进行流的复制,包括一个流扇出到多个下游节点,一个流进入一个或多个 callback handler。
+
+最后,Eino 要求所有编排元素能够感知和处理流。包括 branch,state handler,callback handler,passthrough,lambda 等。
+
+关于 Eino 对流的处理能力,详见 [Eino 流式编程要点](/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)。
+
+### 全局状态
+
+**State**:在 NewGraph 时通过 `compose.WithGenLocalState` 传入 State 的创建方法。这个请求维度的全局状态在一次请求的各环节可读写使用。
+
+Eino 推荐用 `StatePreHandler` 和 `StatePostHandler`,功能定位是:
+
+- StatePreHandler:在每个节点执行前读写 State,以及按需替换节点的 Input。输入需对齐节点的非流式输入类型。
+- StatePostHandler:在每个节点执行后读写 State,以及按需替换节点的 Output。输入需对齐节点的非流式输出类型。
+
+针对流式场景,使用对应的 `StreamStatePreHandler` 和 `StreamStatePostHandler`,输入需分别对齐节点的流式输入和流式输出类型。
+
+这些 state handlers 位于节点外部,通过对 Input 或 Output 的修改影响节点,从而保证了节点的“状态无关”特性。
+
+如果需要在节点内部读写 State,Eino 提供了 `ProcessState[S any](ctx context.Context`**, **`handler func(context.Context`**, **`S) error) error` 函数。
+
+Eino 框架会在所有读写 State 的位置加锁。
+
+### 回调注入
+
+Eino 编排框架认为,进入编排的组件,可能内部埋入了 Callback 切面,也可以没有。这个信息由组件是否实现了 `Checker` 接口,以及接口中 `IsCallbacksEnabled` 方法的返回值来判断。
+
+- 当 `IsCallbacksEnabled` 返回 true 时,Eino 编排框架使用组件实现内部的 Callback 切面。
+- 否则,自动在组件实现外部包上 Callback 切面,(只能)上报 input 和 output。
+
+无论哪种,都会自动推断出 RunInfo。
+
+同时,对 Graph 整体,也一定会注入 Callback 切面,RunInfo 为 Graph 自身。
+
+关于 Eino 的 Callback 能力完整说明,见 [Eino: Callback 用户手册](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)。
+
+### Option 分配
+
+Eino 支持各种维度的 Call Option 分配方式:
+
+- 默认全局,即分配到所有节点,包括嵌套的内部图。
+- 可添加某个组件类型的 Option,这时默认分配到该类型的所有节点,比如 AddChatModelOption。定义了独有 Option 类型的 Lambda,也可以这样把 Option 指定到自身。
+- 可指定任意个具体的节点,使用 `DesignateNode(key ...string)`.
+- 可指定任意深度的嵌套图,或者其中的任意个具体的节点,使用 `DesignateNodeWithPath(path ...*NodePath)`.
+
+关于 Eino 的 Call Option 能力完整说明,见 [Eino: CallOption 能力与规范](/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities)。
+
+### 图嵌套
+
+图编排产物 `Runnable` 与 Lambda 的接口形式非常相似。因此编译好的图可以简单的封装为 Lambda,并以 Lambda 节点的形式嵌套进其他图中。
+
+另一种方式,在编译前,Graph,Chain,Workflow 等都可以直接通过 AddGraph 的方式嵌套进其他图中。两个方式的差异是:
+
+- Lambda 的方式,在 trace 上会多一级 Lambda 节点。其他 Callback handler 视角看也会多一层。
+- Lambda 的方式,需要通过 Lambda 的 Option 来承接 CallOption,无法通过 DesignateNodeWithPath。
+- Lambda 的方式,内部图需事先编译。直接 AddGraph,则内部图随上级图一起编译。
+
+## 内部机制
+
+### 执行时序
+
+以一个添加了 StatePreHandler、StatePostHandler、InputKey、OutputKey,且内部没有实现 Callback 切面的 InvokableLambda(输入为 string,输出为 int)为例,在图中的流式执行完整时序如下:
+
+
+
+在 workflow 的场景中,字段映射发生在两个位置:
+
+- 在节点执行后的 StatePostHandler 以及“流复制”步骤后,每个下游需要的字段会分别抽取出来。
+- 在节点执行前的“合并”步骤之后、StatePreHandler 之前,会将抽取出来的上游字段值转换为当前节点的输入。
+
+### 运行引擎
+
+`NodeTriggerMode == AnyPredecessor` 时,图以 pregel 引擎执行,对应的拓扑结构是有向有环图。特点是:
+
+- 当前执行中的一个或多个节点,所有的后序节点,作为一个 SuperStep,整体一起执行。这时,这些新的节点,会成为“当前”节点。
+- 支持 Branch,支持图中有环,但是可能需要人为添加 passthrough 节点,来确保 SuperStep 中的节点符合预期,如下图:
+
+
+
+上图中 Node 4 和 Node 5 按规则被放在一起执行,大概率不符合预期。需要改成:
+
+
+
+`NodeTriggerMode == AllPredecessor` 时,图以 dag 引擎执行,对应的拓扑结构是有向无环图。特点是:
+
+- 每个节点有确定的前序节点,当所有前序节点都完成后,本节点才具备运行条件。
+- 不支持图中有环,因为会打破“每个节点有确定的前序节点”这一假定。
+- 支持 Branch。在运行时,将 Branch 未选中的节点记为已跳过,不影响 AllPredecessor 的语义。
+
+> 💡
+> 设置 NodeTriggerMode = AllPredecessor 后,节点会在所有前驱就绪后执行,但并不是立即执行,而是依然遵循 SuperStep——在一批节点全部执行完成后再运行新的可运行节点。
+>
+> 如果在 Compile 时传入 compose.WithEagerExecution(),则就绪的节点会立刻运行。
+>
+> 在 Eino v0.4.0 版本及之后的版本中,设置 NodeTriggerMode = AllPredecessor 后会默认开启 EagerExecution。
+
+总结起来,pregel 模式灵活强大但有额外的心智负担,dag 模式清晰简单但场景受限。在 Eino 框架中,Chain 是 pregel 模式,Workflow 是 dag 模式,Graph 则都支持,可由用户从 pregel 和 dag 中选择。
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
new file mode 100644
index 0000000..39ef623
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
@@ -0,0 +1,222 @@
+---
+Description: ""
+date: "2026-01-30"
+lastmod: ""
+tags: []
+title: Eino 流式编程要点
+weight: 4
+---
+
+> 💡
+> 建议先看:[Eino: 基础概念介绍](/zh/docs/eino/overview) [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+
+## 编排流式概述
+
+
+
+编排流式的 Graph 时,需要考虑的几个关键要素:
+
+- 组件/Lambda 中包含哪几种 Lambda 算子: 从 Invoke、Stream、Collect、Transform 中任选
+- 编排拓扑图中,上下游节点的输入、输出是否同为流或同为非流。
+- 如果上下游节点的流类型无法匹配。 需要借助 流化、合包 两个操作
+ - 流化(Streaming):将 T 流化成单 Chunk 的 Stream[T]
+ - 合包(Concat):将 Stream[T] 合并成一个完整的 T。Stream[T] 中的每一“帧”是这个完整 T 的一部分。
+
+## Eino 流式编程的内涵
+
+- 有的组件,天然支持分“帧”来输出,每次输出一个完整出参的一部分,即“流式”输出。流式输出完成后,需要下游把这些“帧”拼接(concat)成完整的出参。典型的例子,是 LLM。
+- 有的组件,天然支持分“帧”来输入,接收到不完整的入参时,就能开始有意义的业务处理,甚至完成业务处理的过程。比如 react agent 中用来判断是调 tool 还是结束运行的 branch 里面,拿到 LLM 的流式输出,从第一个帧里面就可以通过判断 message 是否包含 tool call 来做出决策。
+- 因此,一个组件,从入参角度看,有“非流式”入参和“流式”入参两种,从出参角度看,有“非流式”出参和“流式”出参两种。
+- 组合起来,有四种可能的流式编程范式
+
+
| 函数名 | 模式说明 | 交互模式名称 | Lambda 构造方法 | 说明 |
| Invoke | 输入非流式、输出非流式 | Ping-Pong 模式 | compose.InvokableLambda() | |
| Stream | 输入非流式、输出流式 | Server-Streaming 模式 | compose.StreamableLambda() | |
| Collect | 输入流式、输出非流式 | Client-Streaming | compose.CollectableLambda() | |
| Transform | 输入流式、输出流式 | Bidirectional-Streaming | compose.TransformableLambda() |
| 组件名称 | 是否实现 Invoke | 是否实现 Stream | 是否实现 Collect | 是否实现 Transform |
| Chat model | yes | yes | no | no |
| Chat template | yes | no | no | no |
| Retriever | yes | no | no | no |
| Indexer | yes | no | no | no |
| Embedder | yes | no | no | no |
| Document Loader | yes | no | no | no |
| Document Transformer | yes | no | no | no |
| Tool | yes | yes | no | no |
+
+上图中,如果 Tool 是个 StreamableTool,也就是输出是 StreamReader[Message],则 Tool -> ChatModel 就可能是流式的输出。但是 Chat Model 并没有接收流式输入的业务场景,也没有对应的接口。这时 Eino 框架会自动帮助 ChatModel 补足接收流式输入的能力:
+
+
+
+上面的 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) 中关于“合并帧”的章节。
+
+另一方面,考虑一个相反的例子。还是 React Agent,这次是一个更完整的编排示意图:
+
+
+
+在上图中,branch 接收 chat model 输出的 message,并根据 message 中是否包含 tool call,来选择直接结束 agent 本次运行并将 message 输出,还是调用 Tool 并将调用结果再次给 Chat Model 循环处理。由于这个 Branch 可以通过 message stream 的首个帧就完成逻辑判断,因此我们给这个 Branch 定义的是 Collect 接口,即流式输入,非流式输出:
+
+```go
+compose.NewStreamGraphBranch(func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (endNode string, err error) {
+ msg, err := sr.Recv()
+ if err != nil {
+ return "", err
+ }
+ defer sr.Close()
+
+ if len(msg.ToolCalls) == 0 {
+ return compose._END_, nil
+ }
+
+ return nodeKeyTools, nil
+}
+```
+
+ReactAgent 有两个接口,Generate 和 Stream,分别实现了 Invoke 和 Stream 的流式编程范式。当一个 ReactAgent 以 Stream 的方式被调用时,Chat Model 的输出是 StreamReader[Message],因此 Branch 的输入是 StreamReader[Message],符合这个 Branch condition 的函数签名定义,不需要做任何的转换就可以运行。
+
+但是,当这个 ReactAgent 以 Generate 的方式被调用时,Chat Model 的输出是 Message,因此 Branch 的输入也会是 Message,不符合 Branch Condition 的 StreamReader[Message] 的函数签名定义。这时,Eino 框架会自动将 Message 装箱成 StreamReader[Message],再传给 Branch,而这个 StreamReader 里面只会有一个帧。
+
+> 💡
+> 这种只有一个帧的流,俗称“假流”,因为它并没有带来流式的实际好处即“首包延迟低”,而是仅仅为了满足流式出入参接口签名的要求而做的简单装箱。
+
+总结起来,就是:**在编排中,当一个组件的上游输出是 T,但是组件只提供了 StreamReader[T] 作为输入的业务接口时,框架会自动将 T 装箱成 StreamReader[T] 的单帧流,再输入给这个组件。**
+
+### 编排辅助元素的流式范式
+
+上面提到的 Branch,并不是一个可单独使用的组件,而是只在编排场景中才有意义的“编排辅助元素”,类似的仅编排场景有意义的“组件”,还有一些,详见下图:
+
+| 组件名称 | 使用场景 | 是否实现 Invoke | 是否实现 Stream | 是否实现 Collect | 是否实现 Transform |
| Branch | 根据上游输出,在一组下游 Node 中动态选择一个 | yes | no | yes | no |
| StatePreHandler | Graph中,进入 Node 前修改 State 或/与 Input。可支持流式。 | yes | no | no | yes |
| StatePostHandler | Graph中,Node 完成后修改 State 或/与 Output。可支持流式 | yes | no | no | yes |
| Passthrough | 在并行情况下,为了打平每个并行分支的 Node 个数,可以给 Node 个数少的分支加 Passthrough 节点。Passthrough 节点的输入输出相同,跟随上游节点的输出或跟随下游节点的输入(预期应当相同)。 | yes | no | no | yes |
| Lambda | 封装官方组件未定义的业务逻辑。业务逻辑是哪种范式,就选择对应的那种流式范式来实现。 | yes | yes | yes | yes |
+
+- 否则,若组件实现了 Collect,则将 Collect 封装成 Invoke,即非流式入参转单帧流。
+
+
+
+- 如果都没实现,则必须实现 Transform,将 Transform 封装成 Invoke,即入参转单帧流,出参 concat。
+
+
+
+如果用户通过 **Stream/Collect/Transform** 来调用 Graph,则 Graph 内部所有组件都以 Transform 范式来调用。如果某个组件,没有实现 Transform 范式,则 Eino 框架自动根据组件实现了的流式范式,封装出 Transform 调用范式,优先顺位如下:
+
+- 若组件实现了 Stream,则将 Stream 封装成 Transform,即自动 concat 输入流。
+
+
+
+- 否则,若组件实现了 Collect,则将 Collect 封装成 Transform,即非流式出参转单帧流。
+
+
+
+- 如果都没实现,则必须实现 Invoke,将 Invoke 封装成 Transform,即入参流 concat,出参转单帧流
+
+
+
+结合上面穷举的各种案例,Eino 框架对 T 和 Stream[T] 的自动转换,可以总结为:
+
+- **T -> Stream[T]: 将完整的 T 装箱为单帧的 Stream[T]。非流式变假流式。**
+- **Stream[T] -> T: 将 Stream[T] Concat 为完整的 T。当 Stream[T] 不是单帧流时,可能需要提供针对 T 的 Concat 方法。**
+
+看了上面的实现原理,可能会有疑问,为什么对 graph 的 Invoke,会要求所有内部组件都以 Invoke 调用?以及为什么对 graph 的 Stream/Collect/Transform,会要求所有内部组件都以 Transform 调用?毕竟,可以举出一些反例:
+
+- A, B 两个组件编排为一个 Chain,以 Invoke 调用。其中 A 的业务接口实现了 Stream,B 的业务接口实现了 Collect。这时 graph 内部组件的调用范式有两个选择:
+ - A 以 stream 调用,B 以 collect 调用,整体的 Chain 依然是 Invoke 语义,同时保留了真流式的内部语义。即 A 的输出流不需要做 Concat,可以实时的输入到 B 中。
+ - 目前 Eino 的实现,A、B 都以 Invoke 调用,需要把 A 的输出流 Concat,并把 B 的输入做成假流式。失去了真流式的内部语义。
+- A,B 两个组件编排为一个 Chain,以 Collect 调用。其中 A 实现了 Transform 和 Collect,B 实现了 Invoke。两个选择:
+ - A 以 Collect 调用,B 以 Invoke 调用:整体还是 Collect 的语义,不需要框架做任何的自动转化和装箱操作。
+ - 目前 Eino 的实现,A、B 都以 Transform 调用,由于 A 的业务接口里实现了 Transform,因此 A 的输出和 B 的输入都可能是真流式,而 B 的业务接口里只实现了 Invoke,根据上面的分析,B 的入参会需要由真流式 concat 成非流式。这时就需要用户额外提供 B 的入参的 concat 函数,这本可以避免。
+
+上面两个例子,都可以找到一个明确的、与 Eino 的约定不同的,但却更优的流式调用路径。但是,当泛化到任意的编排场景时,很难找到一个明确定义的、与 Eino 的约定不同的、却总是更优的普适的规则。比如,A->B->C,以 Collect 语义调用,是 A->B 的时候 Collect,还是 B->C 的时候 Collect?潜在的因素有 A、B、C 具体实现的业务接口,可能还有“尽量多的使用真流式”的判断,也许还有哪个参数实现了 Concat,哪个没有实现。如果是更复杂的 Graph,需要考虑的因素会快速增加。在这种情况下,即使框架能定义出一套明确的、更优的普适规则,也很难解释清楚,理解和使用成本会很高,很可能已经超过了这个新规则实际带来的好处。
+
+综上,我们可以说,Eino 编排产物内部各组件在运行时的范式,是 **By Design** 的,明确如下:
+
+- **整体以 Invoke 调用,内部各组件均以 Invoke 调用,不存在任何流式的过程。**
+- **整体以 Stream/Collect/Transform 调用,内部各组件均以 Transform 调用,当出现 Stream[T] -> T 的 concat 过程时,可能需要额外提供 T 的 concat function。**
diff --git a/docs/Eino/docs/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
new file mode 100644
index 0000000..6aac5ca
--- /dev/null
+++ b/docs/Eino/docs/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
@@ -0,0 +1,732 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Workflow 编排框架
+weight: 3
+---
+
+## 什么是 Eino Workflow
+
+是一套编排的 API,与 Graph API 在架构上处于同一层:
+
+```mermaid
+flowchart LR
+ E[Eino compose engine]
+ G[Graph API]
+ W[Workflow API]
+ C[Chain API]
+ E --> G
+ E --> W
+ G --> C
+```
+
+本质特点是:
+
+- 与 Graph API 具有同等级别的能力,都是编排“围绕大模型的信息流”的合适框架工具。
+ - 在节点类型、流处理、callback、option、state、interrupt & checkpoint 等方面保持一致。
+ - 实现 AnyGraph 接口,可以在 AddGraphNode 时作为子图加入上级 Graph/Chain/Workflow。
+ - 也可以把其他 Graph/Chain/Workflow 添加为自己的子图。
+- 字段级别映射能力:节点的输入可以由任意前驱节点的任意输出字段组合而成。
+ - 原生支持 struct,map 以及任意嵌套层级的 struct 和 map 之间的相互映射。
+- 控制流与数据流分离:Graph 的 Edge 是既决定执行顺序,又决定数据传递。Workflow 中可以一起传递,也可以分开传递。
+- 不支持环(即类似 react agent 的 chatmodel->toolsNode->chatmodel 的环路)。NodeTriggerMode 固定为 AllPredecessor。
+
+## 为什么用 Workflow
+
+### 灵活的输入输出类型
+
+例如需要编排两个 lambda 节点,里面是两个“现存的业务函数 f1, f2”,输入输出类型都是符合业务场景的特定结构体,各自不一样:
+
+
+
+Workflow 编排时,将 f1 的输出字段 F1,直接映射到 f2 的输入字段 F3,同时保留 f1,f2 的原始函数签名。达到的效果是:**每个节点是“业务场景决定输入输出”,不需要考虑“谁给我输入,以及谁用我的输出”**。
+
+Graph 编排时,因为“类型对齐”的要求,如果 f1 -> f2,则 f1 的输出类型和 f2 的输入类型需要对齐,需要二选一:
+
+- 定义一个新的 common struct,把 f1 的输出类型和 f2 的输入类型都改成这个 common struct。有成本,可能入侵业务逻辑。
+- f1 的输出类型和 f2 的输入类型都改成 map。丢失了强类型对齐的特性。
+
+### 控制流和数据流分离
+
+看下面这个场景:
+
+
+
+节点 D 同时引用了 A、B、C 的某些输出字段。其中 A-D 的这条虚线,是单纯的“数据流”,不传递“控制”信息,即 A 执行完成与否,不决定 D 是否开始执行。
+
+节点 D 到 E 之间的粗箭头,代表节点 E 不引用节点 D 的任何输出,是单纯的“控制流”,不传递“数据”。即 D 执行完成与否,决定 E 是否开始执行,但是 D 的输出不影响 E 的输入。
+
+图中其他的线,是控制流与数据流合一的。
+
+需要注意的是,数据流能传递的前提,是一定有一条控制流存在,比如 A->D 的数据流,依赖 A->branch->B->D 或者 A->branch->C->D 的控制流存在。即数据流只能引用前驱节点的输出。
+
+例如这个“跨节点”传递特定数据的场景:
+
+
+
+上图中,chat template 节点的输入可以是非常明确的:
+
+`map[string]any{"prompt": "prompt from START", "context": "retrieved context"}`
+
+相对的,如果使用 Graph 或者 Chain API,需要二选一:
+
+- 用 OutputKey 转换节点输出类型(START 节点没法加,所以得额外加 passthrough 节点),ChatTemplate 节点的输入会包含 START 和 retriever 的全量输出(而不是真正需要的某几个字段).
+- START 节点的 prompt 放到 state 里面,ChatTemplate 从 state 中读。额外引入了 state。
+
+## 如何使用 Workflow
+
+### 最简单的 workflow
+
+START -> node -> END
+
+
+
+```go
+// creates and invokes a simple workflow with only a Lambda node.
+// Since all field mappings are ALL to ALL mappings
+// (by using AddInput without field mappings),
+// this simple workflow is equivalent to a Graph: START -> lambda -> END.
+func main() {
+ // create a Workflow, just like creating a Graph
+ wf := compose.NewWorkflow[int, string]()
+
+ // add a lambda node to the Workflow, just like adding the lambda to a Graph
+ wf.AddLambdaNode("lambda", compose.InvokableLambda(
+ func(ctx context.Context, in int) (string, error) {
+ return strconv.Itoa(in), nil
+ })).
+ // add an input to this lambda node from START.
+ // this means mapping all output of START to the input of the lambda.
+ // the effect of AddInput is to set both a control dependency
+ // and a data dependency.
+ AddInput(compose.START)
+
+ // obtain the compose.END of the workflow for method chaining
+ wf.End().
+ // add an input to compose.END,
+ // which means 'using ALL output of lambda node as output of END'.
+ AddInput("lambda")
+
+ // compile the Workflow, just like compiling a Graph
+ run, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ // invoke the Workflow, just like invoking a Graph
+ result, err := run.Invoke(context.Background(), 1)
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ logs.Infof("%v", result)
+}
+```
+
+[Eino example 链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/1_simple/main.go)
+
+核心的几个 API:
+
+- `func NewWorkflow[I, O any](opts ...NewGraphOption) *Workflow[I, O]`
+ - 构建一个新的 Workflow。
+ - 与 `NewGraph` 签名完全一致。
+- `func (wf *Workflow[I, O]) AddChatModelNode(key string, chatModel model.BaseChatModel, opts ...GraphAddNodeOpt) *WorkflowNode `
+ - 向 Workflow 中添加一个新的节点。
+ - 可添加的节点类型与 Graph 完全一致。
+ - 与 Graph 的 AddXXXNode 的差异是,Workflow 不会立刻返回 error,而是在最终 Compile 的时候统一处理和返回 error。
+ - AddXXXNode 拿到的是一个 WorkflowNode,后续向 Node 上添加字段映射等操作,直接用 Method Chaining 来做
+- `func (n *WorkflowNode) AddInput(fromNodeKey string, inputs ...*FieldMapping) *WorkflowNode`
+ - 给一个 WorkflowNode 添加输入字段映射
+ - 返回 WorkflowNode,可继续 Method Chaining。
+- `(wf *Workflow[I, O]) Compile(ctx context.Context, opts ...GraphCompileOption) (Runnable[I, O], error)`
+ - Compile 一个 Workflow。
+ - 与 Compile Graph 的签名完全一致。
+
+### 字段映射
+
+START(输入 struct)-> [并行 lambda1, lambda2] -> END(输出 map)。
+
+我们举一个“计算 string 中字符出现次数的”例子。workflow 整体输入一个 eino 的 Message 和一个 sub string,将 Message.Content 给一个计数器 c1,将 Message.ReasoningContent 给另一个计数器 c2,并行分别计算 sub string 的出现次数,再分别映射到 END:
+
+
+
+上图中,workflow 整体的输入是 message 结构体,c1, c2 两个 lambda 的输入都是 counter 结构体,输出都是 int,workflow 整体输出是 map[string]any. 代码如下:
+
+```go
+// demonstrates the field mapping ability of eino workflow.
+func main() {
+ type counter struct {
+ FullStr string // exported because we will do field mapping for this field
+ SubStr string // exported because we will do field mapping for this field
+ }
+
+ // wordCounter is a lambda function that count occurrences of SubStr within FullStr
+ wordCounter := func(ctx context.Context, c counter) (int, error) {
+ return strings.Count(c.FullStr, c.SubStr), nil
+ }
+
+ type message struct {
+ *schema.Message // exported because we will do field mapping for this field
+ SubStr string // exported because we will do field mapping for this field
+ }
+
+ // create a workflow just like a Graph
+ wf := compose.NewWorkflow[message, map[string]any]()
+
+ // add lambda c1 just like in Graph
+ wf.AddLambdaNode("c1", compose.InvokableLambda(wordCounter)).
+ AddInput(compose.START, // add an input from START, specifying 2 field mappings
+ // map START's SubStr field to lambda c1's SubStr field
+ compose.MapFields("SubStr", "SubStr"),
+ // map START's Message's Content field to lambda c1's FullStr field
+ compose.MapFieldPaths([]string{"Message", "Content"}, []string{"FullStr"}))
+
+ // add lambda c2 just like in Graph
+ wf.AddLambdaNode("c2", compose.InvokableLambda(wordCounter)).
+ AddInput(compose.START, // add an input from START, specifying 2 field mappings
+ // map START's SubStr field to lambda c1's SubStr field
+ compose.MapFields("SubStr", "SubStr"),
+ // map START's Message's ReasoningContent field to lambda c1's FullStr field
+ compose.MapFieldPaths([]string{"Message", "ReasoningContent"}, []string{"FullStr"}))
+
+ wf.End(). // Obtain the compose.END for method chaining
+ // add an input from c1,
+ // mapping full output of c1 to the map key 'content_count'
+ AddInput("c1", compose.ToField("content_count")).
+ // also add an input from c2,
+ // mapping full output of c2 to the map key 'reasoning_content_count'
+ AddInput("c2", compose.ToField("reasoning_content_count"))
+
+ // compile the workflow just like compiling a Graph
+ run, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ // invoke the workflow just like invoking a Graph
+ result, err := run.Invoke(context.Background(), message{
+ Message: &schema.Message{
+ Role: schema.Assistant,
+ Content: "Hello world!",
+ ReasoningContent: "I need to say something meaningful",
+ },
+ SubStr: "o", // would like to count the occurrences of 'o'
+ })
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ logs.Infof("%v", result)
+}
+```
+
+[Eino example 代码链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/2_field_mapping/main.go)
+
+这个例子的主要信息是 `AddInput` 方法可以传递 0-n 个字段映射规则,同时可以多次调用 `AddInput`。这意味着:
+
+- 节点可以从一个前驱节点的输出中引用任意多个字段。
+- 节点可以从任意多个前驱节点中引用字段。
+- 一个映射,可以是“整体到字段”,可以是“字段到整体”,也可以是“整体到整体”,也可以是嵌套字段间的映射。
+- 上面不同的类型,有不同的 API 来表达这个映射:
+ - 顶层字段到顶层字段:`MapFields(string, string)`
+ - 全部输出到顶层字段:`ToField(string)`
+ - 顶层字段到全部输入:`FromField(string)`
+ - 嵌套字段到嵌套字段:`MapFieldPaths(FieldPath, FieldPath)`,只要上游或下游有一方是嵌套的,就需要用
+ - 全部输出到嵌套字段:`ToFieldPath(FieldPath)`
+ - 嵌套字段到全部输入:`FromFieldPath(FieldPath)`
+ - 全部输出到全部输入:直接使用 `AddInput`,不需要传 `FieldMapping`
+
+## 进阶功能
+
+### 只有数据流,没有控制流
+
+想象一个简单的场景:START -> 加法节点 -> 乘法节点 -> END。其中“乘法节点”是将 START 的一个字段和加法节点的结果相乘:
+
+
+
+上图中,乘法节点在加法节点之后执行,即“乘法节点”被“加法节点”控制。但 START 节点不直接控制“乘法节点”,仅仅把数据传了过去。在代码中通过 `AddInputWithOptions(fromNode, fieldMappings, WithNoDirectDependency)` 来指定纯数据流:
+
+```go
+func main() {
+ type calculator struct {
+ Add []int
+ Multiply int
+ }
+
+ adder := func(ctx context.Context, in []int) (out int, err error) {
+ for _, i := range in {
+ out += i
+ }
+ return out, nil
+ }
+
+ type mul struct {
+ A int
+ B int
+ }
+
+ multiplier := func(ctx context.Context, m mul) (int, error) {
+ return m.A * m.B, nil
+ }
+
+ wf := compose.NewWorkflow[calculator, int]()
+
+ wf.AddLambdaNode("adder", compose.InvokableLambda(adder)).
+ AddInput(compose.START, compose.FromField("Add"))
+
+ wf.AddLambdaNode("mul", compose.InvokableLambda(multiplier)).
+ AddInput("adder", compose.ToField("A")).
+ AddInputWithOptions(compose.START, []*compose.FieldMapping{compose.MapFields("Multiply", "B")},
+ // use WithNoDirectDependency to declare a 'data-only' dependency,
+ // in this case, START node's execution status will not determine whether 'mul' node can execute.
+ // START node only passes one field of its output to 'mul' node.
+ compose.WithNoDirectDependency())
+
+ wf.End().AddInput("mul")
+
+ runner, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ result, err := runner.Invoke(context.Background(), calculator{
+ Add: []int{2, 5},
+ Multiply: 3,
+ })
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ logs.Infof("%d", result)
+}
+```
+
+[Eino examples 代码链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/3_data_only/main.go)
+
+这个例子中新引入的 API:
+
+```go
+func (n *WorkflowNode) AddInputWithOptions(fromNodeKey string, inputs []*FieldMapping, opts ...WorkflowAddInputOpt) *WorkflowNode {
+ return n.addDependencyRelation(fromNodeKey, inputs, getAddInputOpts(opts))
+}
+```
+
+以及新的 Option:
+
+```go
+func WithNoDirectDependency() WorkflowAddInputOpt {
+ return func(opt *workflowAddInputOpts) {
+ opt.noDirectDependency = true
+ }
+}
+```
+
+组合起来,可以给节点添加纯“数据依赖关系”。
+
+### 只有控制流,没有数据流
+
+想象一个“依次竞拍,但报价保密”的场景:START -> 竞拍者 1 -> 是否达标 -> 竞拍者 2 -> END:
+
+
+
+在上图中,普通连线是“控制 + 数据”,虚线是“只有数据”,加粗线是“只有控制”。逻辑是:输入一个初始价格,竞拍者 1 给出报价 1,分支判断是否足够高,如果足够高则直接结束,否则把初始价格再给到竞拍者 2,给出报价 2,最后将报价 1、2 汇总输出。
+
+当竞拍者 1 给出报价后,发布公告”竞拍者完成竞拍“。注意 bidder1->announcer 是粗实线,“只有控制”,因为发布公告的时候需要对金额保密!
+
+分支出来的两条加粗线,都是“只有控制”,因为无论 bidder2 还是 END,都不依赖分支给出数据。在代码中通过 `AddDependency(fromNode)` 来指定纯控制流:
+
+```go
+func main() {
+ bidder1 := func(ctx context.Context, in float64) (float64, error) {
+ return in + 1.0, nil
+ }
+
+ bidder2 := func(ctx context.Context, in float64) (float64, error) {
+ return in + 2.0, nil
+ }
+
+ announcer := func(ctx context.Context, in any) (any, error) {
+ logs.Infof("bidder1 had lodged his bid!")
+ return nil, nil
+ }
+
+ wf := compose.NewWorkflow[float64, map[string]float64]()
+
+ wf.AddLambdaNode("b1", compose.InvokableLambda(bidder1)).
+ AddInput(compose.START)
+
+ // just add a node to announce bidder1 had lodged his bid!
+ // It should be executed strictly after bidder1, so we use `AddDependency("b1")`.
+ // Note that `AddDependency()` will only form control relationship,
+ // but not data passing relationship.
+ wf.AddLambdaNode("announcer", compose.InvokableLambda(announcer)).
+ AddDependency("b1")
+
+ // add a branch just like adding branch in Graph.
+ wf.AddBranch("b1", compose.NewGraphBranch(func(ctx context.Context, in float64) (string, error) {
+ if in > 5.0 {
+ return compose.END, nil
+ }
+ return "b2", nil
+ }, map[string]bool{compose.END: true, "b2": true}))
+
+ wf.AddLambdaNode("b2", compose.InvokableLambda(bidder2)).
+ // b2 executes strictly after b1 (through branch dependency),
+ // but does not rely on b1's output,
+ // which means b2 depends on b1 conditionally,
+ // but no data passing between them.
+ AddInputWithOptions(compose.START, nil, compose.WithNoDirectDependency())
+
+ wf.End().AddInput("b1", compose.ToField("bidder1")).
+ AddInput("b2", compose.ToField("bidder2"))
+
+ runner, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ result, err := runner.Invoke(context.Background(), 3.0)
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ logs.Infof("%v", result)
+}
+```
+
+[Eino examples 代码链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/4_control_only_branch/main.go)
+
+这个例子中引入的新 API:
+
+```go
+func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode {
+ return n.addDependencyRelation(fromNodeKey, nil, &workflowAddInputOpts{dependencyWithoutInput: _true_})
+}
+```
+
+可以通过 AddDependency 来给节点指定纯“控制依赖关系”。
+
+### 分支(Branch)
+
+在上面的例子中,我们用与 Graph API 几乎完全相同的方式添加了一个 branch:
+
+```go
+// add a branch just like adding branch in Graph.
+ wf.AddBranch("b1", compose.NewGraphBranch(func(ctx context.Context, in float64) (string, error) {
+ if in > 5.0 {
+ return compose.END, nil
+ }
+ return "b2", nil
+ }, map[string]bool{compose.END: true, "b2": true}))
+```
+
+branch 语义与 Graph 的 AllPredecessor 模式下的 branch 语义相同:
+
+- 有且只有一个'fromNode',即一个 branch 的前置控制节点只能有一个。
+- 可单选(NewGraphBranch),可多选(NewGraphMultiBranch)。
+- Branch 选中的分支,可执行。未选中的分支,标记为 skip。
+- 一个节点,只有在所有入边都完成(成功或 skip),且至少有一条边成功时,这个节点才可以执行。(如上面例子中的 END)
+- 如果一个节点的所有入边都是 skip,则这个节点的所有出边自动标为 skip。
+
+同时,workflow branch 与 graph branch 有一个核心差异:
+
+- Graph branch 始终是“控制和数据合一的”,branch 下游节点的输入,一定是 branch fromNode 的输出。
+- Workflow branch 始终是“只有控制的”,branch 下游节点的输入,自行通过 AddInputWithOptions 的方式指定。
+
+涉及到的新 API:
+
+```go
+func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *WorkflowBranch {
+ wb := &WorkflowBranch{
+ fromNodeKey: fromNodeKey,
+ GraphBranch: branch,
+ }
+
+ wf.workflowBranches = append(wf.workflowBranches, wb)
+ return wb
+}
+```
+
+与 Graph.AddBranch 签名几乎完全相同,可以给 workflow 添加一个分支。
+
+### 静态值(Static Values)
+
+让我们修改下上面的“竞拍”例子,给竞拍者 1 和竞拍者 2 分别给一个“预算”的静态配置:
+
+
+
+budget1 和 budget2 会分别以“静态值”的形式注入到 bidder1 和 bidder2 的 input 中。使用 `SetStaticValue` 方法给 workflow 节点配置静态值:
+
+```go
+func main() {
+ type bidInput struct {
+ Price float64
+ Budget float64
+ }
+
+ bidder := func(ctx context.Context, in bidInput) (float64, error) {
+ if in.Price >= in.Budget {
+ return in.Budget, nil
+ }
+
+ return in.Price + rand.Float64()*in.Budget, nil
+ }
+
+ wf := compose.NewWorkflow[float64, map[string]float64]()
+
+ wf.AddLambdaNode("b1", compose.InvokableLambda(bidder)).
+ AddInput(compose.START, compose.ToField("Price")).
+ // set 'Budget' field to 3.0 for b1
+ SetStaticValue([]string{"Budget"}, 3.0)
+
+ // add a branch just like adding branch in Graph.
+ wf.AddBranch("b1", compose.NewGraphBranch(func(ctx context.Context, in float64) (string, error) {
+ if in > 5.0 {
+ return compose.END, nil
+ }
+ return "b2", nil
+ }, map[string]bool{compose.END: true, "b2": true}))
+
+ wf.AddLambdaNode("b2", compose.InvokableLambda(bidder)).
+ // b2 executes strictly after b1, but does not rely on b1's output,
+ // which means b2 depends on b1, but no data passing between them.
+ AddDependency("b1").
+ AddInputWithOptions(compose.START, []*compose.FieldMapping{compose.ToField("Price")}, compose.WithNoDirectDependency()).
+ // set 'Budget' field to 4.0 for b2
+ SetStaticValue([]string{"Budget"}, 4.0)
+
+ wf.End().AddInput("b1", compose.ToField("bidder1")).
+ AddInput("b2", compose.ToField("bidder2"))
+
+ runner, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ result, err := runner.Invoke(context.Background(), 3.0)
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ logs.Infof("%v", result)
+}
+```
+
+[Eino examples 代码链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/5_static_values/main.go)
+
+这里涉及到的新 API:
+
+```go
+func (n *WorkflowNode) SetStaticValue(path FieldPath, value any) *WorkflowNode {
+ n.staticValues[path.join()] = value
+ return n
+}
+```
+
+通过这个方法给 Workflow 节点的指定字段上设置静态值。
+
+### 流式效果
+
+回到之前的“字符计数”例子,如果我们的 workflow 的输入不再是单个 message,而是一个 message 流,并且我们的计数函数可以对流中的每个 message chunk 分别计数并返回“计数流”:
+
+
+
+我们对之前的例子做一些修改:
+
+- InvokableLambda 改成 TransformableLambda,从而可以消费流,并产生流。
+- 把输入里面的 SubStr 改成静态值,注入到 c1 和 c2 中。
+- Workflow 的整体输入改成 *schema.Message。
+- 以 Transform 方式来调用 workflow,并传入包含 2 个 *schema.Message 的流。
+
+完成后的代码:
+
+```go
+// demonstrates the stream field mapping ability of eino workflow.
+// It's modified from 2_field_mapping.
+func main() {
+ type counter struct {
+ FullStr string // exported because we will do field mapping for this field
+ SubStr string // exported because we will do field mapping for this field
+ }
+
+ // wordCounter is a transformable lambda function that
+ // count occurrences of SubStr within FullStr, for each trunk.
+ wordCounter := func(ctx context.Context, c *schema.StreamReader[counter]) (
+ *schema.StreamReader[int], error) {
+ var subStr, cachedStr string
+ return schema.StreamReaderWithConvert(c, func(co counter) (int, error) {
+ if len(co.SubStr) > 0 {
+ // static values will not always come in the first chunk,
+ // so before the static value (SubStr) comes in,
+ // we need to cache the full string
+ subStr = co.SubStr
+ fullStr := cachedStr + co.FullStr
+ cachedStr = ""
+ return strings.Count(fullStr, subStr), nil
+ }
+
+ if len(subStr) > 0 {
+ return strings.Count(co.FullStr, subStr), nil
+ }
+ cachedStr += co.FullStr
+ return 0, schema.ErrNoValue
+ }), nil
+ }
+
+ // create a workflow just like a Graph
+ wf := compose.NewWorkflow[*schema.Message, map[string]int]()
+
+ // add lambda c1 just like in Graph
+ wf.AddLambdaNode("c1", compose.TransformableLambda(wordCounter)).
+ AddInput(compose.START, // add an input from START, specifying 2 field mappings
+ // map START's Message's Content field to lambda c1's FullStr field
+ compose.MapFields("Content", "FullStr")).
+ // we can set static values even if the input will be stream
+ SetStaticValue([]string{"SubStr"}, "o")
+
+ // add lambda c2 just like in Graph
+ wf.AddLambdaNode("c2", compose.TransformableLambda(wordCounter)).
+ AddInput(compose.START, // add an input from START, specifying 2 field mappings
+ // map START's Message's ReasoningContent field to lambda c1's FullStr field
+ compose.MapFields("ReasoningContent", "FullStr")).
+ SetStaticValue([]string{"SubStr"}, "o")
+
+ wf.End(). // Obtain the compose.END for method chaining
+ // add an input from c1,
+ // mapping full output of c1 to the map key 'content_count'
+ AddInput("c1", compose.ToField("content_count")).
+ // also add an input from c2,
+ // mapping full output of c2 to the map key 'reasoning_content_count'
+ AddInput("c2", compose.ToField("reasoning_content_count"))
+
+ // compile the workflow just like compiling a Graph
+ run, err := wf.Compile(context.Background())
+ if err != nil {
+ logs.Errorf("workflow compile error: %v", err)
+ return
+ }
+
+ // call the workflow using Transform just like calling a Graph with Transform
+ result, err := run.Transform(context.Background(),
+ schema.StreamReaderFromArray([]*schema.Message{
+ {
+ Role: schema.Assistant,
+ ReasoningContent: "I need to say something meaningful",
+ },
+ {
+ Role: schema.Assistant,
+ Content: "Hello world!",
+ },
+ }))
+ if err != nil {
+ logs.Errorf("workflow run err: %v", err)
+ return
+ }
+
+ var contentCount, reasoningCount int
+ for {
+ chunk, err := result.Recv()
+ if err != nil {
+ if err == io.EOF {
+ result.Close()
+ break
+ }
+
+ logs.Errorf("workflow receive err: %v", err)
+ return
+ }
+
+ logs.Infof("%v", chunk)
+
+ contentCount += chunk["content_count"]
+ reasoningCount += chunk["reasoning_content_count"]
+ }
+
+ logs.Infof("content count: %d", contentCount)
+ logs.Infof("reasoning count: %d", reasoningCount)
+}
+```
+
+[Eino examples 代码链接](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/6_stream_field_map/main.go)
+
+基于上面这个例子,我们总结出 workflow 流式的一些特点:
+
+- 依然是 100% 的 Eino stream:四种范式(invoke, stream, collect, transform),由 Eino 框架自动转换、复制、拼接、合并。
+- 字段映射的配置,不需要特殊处理流:无论实际的输入输出是不是流,AddInput 的写法都一样,Eino 框架负责处理基于流的映射。
+- 静态值,不需要特殊处理流:即使实际输入是个流,也可以一样的方式 SetStaticValue。Eino 框架会把静态值放在 input stream 中,但不一定是第一个读到的 chunk。
+
+### 字段映射各场景
+
+#### 类型对齐
+
+Workflow 遵循与 Graph 同一套类型对齐规则,只是对齐的粒度由完整的输入输出对齐,变为了映射成对的字段间的类型对齐。具体为:
+
+- 类型完全相同,在 Compile 时会校验通过,一定能对齐。
+- 类型不同,但上游可以 Assign 到下游(比如上游具体类型,下游 Any),在 Compile 时会校验通过,一定能对齐。
+- 上游无法 Assign 到下游(比如上游 int,下游 string),在 Compile 时会报错。
+- 上游可能能 Assign 到下游(比如上游 Any,下游 int),在 Compile 时无法确定,会推迟到执行时,取出上游的实际类型,再判断。此时如果判断上游不能 Assign 到下游,则会抛出 error。
+
+#### Merge 的各场景
+
+Merge 是指一个节点的输入映射自多个 `FieldMapping` 的情况。
+
+- 映射到多个不同的字段:支持
+- 映射到一个相同的字段:不支持
+- 映射到整体,同时也有映射到字段:冲突,不支持
+
+#### 嵌套的 map[string]any
+
+比如这个映射:`ToFieldPath([]string{"a","b"})`,目标节点的输入类型是 `map[string]any`,映射时的顺序是:
+
+1. 第一级“a”,此时的结果是 `map[string]any{"a": nil}`
+2. 第二级“b”,此时的结果是 `map[string]any{"a": map[string]any{"b": x}}`
+
+可以看到,在第二级的时候,Eino 框架自动把 any 替换为了实际的 `map[string]any`
+
+#### CustomExtractor
+
+有些场景,标准的字段映射语义无法支持,比如上游是 []int,想取出第一个元素映射到下游,此时我们用 `WithCustomExtractor` :
+
+```go
+t.Run("custom extract from array element", func(t *testing.T) {
+ wf := NewWorkflow[[]int, map[string]int]()
+ wf.End().AddInput(_START_, ToField("a", WithCustomExtractor(func(input any) (any, error) {
+ return input.([]int)[0], nil
+ })))
+ r, err := wf.Compile(context.Background())
+ assert.NoError(t, err)
+ result, err := r.Invoke(context.Background(), []int{1, 2})
+ assert.NoError(t, err)
+ assert.Equal(t, map[string]int{"a": 1}, result)
+})
+```
+
+当使用 WithCustomExtractor 时,一切 Compile 时的类型对齐校验都无法进行,只能推迟到执行时校验。
+
+### 一些约束
+
+- Map Key 的限制:只支持 string,或者 string alias(能 convert 到 string 的类型)。
+- 不支持的 CompileOption:
+ - `WithNodeTriggerMode`,因为固定为 `AllPredecessor`。
+ - `WithMaxRunSteps`,因为不会有环。
+- 如果映射来源是 Map Key,要求 Map 中必须有这个 key。但如果映射来源是 Stream,Eino 无法判断 stream 中的所有帧中是否至少有一次出现这个 key,因此 Stream 时无法校验。
+- 如果映射来源字段或者目标字段属于 struct ,则要求这些字段必须是导出的,因为内部使用了反射。
+- 映射来源是 nil:一般情况下支持,只有当映射目标不可能是 nil 时报错,比如目标是基础类型(int 等)。
+
+## 实际应用
+
+### Coze-Studio 工作流
+
+[Coze-Studio](https://github.com/coze-dev/coze-studio) 开源版的工作流引擎是基于 Eino Workflow 编排框架。参见:[11. 新增工作流节点类型(后端)](https://github.com/coze-dev/coze-studio/wiki/11.-%E6%96%B0%E5%A2%9E%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B%EF%BC%88%E5%90%8E%E7%AB%AF%EF%BC%89)
diff --git a/docs/Eino/docs/core_modules/components/_index.md b/docs/Eino/docs/core_modules/components/_index.md
new file mode 100644
index 0000000..2ca96d2
--- /dev/null
+++ b/docs/Eino/docs/core_modules/components/_index.md
@@ -0,0 +1,75 @@
+---
+Description: ""
+date: "2026-01-20"
+lastmod: ""
+tags: []
+title: Components 组件
+weight: 1
+---
+
+大模型应用开发和传统应用开发最显著的区别在于大模型所具备的两大核心能力:
+
+- **基于语义的文本处理能力**:能够理解和生成人类语言,处理非结构化的内容语义关系
+- **智能决策能力**:能够基于上下文进行推理和判断,做出相应的行为决策
+
+这两项核心能力催生了三种主要的应用模式:
+
+1. **直接对话模式**:处理用户输入并生成相应回答
+2. **知识处理模式**:对文本文档进行语义化处理、存储和检索
+3. **工具调用模式**:基于上下文做出决策并调用相应工具
+
+这些模式高度概括了当前大模型应用的主要场景,为我们提供了抽象和标准化的基础。基于此,Eino 将这些常用能力抽象为可复用的「组件」(Components)
+
+组件抽象和这几种模式关系对应如下:
+
+**对话处理类组件:**
+
+1. 模板化处理和大模型交互参数的组件抽象: `ChatTemplate`、`AgenticChatTemplate`
+
+ > 详见 [Eino: ChatTemplate 使用说明](/zh/docs/eino/core_modules/components/chat_template_guide)、[Eino: AgenticChatTemplate 使用说明[Beta]](/zh/docs/eino/core_modules/components/agentic_chat_template_guide)
+ >
+2. 直接和大模型交互的组件抽象: `ChatModel`、`AgenticModel`
+
+ > 详见 [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)、[Eino: AgenticModel 使用说明[Beta]](/zh/docs/eino/core_modules/components/agentic_chat_model_guide)
+ >
+
+**文本语义处理类组件:**
+
+1. 获取和处理文本文档的组件抽象: `Document.Loader` 、`Document.Transformer`
+
+ > 详见 [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide)、[Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide)
+ >
+2. 文本文档语义化处理的组件抽象: `Embedding`
+
+ > 详见 [Eino: Embedding 使用说明](/zh/docs/eino/core_modules/components/embedding_guide)
+ >
+3. Embedding 之后将数据索引进行存储的组件抽象: `Indexer`
+
+ > 详见 [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide)
+ >
+4. 将语义相关文本文档进行索引和召回的组件抽象: `Retriever`
+
+ > 详见 [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide)
+ >
+
+**决策执行类组件**:
+
+1. 大模型能够做决策并调用工具的组件抽象:`ToolsNode`、`AgenticToolsNode`
+
+ > 详见 [Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)、[Eino: AgenticToolsNode&Tool 使用说明[Beta]](/zh/docs/eino/core_modules/components/agentic_tools_node_guide)
+ >
+
+**自定义组件:**
+
+1. 用户自定义代码逻辑的组件抽象:`Lambda`
+
+ > 详见 [Eino: Lambda 使用说明](/zh/docs/eino/core_modules/components/lambda_guide)
+ >
+
+组件是大模型应用能力的提供者,是大模型应用构建过程中的砖和瓦,组件抽象的优劣决定了大模型应用开发的复杂度,Eino 的组件抽象秉持着以下设计原则:
+
+1. **模块化和标准化**,将一系列功能相同的能力抽象成统一的模块,组件间职能明确、边界清晰,支持灵活地组合。
+2. **可扩展性**,接口的设计保持尽可能小的模块能力约束,让组件的开发者能方便地实现自定义组件的开发。
+3. **可复用性**,把最常用的能力和实现进行封装,提供给开发者开箱即用的工具使用。
+
+组件的抽象可以让大模型应用开发形成比较固定的范式,降低认知复杂度,增强共同协作的效率。让组件的封装让开发者可以专注于业务逻辑的实现,避免重复造轮子,以快速构建高质量的大模型应用。
diff --git a/docs/Eino/docs/core_modules/components/agentic_chat_model_guide.md b/docs/Eino/docs/core_modules/components/agentic_chat_model_guide.md
new file mode 100644
index 0000000..55b36bf
--- /dev/null
+++ b/docs/Eino/docs/core_modules/components/agentic_chat_model_guide.md
@@ -0,0 +1,1191 @@
+---
+Description: ""
+date: "2026-03-03"
+lastmod: ""
+tags: []
+title: AgenticModel 使用说明[Beta]
+weight: 10
+---
+
+> 💡
+> 本功能在 [v0.9](https://github.com/cloudwego/eino/releases/tag/v0.9.0-alpha.2) 版本开始提供。
+
+## 基本介绍
+
+AgenticModel 是一种以 “目标驱动的自主执行” 为核心的模型能力抽象。随着缓存、内置工具等能力在 OpenAI Responses API、Claude API 等先进厂商的 API 中得到原生支持,模型正在从 “一次性问答引擎” 升级为 “面向用户目标的自主行动体”:能够围绕目标进行闭环规划、调用工具与迭代执行,从而完成更复杂的任务。
+
+### 与 ChatModel 差异
+
+| AgenticModel | ChatModel | |
| 定位 | 基于 AgenticMessage 的 Model 组件抽象 | 基于 Message 的 Model 组件抽象 |
| 核心实体 | Message | |
| 能力 | ||
| 相关组件 |
| AgenticModel | ChatModel | |
| Temperature | 支持 | 支持 |
| Model | 支持 | 支持 |
| TopP | 支持 | 支持 |
| Tools | 支持 | 支持 |
| ToolChoice | 支持 | 支持 |
| MaxTokens | 支持 | 支持 |
| AllowedToolNames | 不支持 | 支持 |
| Stop | 部分组件实现支持 | 支持 |
| AllowedTools | 支持 | 不支持 |
+
+## 如何安装
+
+### 版本安装依赖
+
+ | Plugin Version | GoLand IDE Version | VS Code Version | eino-ext/devops Version |
| 1.1.0 | 2023.2+ | 1.97.x | 0.1.0 |
| 1.0.7 | 2023.2+ | - | 0.1.0 |
| 1.0.6 | 2023.2+ | - | 0.1.0 |
| 1.0.5 | 2023.2+ | - | 0.1.0 |
| 1.0.4 | 2023.2+ | - | 0.1.0 |
+ |
+ |
+
+## 功能简介
+
+> 💡
+> **插件安装完毕** ✅,**接下来就可以体验插件提供的调试与编排能力了** ~
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+### 配置调试地址
+
+
+1.点击左侧或正中间调试功能进入调试配置
+
+ |
+2.点击配置调试地址
+
+ |
+3.填入 127.0.0.1:52538
+
+ |
+4.点击确认进入调试界面,选择要调试的Graph
+
+ |
+1.点击「Test Run」从 start 节点开始执行
+
+ |
+2.输入 "hello eino",点击确认
+
+
+ |
+3.在调试区域展示有各个节点的输入和输出
+
+ |
+4.点击 Input 和 Output 切换查看节点信息
+
+ |
+
+### 编排拓扑可视化
+
+支持 Graph 和 Chain 编排拓扑可视化。
+
+
+
+### 从任意节点开始调试
+
+
+
+### 查看节点执行结果
+
+每个节点执行结果都会按执行顺序展示在调试区域,包括:输入、输出、执行耗时
+
+
+
+## 从零开始调试
+
+### 使用 Eino 进行编排
+
+插件支持对 Graph 和 Chain 的编排产物进行调试,假设你已经有编排代码如下
+
+```go
+func RegisterSimpleGraph(ctx context.Context) {
+ g := compose.NewGraph[string, string]()
+ _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_1,", nil
+ }))
+ _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_2,", nil
+ }))
+ _ = g.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_3,", nil
+ }))
+
+ _ = g.AddEdge(compose.START, "node_1")
+ _ = g.AddEdge("node_1", "node_2")
+ _ = g.AddEdge("node_2", "node_3")
+ _ = g.AddEdge("node_3", compose.END)
+
+ _, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("compile graph failed, err=%v", err)
+ return
+ }
+}
+```
+
+### 安装依赖
+
+在项目目录下依次执行以下指令
+
+```bash
+# 1. Pull latest devops repository
+go get github.com/cloudwego/eino-ext/devops@latest
+
+# 2. Cleans and updates go.mod and go.sum
+go mod tidy
+```
+
+### 调用调试初始化函数
+
+因为调试需要在用户主进程中启动一个 HTTP 服务,以用作与本地调试插件交互,所以用户需要主动调用一次 _github.com/cloudwego/eino-ext/devops_ 中的 `Init()` 来启动调试服务。
+
+> 💡
+> 注意事项
+>
+> 1. 确保目标调试的编排产物至少执行过一次 `Compile()`。
+> 2. `devops.Init()` 的执行必须要在调用 `Compile()` 之前。
+> 3. 用户需要保证 `devops.Init()` 执行后主进程不能退出。
+
+如在 `main()` 函数中增加调试服务启动代码
+
+```go
+// 1.调用调试服务初始化函数
+err := devops.Init(ctx)
+if err != nil {
+ logs.Errorf("[eino dev] init failed, err=%v", err)
+ return
+}
+
+// 2.编译目标调试的编排产物
+RegisterSimpleGraph(ctx)
+```
+
+### 运行用户进程
+
+在本地电脑或者远程环境中运行你的进程,并保证主进程不会退出。
+
+在 github.com/cloudwego/eino-examples/devops/debug/main.go 中,`main()` 代码如下
+
+```go
+func main() {
+ ctx := context.Background()
+ // Init eino devops server
+ err := devops.Init(ctx)
+ if err != nil {
+ logs.Errorf("[eino dev] init failed, err=%v", err)
+ return
+ }
+
+ // Register chain, graph and state_graph for demo use
+ chain.RegisterSimpleChain(ctx)
+ graph.RegisterSimpleGraph(ctx)
+ graph.RegisterSimpleStateGraph(ctx)
+
+ // Blocking process exits
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+ <-sigs
+
+ // Exit
+ logs.Infof("[eino dev] shutting down\n")
+}
+```
+
+### 配置调试地址
+
+- **IP**:用户进程所在服务器的 IP 地址。
+ - 用户进程运行在本地电脑,则填写 `127.0.0.1`;
+ - 用户进程运行在远程服务器上,则填写远程服务器的 IP 地址,兼容 IPv4 和 IPv6 。
+- **Port**:调试服务监听的端口,默认是 `52538`,可通过 「WithDevServerPort」 这一 option 方法进行修改
+
+> 💡
+> 注意事项
+>
+> - 本地电脑调试:系统可能会弹出网络接入警告,允许接入即可。
+> - 远程服务器调试:需要你保证端口可访问。
+
+IP 和 Port 配置完成后,点击确认,调试插件会自动连接到目标调试服务器。如果成功连接,连接状态指示器会变成绿色。
+
+
+
+### 选择目标调试编排产物
+
+确保你目标调试的编排产物至少执行过一次 `Compile()`。因为调试设计是面向编排产物实例,所以如果多次执行 `Compile()`,会在调试服务中注册多个编排产物,继而在选择列表中看到多个可调试目标。
+
+
+
+### 开始调试
+
+调试支持从任意节点开始调试,包括 start 节点和其他中间节点。
+
+- 从 START 节点开始调试:直接点击 「Test Run」,然后输入 mock 的 input(如果 input 是复杂结构的话,会自动对 input 的结构进行推断)然后点击确定,开始执行你的 graph,每个 node 的结果会在下方显示。
+
+
+
+
+
+- 从任意的可操作节点开始调试:比如,从第二个节点开始执行。
+
+
+
+
+
+### 查看执行结果
+
+从 START 节点开始调试,点击 Test Run 后,在插件下方查看调试结果。
+
+
+
+从任意的可操作节点进行调试,在插件下方查看调试结果。
+
+
+
+## 高阶功能
+
+### 指定 interface 字段的实现类型
+
+对于 interface 类型的字段,会被默认渲染为 `{}` 。在 `{}` 中输入空格可唤出 interface 实现类型的列表,选中某个类型后,系统会生成一个特殊的结构体以表达 interface 的信息;该特殊结构体定义如下:
+
+```go
+{
+ "_value": {} // 按具体类型生成的 json value
+ "_eino_go_type": "*model.MyConcreteType" // Go 类型名
+}
+```
+
+> 💡
+> 系统内已经内置了一些常见的 interface 类型,如 `string`、`schema.Message` 等,可直接选择使用。如果需要自定义 interface 实现类型,可通过 `devops` 提供的 `AppendType` 方法进行注册。
+
+1. 假设你已经有编排代码如下,其中,graph 的输入定义为 `any`,`node_1` 的输入定义为 `*NodeInfo`;
+
+ ```go
+ type NodeInfo struct {
+ Message string
+ }
+
+ func RegisterGraphOfInterfaceType(ctx context.Context) {
+ // Define a graph that input parameter is any.
+ g := compose.NewGraph[any, string]()
+
+ _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input *NodeInfo) (output string, err error) {
+ if input == nil {
+ return "", nil
+ }
+ return input.Message + " process by node_1,", nil
+ }))
+
+ _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_2,", nil
+ }))
+
+ _ = g.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_3,", nil
+ }))
+
+ _ = g.AddEdge(compose._START_, "node_1")
+
+ _ = g.AddEdge("node_1", "node_2")
+
+ _ = g.AddEdge("node_2", "node_3")
+
+ _ = g.AddEdge("node_3", compose._END_)
+
+ r, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("compile graph failed, err=%v", err)
+ return
+ }
+ }
+ ```
+2. 调试前,通过 `AppendType` 方法在 `Init()` 时注册自定义的 `*NodeInfo` 类型:
+
+ ```go
+ err := devops.Init(ctx, devops.AppendType(&graph.NodeInfo{}))
+ ```
+3. 调试过程中,在 Test Run 的 Json 输入框中,对于 interface 类型的字段,默认会呈现为 `{}`。可以通过在 `{}` 中键入一个空格,来查看所有内置的以及自定义注册的数据类型,并选择该 interface 的具体实现类型。
+
+
+
+1. 在 `_value` 字段中补全调试节点输入。
+
+
+
+1. 点击确认,查看调试结果。
+
+
+
+#### map[string]any 调试
+
+这里再解释下输入类型为 map[string]any 时如何调试;如果某个节点的输入类型为 map[string]any,如下所示:
+
+```go
+func RegisterAnyInputGraph(ctx context.Context) {
+ g := compose.NewGraph[map[string]any, string]()
+
+ _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input map[string]any) (output string, err error) {
+ for k, v := range input {
+ switch v.(type) {
+ case string:
+ output += k + ":" + v.(string) + ","
+ case int:
+ output += k + ":" + fmt.Sprintf("%d", v.(int))
+ default:
+ return "", fmt.Errorf("unsupported type: %T", v)
+ }
+ }
+
+ return output, nil
+ }))
+
+ _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_2,", nil
+ }))
+
+ _ = g.AddEdge(compose.START, "node_1")
+
+ _ = g.AddEdge("node_1", "node_2")
+
+ _ = g.AddEdge("node_2", compose.END)
+
+ r, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("compile graph failed, err=%v", err)
+ return
+ }
+
+ message, err := r.Invoke(ctx, map[string]any{"name": "bob", "score": 100})
+ if err != nil {
+ logs.Errorf("invoke graph failed, err=%v", err)
+ return
+ }
+
+ logs.Infof("eino any input graph output is: %v", message)
+}
+```
+
+调试过程中,在 Test Run 的 Json 输入框中,你需要输入以下格式的内容:
+
+```json
+{
+ "name": {
+ "_value": "alice",
+ "_eino_go_type": "string"
+ },
+ "score": {
+ "_value": "99",
+ "_eino_go_type": "int"
+ }
+}
+```
diff --git a/docs/Eino/docs/core_modules/devops/visual_orchestration_plugin_guide.md b/docs/Eino/docs/core_modules/devops/visual_orchestration_plugin_guide.md
new file mode 100644
index 0000000..611a9a8
--- /dev/null
+++ b/docs/Eino/docs/core_modules/devops/visual_orchestration_plugin_guide.md
@@ -0,0 +1,114 @@
+---
+Description: ""
+date: "2025-12-03"
+lastmod: ""
+tags: []
+title: Eino Dev 可视化编排插件功能指南
+weight: 2
+---
+
+## 简介
+
+> 💡
+> Goland 提供的 Eino 可视化编排插件, 在 GoLand 中可以通过组件拖拽实现 Graph 的编排生成代码,并支持导入导出
+
+## 初认插件
+
+### 插件功能介绍
+
+
+
+## 编排组件介绍
+
+### 图 ( Graph )
+
+- 与 Eino 中的 Graph 概念一致,指最终由插件侧生成的 Graph,可在以下界面添加 Graph。
+- 点击添加插件,则弹出创建对话框,根据字段说明补充配置信息,即可生成一个 Graph 编排对象。
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+### 创建并编排 Graph
+
+- 界面左下角新增 Graph,在弹窗对话框填写 Graph 相关配置,生成 Graph 画布。
+- 按需从 AddNodes 选择合适的 Node 组件,添加的画布。
+- 依据业务编排逻辑将 Node 组件连接,完成 Graph 业务编排逻辑。
+
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
| 字段 | 类型 | 必填 | 说明 |
| PatchedContentGenerator | func(ctx, toolName, toolCallID string) (string, error) | 否 | 自定义生成占位符消息内容的函数。参数包含工具名和调用 ID,返回要填充的内容 |
+
+**处理逻辑:**
+
+1. 在 `BeforeModelRewriteState` 钩子中执行
+2. 遍历所有消息,查找包含 `ToolCalls` 的 Assistant 消息
+3. 对于每个 ToolCall,检查后续消息中是否存在对应的 Tool 消息(通过 `ToolCallID` 匹配)
+4. 如果找不到对应的 Tool 消息,则插入一个占位符消息
+5. 返回修复后的消息列表
+
+## 示例场景
+
+### 修复前的消息历史
+
+```
+[User] "帮我查询天气"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: 晴天,25°C"
+[User] "不用查位置了,直接告诉我北京的天气" <- 用户中断
+```
+
+### 修复后的消息历史
+
+```
+[User] "帮我查询天气"
+[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}]
+[Tool] "call_1: 晴天,25°C"
+[Tool] "call_2: 工具调用 get_location(ID 为 call_2)已被取消..." <- 自动插入
+[User] "不用查位置了,直接告诉我北京的天气"
+```
+
+## 多语言支持
+
+占位符消息支持中英文,通过 `adk.SetLanguage()` 切换:
+
+```go
+import "github.com/cloudwego/eino/adk"
+
+adk.SetLanguage(adk.LanguageChinese) // 中文
+adk.SetLanguage(adk.LanguageEnglish) // 英文(默认)
+```
+
+## 注意事项
+
+> 💡
+> 此中间件仅在 `BeforeModelRewriteState` 钩子中修改本次运行的历史消息,不会影响实际存储的消息历史。修复只是临时的,仅用于本轮 agent 调用。
+
+- 建议将此中间件放在中间件链的**前面**,确保其他中间件处理的是完整的消息历史
+- 如果你的场景需要持久化修复后的消息,请在 `PatchedContentGenerator` 中实现相应逻辑
diff --git a/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md b/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
new file mode 100644
index 0000000..daf5662
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md
@@ -0,0 +1,292 @@
+---
+Description: ""
+date: "2026-03-09"
+lastmod: ""
+tags: []
+title: PlanTask
+weight: 6
+---
+
+# PlanTask 中间件
+
+adk/middlewares/plantask
+
+> 💡
+> 本中间件在 v0.8.0 版本引入。
+
+## 概述
+
+`plantask` 是一个任务管理中间件,让 Agent 可以创建和管理任务列表。中间件通过 `BeforeAgent` 钩子注入四个工具:
+
+- **TaskCreate**: 创建任务
+- **TaskGet**: 查看任务详情
+- **TaskUpdate**: 更新任务
+- **TaskList**: 列出所有任务
+
+主要用途:
+
+- 跟踪复杂任务的进度
+- 把大任务拆成小步骤
+- 管理任务间的依赖关系
+
+---
+
+## 架构
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ Agent │
+│ │
+│ ┌───────────────────────────────────────────────────────────────────┐ │
+│ │ BeforeAgent: 注入任务工具 │ │
+│ │ - TaskCreate │ │
+│ │ - TaskGet │ │
+│ │ - TaskUpdate │ │
+│ │ - TaskList │ │
+│ └───────────────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ Backend │
+│ │
+│ 存储结构: │
+│ baseDir/ │
+│ ├── .highwatermark # ID 计数器 │
+│ ├── 1.json # 任务 #1 │
+│ ├── 2.json # 任务 #2 │
+│ └── ... │
+│ │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 配置
+
+```go
+type Config struct {
+ Backend Backend // 存储后端,必填
+ BaseDir string // 任务文件目录,必填
+}
+```
+
+- 注意这个 Backend 的实现,应该是 session 维度隔离的,不同的 session 对应不同的 Backend(任务列表)
+
+---
+
+## Backend 接口
+
+```go
+type Backend interface {
+ LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error)
+ Read(ctx context.Context, req *ReadRequest) (string, error)
+ Write(ctx context.Context, req *WriteRequest) error
+ Delete(ctx context.Context, req *DeleteRequest) error
+}
+```
+
+---
+
+## 任务结构
+
+```go
+type task struct {
+ ID string `json:"id"` // 任务 ID
+ Subject string `json:"subject"` // 标题
+ Description string `json:"description"` // 描述
+ Status string `json:"status"` // 状态
+ Blocks []string `json:"blocks"` // 阻塞哪些任务
+ BlockedBy []string `json:"blockedBy"` // 被哪些任务阻塞
+ ActiveForm string `json:"activeForm"` // 进行时文案
+ Owner string `json:"owner"` // 负责 agent
+ Metadata map[string]any `json:"metadata"` // 自定义数据
+}
+```
+
+### 状态
+
+| 状态 | 说明 |
pending | 待处理(默认) |
in_progress | 进行中 |
completed | 已完成 |
deleted | 删除(会删掉文件) |
| 参数 | 类型 | 必填 | 说明 |
subject | string | 是 | 标题 |
description | string | 是 | 描述 |
activeForm | string | 否 | 进行时文案,比如"正在运行测试" |
metadata | object | 否 | 自定义数据 |
| 参数 | 类型 | 必填 | 说明 |
taskId | string | 是 | 任务 ID |
| 参数 | 类型 | 必填 | 说明 |
taskId | string | 是 | 任务 ID |
subject | string | 否 | 新标题 |
description | string | 否 | 新描述 |
activeForm | string | 否 | 新的进行时文案 |
status | string | 否 | 新状态 |
addBlocks | []string | 否 | 添加被阻塞的任务 |
addBlockedBy | []string | 否 | 添加阻塞自己的任务 |
owner | string | 否 | 负责 agent |
metadata | object | 否 | 自定义数据(设 null 删除) |
| 字段 | 类型 | 说明 |
Name | string | Skill 的唯一标识符。Agent 通过此名称调用 Skill ,建议使用简短、有意义的名称(如 pdf-processing、 web-research)。对应 SKILL.md 中 frontmatter 的 name字段 |
Description | string | Skill 的功能描述。这是 Agent 判断是否使用该 Skill 的关键依据,应清晰说明技 Skill 能适用的场景和能力。对应 SKILL.md 中 frontmatter 的 description字段 |
Context | ContextMode | 上下文模式。可选值:fork_with_context(复制历史消息创建新 Agent 执行)、 fork(隔离上下文创建新 Agent 执行)。留空表示内联模式(直接返回 Skill 内容) |
Agent | string | 指定使用的 Agent 名称。配合 Context字段使用,通过 AgentHub获取对应的 Agent 工厂函数。留空时使用默认 Agent |
Model | string | 指定使用的模型名称。通过 ModelHub获取对应的模型实例。在 Context 模式下传递给 Agent 工厂;在内联模式下切换后续 ChatModel 调用使用的模型 |
| 模式 | 说明 |
| 内联(默认) | Skill 内容直接作为工具结果返回,由当前 Agent 继续处理 |
| ForkWithContext | 创建新 Agent,复制当前对话历史,独立执行 Skill 任务后返回结果 |
| Fork | 创建新 Agent,使用隔离的上下文(仅包含 Skill 内容),独立执行后返回结果 |
| 字段 | 类型 | 说明 |
FrontMatter | FrontMatter | 嵌入的元数据结构,包含 Name、 Description、 Context、 Agent、 Model |
Content | string | SKILL.md 文件中 frontmatter 之后的正文内容。包含 Skill 的详细指令、工作流程、示例等,Agent 激活 Skill 后会读取此内容 |
BaseDirectory | string | Skill 目录的绝对路径。Agent 可以使用此路径访问 Skill 目录中的其他资源文件(如脚本、模板、参考文档等) |
| 方法 | 说明 |
List | 列出所有可用技能的元数据。在 Agent 启动时调用,用于构建技能工具的描述信息,让 Agent 知道有哪些技能可用 |
Get | 根据名称获取完整的技能内容。当 Agent 决定使用某个技能时调用,返回包含详细指令的完整 Skill 结构 |
| 字段 | 类型 | 必需 | 说明 |
| Backend | filesystem.Backend | 是 | 文件系统后端实现,用于文件操作 |
| BaseDir | string | 是 | 技能根目录的路径。会扫描此目录下的所有一级子目录,查找包含 SKILL.md文件的目录作为技能 |
| 字段 | 类型 | 必需 | 默认值 | 说明 |
Backend | Backend | 是 | 技能后端实现。负责技能的存储和检索,可使用内置的 LocalBackend或自定义实现 | |
SkillToolName | *string | 否 | "skill" | 技能工具的名称。Agent 通过此名称调用技能工具。如果你的 Agent 已有同名工具,可以通过此字段自定义名称避免冲突 |
AgentHub | AgentHub | 否 | 提供 Agent 工厂函数。当 Skill 使用 context: fork或 context: isolate时必填 | |
ModelHub | ModelHub | 否 | 提供模型实例。当 Skill 指定 model字段时使用 | |
CustomSystemPrompt | SystemPromptFunc | 否 | 内置提示词 | 自定义系统提示词函数 |
CustomToolDescription | ToolDescriptionFunc | 否 | 内置描述 | 自定义工具描述函数 |
+
+> 💡
+> Skill Middleware 仅提供了如上图所示的加载 SKILL.md 能力,如果 Skill 需要 agent 具备读取文件、执行脚本等能力,需要用户另外为 agent 配置。
diff --git a/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md b/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
new file mode 100644
index 0000000..f59fe4d
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md
@@ -0,0 +1,210 @@
+---
+Description: ""
+date: "2026-03-09"
+lastmod: ""
+tags: []
+title: Summarization
+weight: 4
+---
+
+## 概述
+
+Summarization 中间件会在对话的 token 数量超过配置阈值时,自动压缩对话历史。这有助于在长对话中保持上下文连续性,同时控制在模型的 token 限制范围内。
+
+> 💡
+> 本中间件在 v0.8.0 版本引入。
+
+## 快速开始
+
+```go
+import (
+ "context"
+ "github.com/cloudwego/eino/adk/middlewares/summarization"
+)
+
+// 使用最小配置创建中间件
+mw, err := summarization.New(ctx, &summarization.Config{
+ Model: yourChatModel, // 必填:用于生成摘要的模型
+})
+if err != nil {
+ // 处理错误
+}
+
+// 与 ChatModelAgent 一起使用
+agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Model: yourChatModel,
+ Middlewares: []adk.ChatModelAgentMiddleware{mw},
+})
+```
+
+## 配置项
+
+| 字段 | 类型 | 必填 | 默认值 | 说明 |
| Model | model.BaseChatModel | 是 | 用于生成摘要的聊天模型 | |
| ModelOptions | []model.Option | 否 | 传递给模型生成摘要时的选项 | |
| TokenCounter | TokenCounterFunc | 否 | 约 4 字符/token | 自定义 token 计数函数 |
| Trigger | *TriggerCondition | 否 | 190,000 tokens | 触发摘要的条件 |
| UserInstruction | string | 否 | 内置 prompt | 自定义摘要指令 |
| TranscriptFilePath | string | 否 | 完整对话记录文件路径 | |
| GenModelInput | GenModelInputFunc | 否 | 自定义摘要模型输入的预处理函数 | |
| Finalize | FinalizeFunc | 否 | 自定义最终消息的后处理函数 | |
| Callback | CallbackFunc | 否 | 在 Finalize 之后调用,用于观察状态变化(只读) | |
| EmitInternalEvents | bool | 否 | false | 是否发送内部事件 |
| PreserveUserMessages | *PreserveUserMessages | 否 | Enabled: true | 是否在摘要中保留原始用户消息 |
| 事件类型 | 触发时机 | 携带数据 |
| ActionTypeBeforeSummarize | 生成摘要之前 | 原始消息列表 |
| ActionTypeAfterSummarize | 完成总结之后 | 最终消息列表 |
| 参数 | 类型 | 必填 | 说明 |
regex_pattern | string | 是 | 匹配工具名的正则表达式 |
| 特性 | AgentMiddleware (结构体) | ChatModelAgentMiddleware (接口) |
| 扩展性 | 封闭,用户无法添加新方法 | 开放,用户可实现自定义 handler |
| Context 传播 | 回调只返回 error | 所有方法返回 (context.Context, ..., error) |
| 配置管理 | 分散在闭包中 | 集中在结构体字段中 |
| 方法 | 输入 | 输出 | 作用范围 |
BeforeAgent | Agent 运行环境 (*ChatModelAgentContext) | 修改后的 Agent 运行环境 | 整个 Run 生命周期,仅调用一次 |
BeforeModelRewriteState | 持久化状态 + Model 运行环境 | 修改后的持久化状态 | 跨 iteration 的持久化状态(消息列表) |
WrapModel | 被 wrap 的 ChatModel + Model 运行环境 | 包装后的 Model | 单次 Model 请求的输入、输出和配置 |
AfterModelRewriteState | 持久化状态(含响应)+ Model 运行环境 | 修改后的持久化状态 | 跨 iteration 的持久化状态(消息列表) |
WrapInvokableToolCall | 被 wrap 的 Tool + Tool 运行环境 | 包装后的 endpoint | 单次 Tool 请求的输入、输出和配置 |
WrapStreamableToolCall | 被 wrap 的 Tool + Tool 运行环境 | 包装后的 endpoint | 单次 Tool 请求的输入、输出和配置 |
| 类型 | 描述 |
FileInfo | 文件/目录信息:路径、是否目录、大小、修改时间 |
FileContent | 文件内容 + 行号信息 |
GrepMatch | 搜索匹配结果:内容、路径、行号 |
ReadRequest | 读取请求:路径、offset(从第几行开始,1-based)、limit(读取行数) |
GrepRequest | 搜索请求:pattern(支持正则)、路径、glob 过滤、文件类型过滤等 |
WriteRequest | 写入请求:路径、内容 |
EditRequest | 编辑请求:路径、旧字符串、新字符串、是否全部替换 |
ExecuteRequest | 命令执行请求:命令字符串、是否后台运行 |
ExecuteResponse | 命令执行结果:输出内容、退出码、是否被截断 |
| 特性 | InMemory | Local | Agentkit Sandbox |
| 执行模型 | 内存 | 本地直接 | 远程沙箱 |
| 网络依赖 | 无 | 无 | 需要 |
| 配置复杂度 | 零配置 | 零配置 | 需要凭证 |
| 持久化 | 否 | 是 | 是 |
| Shell 支持 | 否 | 支持(含流式) | 支持 |
| 适用场景 | 测试/临时 | 开发/本地环境 | 多租户/生产环境 |
| 方法 | 描述 |
| LsInfo | 列出目录内容 |
| Read | 读取文件内容(支持分页) |
| Write | 创建新文件(已存在则报错) |
| Edit | 替换文件内容 |
| GrepRaw | 搜索文件内容 |
| GlobInfo | 按模式查找文件 |
| Execute | 执行 shell 命令 |
| 特性 | Agentkit | Local |
| 执行模型 | 远程沙箱 | 本地直接 |
| 网络依赖 | 需要 | 不需要 |
| 配置复杂度 | 需要凭证 | 零配置 |
| 安全模型 | 隔离沙箱 | OS 权限 |
| 适用场景 | 多租户/生产环境 | 开发/本地环境 |
| 方法 | 描述 |
| LsInfo | 列出目录内容 |
| Read | 读取文件内容(支持分页,默认 200 行) |
| Write | 创建新文件(已存在则报错) |
| Edit | 替换文件内容 |
| GrepRaw | 搜索文件内容(字面量匹配) |
| GlobInfo | 按模式查找文件 |
| Execute | 执行 shell 命令 |
| ExecuteStreaming | 流式执行命令 |
| 特性 | Local | Agentkit |
| 执行模型 | 本地直接 | 远程沙箱 |
| 网络依赖 | 无 | 需要 |
| 配置复杂度 | 零配置 | 需要凭证 |
| 安全模型 | OS 权限 | 隔离沙箱 |
| 流式输出 | 支持 | 不支持 |
| 平台支持 | Unix/Linux/macOS | 任意 |
| 适用场景 | 开发/本地环境 | 多租户/生产环境 |
| 参数 | 类型 | 必填 | 默认值 | 说明 |
Backend | Backend | 是 | - | 文件读取后端,负责实际的文件 I/O |
AgentsMDFiles | []string | 是 | - | 要加载的 Agents.md 文件路径列表(至少一个) |
AllAgentsMDMaxBytes | int | 否 | 0(无限制) | 所有文件的总字节数上限 |
OnLoadWarning | func(string, error) | 否 | log.Printf | 非致命错误的回调函数 |
| 场景 | 行为 |
文件不存在 (os.ErrNotExist) | 跳过该文件,触发 OnLoadWarning |
循环 @import | 跳过循环文件,触发 OnLoadWarning |
@import深度超过 5 层 | 跳过,触发 OnLoadWarning |
累计大小超过 AllAgentsMDMaxBytes | 跳过后续文件,触发 OnLoadWarning(第一个文件始终完整加载) |
| 权限被拒 / I/O 错误 | 中止加载,返回 error |
| 所有文件内容为空 | 不注入,原样传递输入消息 |
| 工具 | 默认名称 | 描述 | 条件 |
| 列出目录 | 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 |
| 调用方式 | 字段值 |
Agent.Run() | Input字段有值, ResumeInfo为 nil |
Agent.Resume() | ResumeInfo字段有值, Input为 nil |
| Agent 类型 | GetType() 返回值 |
| ChatModelAgent | "ChatModel" |
| workflowAgent (Sequential) | "Sequential" |
| workflowAgent (Parallel) | "Parallel" |
| workflowAgent (Loop) | "Loop" |
| DeterministicTransfer Agent | "DeterministicTransfer" |
+Run 方法1. 初始化回调上下文2. 处理输入3. 调用 OnStart4. 执行 Agent 逻辑5. 注册 OnEnd(在事件流创建时) |
+Resume 方法1. 构建 ResumeInfo2. 初始化回调上下文3. 调用 OnStart4. 恢复 Agent 执行5. 注册 OnEnd(在事件流创建时) |
| 协作方式 | 描述 |
| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
| 上下文策略 | 描述 |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 |
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
+
+在 Eino ADK 中,当为一个 Agent 构建 AgentInput 时,它能看到的 History 是“所有在我之前产生的 AgentEvent”。
+
+值得一提的是 ParallelWorkflowAgent:并行的两个子 Agent(A,B),在并行执行过程中,相互不可见对方产生的 AgentEvent,因为并行的 A、B 没有谁是在另一个之前。
+
+#### RunPath
+
+History 中每个 AgentEvent 都是由“特定 Agent 在特定的执行序列中产生的”,也就是 AgentEvent 有自身的 RunPath。RunPath 的作用是传递出这个信息,在 eino 框架中不乘载其他功能。
+
+下面表格中给出各种编排模式下,Agent 执行时的具体 RunPath:
+
+
+
+三个 Agent 均使用 ChatModelAgent 实现:
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/compose"
+)
+
+func newChatModel() model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return cm
+}
+
+type GetWeatherInput struct {
+ City string `json:"city"`
+}
+
+func NewWeatherAgent() adk.Agent {
+ weatherTool, err := utils.InferTool(
+ "get_weather",
+ "Gets the current weather for a specific city.", // English description
+ func(ctx context.Context, input *GetWeatherInput) (string, error) {
+ return fmt.Sprintf(`the temperature in %s is 25°C`, input.City), nil
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "WeatherAgent",
+ Description: "This agent can get the current weather for a given city.",
+ Instruction: "Your sole purpose is to get the current weather for a given city by using the 'get_weather' tool. After calling the tool, report the result directly to the user.",
+ Model: newChatModel(),
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{weatherTool},
+ },
+ },
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+func NewChatAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ChatAgent",
+ Description: "A general-purpose agent for handling conversational chat.", // English description
+ Instruction: "You are a friendly conversational assistant. Your role is to handle general chit-chat and answer questions that are not related to any specific tool-based tasks.",
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+func NewRouterAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "RouterAgent",
+ Description: "A manual router that transfers tasks to other expert agents.",
+ Instruction: `You are an intelligent task router. Your responsibility is to analyze the user's request and delegate it to the most appropriate expert agent.If no Agent can handle the task, simply inform the user it cannot be processed.`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+```
+
+之后使用 Eino ADK 的 Transfer 能力搭建 Multi-Agent 并运行,ChatModelAgent 实现了 OnSubAgent 接口,在 adk.SetSubAgents 方法中会使用此接口向 ChatModelAgent 注册父/子 Agent,不需要用户处理 TransferAction 生成问题:
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/adk"
+)
+
+func main() {
+ weatherAgent := NewWeatherAgent()
+ chatAgent := NewChatAgent()
+ routerAgent := NewRouterAgent()
+
+ ctx := context.Background()
+ a, err := adk.SetSubAgents(ctx, routerAgent, []adk.Agent{chatAgent, weatherAgent})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: a,
+ })
+
+ // query weather
+ println("\n\n>>>>>>>>>query weather<<<<<<<<<")
+ iter := runner.Query(ctx, "What's the weather in Beijing?")
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+ if event.Action != nil {
+ fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
+ } else {
+ fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
+ }
+ }
+
+ // failed to route
+ println("\n\n>>>>>>>>>failed to route<<<<<<<<<")
+ iter = runner.Query(ctx, "Book me a flight from New York to London tomorrow.")
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+ if event.Action != nil {
+ fmt.Printf("\nAgent[%s]: transfer to %+v\n\n======\n", event.AgentName, event.Action.TransferToAgent.DestAgentName)
+ } else {
+ fmt.Printf("\nAgent[%s]:\n%+v\n\n======\n", event.AgentName, event.Output.MessageOutput.Message)
+ }
+ }
+}
+```
+
+运行结果:
+
+```yaml
+>>>>>>>>>query weather<<<<<<<<<
+Agent[RouterAgent]:
+assistant:
+tool_calls:
+{Index:
+
+```go
+// github.com/cloudwego/eino/adk/prebuilt/supervisor.go
+
+type SupervisorConfig struct {
+ Supervisor adk.Agent
+ SubAgents []adk.Agent
+}
+
+func NewSupervisor(ctx context.Context, conf *SupervisorConfig) (adk.Agent, error) {
+ subAgents := make([]adk.Agent, 0, len(conf.SubAgents))
+ supervisorName := conf.Supervisor.Name(ctx)
+ for _, subAgent := range conf.SubAgents {
+ subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{
+ Agent: subAgent,
+ ToAgentNames: []string{supervisorName},
+ }))
+ }
+
+ return adk.SetSubAgents(ctx, conf.Supervisor, subAgents)
+}
+```
+
+## Workflow Agents
+
+WorkflowAgent 支持以代码中预设好的流程运行 Agents。Eino ADK 提供了三种基础 Workflow Agent:Sequential、Parallel、Loop,它们之间可以互相嵌套以完成更复杂的任务。
+
+默认情况下,Workflow 中每个 Agent 的输入由 History 章节中介绍的方式生成,可以通过 WithHistoryRewriter 自定 AgentInput 生成方式。
+
+当 Agent 产生 ExitAction Event 后,Workflow Agent 会立刻退出,无论之后有没有其他需要运行的 Agent。
+
+详解与用例参考请见:[Eino ADK: Workflow Agents](/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow)
+
+### SequentialAgent
+
+SequentialAgent 会按照你提供的顺序,依次执行一系列 Agent:
+
+
+
+```go
+type SequentialAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+}
+
+func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
+```
+
+### LoopAgent
+
+LoopAgent 基于 SequentialAgent 实现,在 SequentialAgent 运行完成后,再次从头运行:
+
+
+
+```go
+type LoopAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+
+ MaxIterations int // 最大循环次数
+}
+
+func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
+```
+
+### ParallelAgent
+
+ParallelAgent 会并发运行若干 Agent:
+
+
+
+```go
+type ParallelAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+}
+
+func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
+```
+
+## AgentAsTool
+
+当 Agent 运行仅需要明确清晰的指令,而非完整运行上下文(History)时,该 Agent 可以转换为 Tool 进行调用:
+
+```go
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+转换为 Tool 后,Agent 可以被支持 function calling 的 ChatModel 调用,也可以被所有基于 LLM 驱动的 Agent 调用,调用方式取决于 Agent 实现。
+
+消息历史隔离:作为 Tool 的 Agent,不会继承上级 Agent 的消息历史(History)。
+
+SessionValues 共享:但是,会共享上级 Agent 的 SessionValues,即读写同一个 KV map。
+
+内部事件透出:作为 Tool 的 Agent 也是 Agent,会产生 AgentEvent。这些内部的 AgentEvent,默认情况下,不会通过 `Runner` 返回的 `AsyncIterator` 透出。在部分业务场景中,如果需要像用户透出内部 AgentTool 的 AgentEvent,需要在 AgentTool 的上级 `ChatModelAgent` 的 `ToolsConfig` 中增加配置,开启内部事件透出:
+
+```go
+// from adk/chatmodel.go
+
+**type **ToolsConfig **struct **{
+ // other configurations...
+
+ _// EmitInternalEvents indicates whether internal events from agentTool should be emitted_
+_ // to the parent generator via a tool option injection at run-time._
+_ _EmitInternalEvents bool
+}
+```
+
+这些内部事件,不会进入上级 agent 的上下文(除了本来就会进入的最后一条 message),各种 AgentAction 也不会生效(InterruptAction 除外)。
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_extension.md b/docs/Eino/docs/core_modules/eino_adk/agent_extension.md
new file mode 100644
index 0000000..1a87c5d
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_extension.md
@@ -0,0 +1,118 @@
+---
+Description: ""
+date: "2025-11-20"
+lastmod: ""
+tags: []
+title: Agent Runner 与扩展
+weight: 6
+---
+
+# Agent Runner
+
+## 定义
+
+Runner 是 Eino ADK 中负责执行 Agent 的核心引擎。它的主要作用是管理和控制 Agent 的整个生命周期,如处理多 Agent 协作,保存传递上下文等,interrupt、callback 等切面能力也均依赖 Runner 实现。任何 Agent 都应通过 Runner 来运行。
+
+## Interrupt & Resume
+
+Agent Runner 提供运行时中断与恢复的功能,该功能允许一个正在运行的 Agent 主动中断其执行并保存当前状态,支持从中断点恢复执行。该功能常用于 Agent 处理流程中需要外部输入、长时间等待或可暂停等场景。
+
+下面将对一次中断到恢复过程中的三个关键点进行介绍:
+
+1. Interrupted Action:由 Agent 抛出中断事件,Agent Runner 拦截
+2. Checkpoint:Agent Runner 拦截事件后保存当前运行状态
+3. Resume:运行条件重新 ready 后,由 Agent Runner 从断点恢复运行
+
+### Interrupted Action
+
+在 Agent 的执行过程中,可以通过产生包含 Interrupted Action 的 AgentEvent 来主动中断 Runner 的运行。
+
+当 Event 中的 Interrupted 不为空时,Agent Runner 便会认为发生中断:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+type AgentAction struct {
+ // other actions
+ Interrupted *InterruptInfo
+ // other actions
+}
+
+// github.com/cloudwego/eino/adk/interrupt.go
+type InterruptInfo struct {
+ Data any
+}
+```
+
+当中断发生时,可以通过 InterruptInfo 结构体附带自定义的中断信息。此信息:
+
+1. 会被传递给调用者,可以通过该信息向调用者说明中断原因等
+2. 如果后续需要恢复 Agent 运行,InterruptInfo 会在恢复时重新传递给中断的 Agent,Agent 可以依据该信息恢复运行
+
+```go
+// 例如 ChatModelAgent 中断时,会发送如下的 AgentEvent:
+h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{
+ Interrupted: &InterruptInfo{
+ Data: &ChatModelAgentInterruptInfo{Data: data, Info: info},
+ },
+}})
+```
+
+### 状态持久化 (Checkpoint)
+
+当 Runner 捕获到这个带有 Interrupted Action 的 Event 时,会立即终止当前的执行流程。 如果:
+
+1. Runner 中设置了 CheckPointStore
+
+```go
+// github.com/cloudwego/eino/adk/runner.go
+type RunnerConfig struct {
+ // other fields
+ CheckPointStore CheckPointStore
+}
+
+// github.com/cloudwego/eino/adk/interrupt.go
+type CheckPointStore interface {
+ Set(ctx context.Context, key string, value []byte) error
+ Get(ctx context.Context, key string) ([]byte, bool, error)
+}
+```
+
+1. 调用 Runner 时通过 AgentRunOption WithCheckPointID 传入 CheckPointID
+
+```go
+// github.com/cloudwego/eino/adk/interrupt.go
+func WithCheckPointID(id string) _AgentRunOption_
+```
+
+Runner 在终止运行后会将当前运行状态(原始输入、对话历史等)以及 Agent 抛出的 InterruptInfo 以 CheckPointID 为 key 持久化到 CheckPointStore 中。
+
+> 💡
+> 为了保存 interface 中数据的原本类型,Eino ADK 使用 gob([https://pkg.go.dev/encoding/gob](https://pkg.go.dev/encoding/gob))序列化运行状态。因此在使用自定义类型时需要提前使用 gob.Register 或 gob.RegisterName 注册类型(更推荐后者,前者使用路径加类型名作为默认名字,因此类型的位置和名字均不能发生变更)。Eino 会自动注册框架内置的类型。
+
+### Resume
+
+运行中断,调用 Runner 的 Resume 接口传入中断时的 CheckPointID 可以恢复运行:
+
+```go
+// github.com/cloudwego/eino/adk/runner.go
+func (r *Runner) Resume(ctx context.Context, checkPointID string, opts ...AgentRunOption) (*AsyncIterator[*AgentEvent], error)
+```
+
+恢复 Agent 运行需要发生中断的 Agent 实现了 ResumableAgent 接口, Runner 从 CheckPointerStore 读取运行状态并恢复运行,其中 InterruptInfo 和上次运行配置的 EnableStreaming 会作为输入提供给 Agent:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+type ResumableAgent interface {
+ Agent
+
+ Resume(ctx context.Context, info *ResumeInfo, opts ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+
+// github.com/cloudwego/eino/adk/interrupt.go
+type ResumeInfo struct {
+ EnableStreaming bool
+ *_InterruptInfo_
+}
+```
+
+Resume 如果向 Agent 传入新信息,可以定义 AgentRunOption,在调用 Runner.Resume 时传入。
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_hitl.md b/docs/Eino/docs/core_modules/eino_adk/agent_hitl.md
new file mode 100644
index 0000000..d168a04
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_hitl.md
@@ -0,0 +1,1189 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Eino human-in-the-loop框架:技术架构指南
+weight: 7
+---
+
+## 概述
+
+本文档提供 Eino 的 human-in-the-loop (Human-in-the-Loop, HITL) 框架架构的技术细节,重点介绍中断/恢复机制和底层的寻址系统。
+
+## human-in-the-loop 的需求
+
+下图说明了在中断/恢复过程中,每个组件必须回答的关键问题。理解这些需求是掌握架构设计背后原因的关键。
+
+```mermaid
+graph TD
+ subgraph P1 [中断阶段]
+ direction LR
+ subgraph Dev1 [开发者]
+ direction TB
+ D1[我现在应该中断吗?
+
+可以通过 ToolsConfig 为 ChatModelAgent 配置 Tool:
+
+```go
+// github.com/cloudwego/eino/adk/chatmodel.go
+
+type ToolsConfig struct {
+ compose.ToolsNodeConfig
+
+ // Names of the tools that will make agent return directly when the tool is called.
+ // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
+ ReturnDirectly map[string]bool
+
+ // EmitInternalEvents indicates whether internal events from agentTool should be emitted
+ // to the parent generator via a tool option injection at run-time.
+ EmitInternalEvents bool
+}
+```
+
+ToolsConfig 复用了 Eino Graph ToolsNodeConfig,详细参考:[Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。额外提供了 ReturnDirectly 配置,ChatModelAgent 调用配置在 ReturnDirectly 中的 Tool 后会直接退出。
+
+## ChatModelAgent 配置字段
+
+> 💡
+> 注意:GenModelInput 默认情况下,会通过 adk.GetSessionValues() 并以 F-String 的格式渲染 Instruction,如需关闭此行为,可定制 GenModelInput 方法。
+
+```go
+type ChatModelAgentConfig struct {
+ // Name of the agent. Better be unique across all agents.
+ Name string
+ // Description of the agent's capabilities.
+ // Helps other agents determine whether to transfer tasks to this agent.
+ Description string
+ // Instruction used as the system prompt for this agent.
+ // Optional. If empty, no system prompt will be used.
+ // Supports f-string placeholders for session values in default GenModelInput, for example:
+ // "You are a helpful assistant. The current time is {Time}. The current user is {User}."
+ // These placeholders will be replaced with session values for "Time" and "User".
+ Instruction string
+
+ Model model.ToolCallingChatModel
+
+ ToolsConfig ToolsConfig
+
+ // GenModelInput transforms instructions and input messages into the model's input format.
+ // Optional. Defaults to defaultGenModelInput which combines instruction and messages.
+ GenModelInput GenModelInput
+
+ // Exit defines the tool used to terminate the agent process.
+ // Optional. If nil, no Exit Action will be generated.
+ // You can use the provided 'ExitTool' implementation directly.
+ Exit tool.BaseTool
+
+ // OutputKey stores the agent's response in the session.
+ // Optional. When set, stores output via AddSessionValue(ctx, outputKey, msg.Content).
+ OutputKey string
+
+ // MaxIterations defines the upper limit of ChatModel generation cycles.
+ // The agent will terminate with an error if this limit is exceeded.
+ // Optional. Defaults to 20.
+ MaxIterations int
+
+ // ModelRetryConfig configures retry behavior for the ChatModel.
+ // When set, the agent will automatically retry failed ChatModel calls
+ // based on the configured policy.
+ // Optional. If nil, no retry will be performed.
+ ModelRetryConfig *ModelRetryConfig
+}
+
+type ToolsConfig struct {
+ compose.ToolsNodeConfig
+
+ // Names of the tools that will make agent return directly when the tool is called.
+ // When multiple tools are called and more than one tool is in the return directly list, only the first one will be returned.
+ ReturnDirectly map[string]bool
+
+ // EmitInternalEvents indicates whether internal events from agentTool should be emitted
+ // to the parent generator via a tool option injection at run-time.
+ EmitInternalEvents bool
+}
+
+type GenModelInput func(ctx context.Context, instruction string, input *AgentInput) ([]Message, error)
+```
+
+- `Name`:Agent 名称
+- `Description`:Agent 描述
+- `Instruction`:调用 ChatModel 时的 System Prompt,支持 f-string 渲染
+- `Model`:运行所使用的 ChatModel,要求支持工具调用
+- `ToolsConfig`:工具配置
+ - ToolsConfig 复用了 Eino Graph ToolsNodeConfig,详细参考:[Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。
+ - ReturnDirectly:当 ChatModelAgent 调用配置在 ReturnDirectly 中的 Tool 后,将携带结果立刻退出,不会按照 react 模式返回 ChatModel。如果命中了多个 Tool,只有首个 Tool 会返回。Map key 为 Tool 名称。
+ - EmitInternalEvents:当通过 adk.AgentTool() 将一个 Agent 通过 ToolCall 的形式当成 SubAgent 时,默认情况下,这个 SubAgent 不会发送 AgentEvent,只将最终结果作为 ToolResult 返回。
+- `GenModelInput`:Agent 被调用时会使用该方法将 `Instruction` 和 `AgentInput` 转换为调用 ChatModel 的 Messages。Agent 提供了默认的 GenModelInput 方法:
+ 1. 将 `Instruction` 作为 `System Message` 加到 `AgentInput.Messages` 前
+ 2. 将 `SessionValues` 为 variables 渲染到步骤 1 的 message list 中
+
+> 💡
+> 默认的 `GenModelInput` 使用 pyfmt 渲染,message list 中的文本会被作为 pyfmt 模板,这意味着文本中的 '{' 与 '}' 都会被视为关键字,如果希望直接输入这两个字符,需要进行转义 '{{'、'}}'
+
+- `OutputKey`:配置后,ChatModelAgent 运行产生的最后一条 Message 将会以 `OutputKey` 为 key 设置到 `SessionValues` 中
+- `MaxIterations`:react 模式下 ChatModel 最大生成次数,超过时 Agent 会报错退出,默认值为 20
+- `Exit`:Exit 是一个特殊的 Tool,当模型调用这个工具并执行后,ChatModelAgent 将直接退出,效果与 `ToolsConfig.ReturnDirectly` 类似。ADK 提供了一个默认 ExitTool 实现供用户使用:
+
+```go
+type ExitTool struct{}
+
+func (et ExitTool) Info(_ context.Context) (*schema.ToolInfo, error) {
+ return ToolInfoExit, nil
+}
+
+func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ ...tool.Option) (string, error) {
+ type exitParams struct {
+ FinalResult string `json:"final_result"`
+ }
+
+ params := &exitParams{}
+ err := sonic.UnmarshalString(argumentsInJSON, params)
+ if err != nil {
+ return "", err
+ }
+
+ err = SendToolGenAction(ctx, "exit", NewExitAction())
+ if err != nil {
+ return "", err
+ }
+
+ return params.FinalResult, nil
+}
+```
+
+- `ModelRetryConfig`: 配置后,ChatModel 请求过程中发生的各种错误(包括直接返回错误、流式响应过程中发生错误等),都会按照配置的策略选择是否以及何时进行重试。如果是流式响应过程中发生错误,则这一次流式响应依然会第一时间通过 AgentEvent 的形式返回出去。如果这次流式响应过程中的错误,按照配置的策略,会进行重试,则消费 AgentEvent 中的 message stream,会得到 `WillRetryError`。用户可以处理这个 error,做对应的上屏展示等处理,示例如下:
+
+```go
+iterator := agent.Run(ctx, input)
+for {
+ event, ok := iterator.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ handleFinalError(event.Err)
+ break
+ }
+
+ // Process streaming output
+ if event.Output != nil && event.Output.MessageOutput.IsStreaming {
+ stream := event.Output.MessageOutput.MessageStream
+ for {
+ msg, err := stream.Recv()
+ if err == io.EOF {
+ break // Stream completed successfully
+ }
+ if err != nil {
+ // Check if this error will be retried (more streams coming)
+ var willRetry *adk.WillRetryError
+ if errors.As(err, &willRetry) {
+ log.Printf("Attempt %d failed, retrying...", willRetry.RetryAttempt)
+ break // Wait for next event with new stream
+ }
+ // Original error - won't retry, agent will stop and the next AgentEvent probably will be an error
+ log.Printf("Final error (no retry): %v", err)
+ break
+ }
+ // Display chunk to user
+ displayChunk(msg)
+ }
+ }
+}
+```
+
+## ChatModelAgent Transfer
+
+`ChatModelAgent` 支持将其他 Agent 的元信息转为自身的 Tool ,经由 ChatModel 判断实现动态 Transfer:
+
+- `ChatModelAgent` 实现了 `OnSubAgents` 接口,使用 `SetSubAgents` 为 `ChatModelAgent` 设置子 Agents 后,`ChatModelAgent` 会增加一个 `Transfer Tool`,并且在 prompt 中指示 ChatModel 在需要 transfer 时调用这个 Tool 并以 transfer 目标 AgentName 作为 Tool 输入。
+
+```go
+const (
+ TransferToAgentInstruction = `Available other agents: %s
+
+Decision rule:
+- If you're best suited for the question according to your description: ANSWER
+- If another agent is better according its description: CALL '%s' function with their agent name
+
+When transferring: OUTPUT ONLY THE FUNCTION CALL`
+)
+
+func genTransferToAgentInstruction(ctx context.Context, agents []Agent) string {
+ var sb strings.Builder
+ for _, agent := range agents {
+ sb.WriteString(fmt.Sprintf("\n- Agent name: %s\n Agent description: %s",
+ agent.Name(ctx), agent.Description(ctx)))
+ }
+
+ return fmt.Sprintf(TransferToAgentInstruction, sb.String(), TransferToAgentToolName)
+}
+```
+
+- `Transfer Tool` 运行会设置 Transfer Event,指定跳转到目标 Agent 上,完成后 ChatModelAgent 退出。
+- Agent Runner 接收到 Transfer Event 后,跳转到目标 Agent 上执行,完成 Transfer 操作
+
+## ChatModelAgent AgentAsTool
+
+当需要被调用的 Agent 不需要完整的运行上下文,仅需要明确清晰的入参即可正确运行时,该 Agent 可以转换为 Tool 交由 `ChatModelAgent` 判断调用:
+
+- ADK 中提供了工具方法,可以方便地将 Eino ADK Agent 转化为 Tool 供 ChatModelAgent 调用:
+
+```go
+// github.com/cloudwego/eino/adk/agent_tool.go
+
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+- 被转换为 Tool 后的 Agent 可以通过 `ToolsConfig` 直接注册在 ChatModelAgent 中
+
+```go
+bookRecommender := NewBookRecommendAgent()
+bookRecommendeTool := NewAgentTool(ctx, bookRecommender)
+
+a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ // ...
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{bookRecommendeTool},
+ },
+ },
+})
+```
+
+## ChatModelAgent Middleware
+
+`ChatModelAgentMiddleware` 是 `ChatModelAgent` 的扩展机制,允许开发者在 Agent 执行的各个阶段注入自定义逻辑:
+
+
+
+`ChatModelAgentMiddleware` 定义为 interface,开发者可以实现此 interface 并通过配置到 `ChatModelAgentConfig` 使其在 `ChatModelAgent` 中生效:
+
+```go
+type ChatModelAgentMiddleware interface {
+ // ...
+}
+
+a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ // ...
+ Handlers: []adk.ChatModelAgentMiddleware{
+ &MyMiddleware{},
+ },
+})
+```
+
+**使用 BaseChatModelAgentMiddleware**
+
+`BaseChatModelAgentMiddleware` 提供所有方法的默认空实现。通过嵌入它,可以只覆盖需要的方法:
+
+```go
+type MyMiddleware struct {
+ *adk.BaseChatModelAgentMiddleware
+ // 自定义字段
+ logger *log.Logger
+}
+
+// 只需覆盖需要的方法
+func (m *MyMiddleware) BeforeModelRewriteState(
+ ctx context.Context,
+ state *adk.ChatModelAgentState,
+ mc *adk.ModelContext,
+) (context.Context, *adk.ChatModelAgentState, error) {
+ m.logger.Printf("Messages count: %d", len(state.Messages))
+ return ctx, state, nil
+}
+```
+
+### BeforeAgent
+
+在每次 Agent 运行前调用,可用于修改指令和工具配置。ChatModelAgentContext 定义了 BeforeAgent 中可读写的内容:
+
+```go
+type ChatModelAgentContext struct {
+ // InstructionAgent 是当前 Agent 的指令
+ Instruction string
+ // Tools 是当前配置的原始工具列表
+ Tools []tool.BaseTool
+ // ReturnDirectly 配置调用后直接返回的工具名称集合
+ ReturnDirectly map[string]bool
+}
+
+type ChatModelAgentMiddleware interface {
+ // ...
+ BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error)
+ // ...
+}
+```
+
+例子:
+
+```go
+func (m *MyMiddleware) BeforeAgent(
+ ctx context.Context,
+ runCtx *adk.ChatModelAgentContext,
+) (context.Context, *adk.ChatModelAgentContext, error) {
+ // 拷贝 runCtx,避免修改输入
+ nRunCtx := *runCtx
+
+ // 修改指令
+ nRunCtx.Instruction += "\n\n请始终使用中文回复。"
+
+ // 添加工具
+ nRunCtx.Tools = append(runCtx.Tools, myCustomTool)
+
+ // 设置工具直接返回
+ nRunCtx.ReturnDirectly["my_tool"] = true
+
+ return ctx, &nRunCtx, nil
+}
+```
+
+### BeforeModelRewriteState / AfterModelRewriteState
+
+在每次模型调用前/后调用,可用于检查和修改消息历史。ModelContext 定义了只读内容,ChatModelAgentState 定义了可读写内容:
+
+```go
+type ModelContext struct {
+ // Tools 包含当前配置给 Agent 的工具列表
+ // 在请求时填充,包含将要发送给模型的工具信息
+ Tools []*schema.ToolInfo
+
+ // ModelRetryConfig 包含模型的重试配置
+ // 从 Agent 的 ModelRetryConfig 填充
+ ModelRetryConfig *ModelRetryConfig
+}
+
+type ChatModelAgentState struct {
+ // Messages 包含当前会话中的所有消息
+ Messages []Message
+}
+
+type ChatModelAgentMiddleware interface {
+ BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
+ AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error)
+}
+```
+
+例子:
+
+```go
+func (m *MyMiddleware) BeforeModelRewriteState(
+ ctx context.Context,
+ state *adk.ChatModelAgentState,
+ mc *adk.ModelContext,
+) (context.Context, *adk.ChatModelAgentState, error) {
+ // 拷贝 state,避免修改入参
+ nState := *state
+
+ // 检查消息历史
+ if len(state.Messages) > 50 {
+ // 截断过旧的消息
+ nState.Messages = state.Messages[len(state.Messages)-50:]
+ }
+ return ctx, &nState, nil
+}
+
+func (m *MyMiddleware) AfterModelRewriteState(
+ ctx context.Context,
+ state *adk.ChatModelAgentState,
+ mc *adk.ModelContext,
+) (context.Context, *adk.ChatModelAgentState, error) {
+ // 模型响应是最后一条消息
+ lastMsg := state.Messages[len(state.Messages)-1]
+ m.logger.Printf("Model response: %s", lastMsg.Content)
+ return ctx, state, nil
+}
+```
+
+### WrapModel
+
+包装模型调用,可用于拦截和修改模型的输入输出:
+
+```go
+type ChatModelAgentMiddleware interface {
+ WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error)
+}
+```
+
+例子:
+
+```go
+func (m *MyMiddleware) WrapModel(
+ ctx context.Context,
+ chatModel model.BaseChatModel,
+ mc *adk.ModelContext,
+) (model.BaseChatModel, error) {
+ return &loggingModel{
+ inner: chatModel,
+ logger: m.logger,
+ }, nil
+}
+
+type loggingModel struct {
+ inner model.BaseChatModel
+ logger *log.Logger
+}
+
+func (m *loggingModel) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) {
+ m.logger.Printf("Input messages: %d", len(msgs))
+ resp, err := m.inner.Generate(ctx, msgs, opts...)
+ m.logger.Printf("Output: %v, error: %v", resp != nil, err)
+ return resp, err
+}
+
+func (m *loggingModel) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) {
+ return m.inner.Stream(ctx, msgs, opts...)
+}
+```
+
+### WrapInvokableToolCall / WrapStreamableToolCall
+
+包装工具调用,可用于拦截和修改工具的输入输出:
+
+```go
+// InvokableToolCallEndpoint 是工具调用的函数签名。
+// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。
+type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error)
+
+// StreamableToolCallEndpoint 是流式工具调用的函数签名。
+// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。
+type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error)
+
+type ToolContext struct {
+ // Name 说明了本次调用工具的名称
+ Name string
+ // CallID 说明了本次调用工具的 ToolCallID
+ CallID string
+}
+
+type ChatModelAgentMiddleware interface {
+ WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error)
+ WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error)
+}
+```
+
+例子:
+
+```go
+func (m *MyMiddleware) WrapInvokableToolCall(
+ ctx context.Context,
+ endpoint adk.InvokableToolCallEndpoint,
+ tCtx *adk.ToolContext,
+) (adk.InvokableToolCallEndpoint, error) {
+ return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
+ m.logger.Printf("Calling tool: %s (ID: %s)", tCtx.Name, tCtx.CallID)
+ start := time.Now()
+
+ result, err := endpoint(ctx, argumentsInJSON, opts...)
+
+ m.logger.Printf("Tool %s completed in %v", tCtx.Name, time.Since(start))
+ return result, err
+ }, nil
+}
+```
+
+# ChatModelAgent 使用示例
+
+## 场景说明
+
+创建一个图书推荐 Agent,Agent 将能够根据用户的输入推荐相关图书。
+
+## 代码实现
+
+### 步骤 1: 定义工具
+
+图书推荐 Agent 需要一个根据能够根据用户要求(题材、评分等)检索图书的工具 `book_search` 。
+
+利用 Eino 提供的工具方法可以方便地创建(可参考[如何创建一个 tool ?](/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool)):
+
+```go
+import (
+ "context"
+ "log"
+
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+)
+
+type BookSearchInput struct {
+ Genre string `json:"genre" jsonschema:"description=Preferred book genre,enum=fiction,enum=sci-fi,enum=mystery,enum=biography,enum=business"`
+ MaxPages int `json:"max_pages" jsonschema:"description=Maximum page length (0 for no limit)"`
+ MinRating int `json:"min_rating" jsonschema:"description=Minimum user rating (0-5 scale)"`
+}
+
+type BookSearchOutput struct {
+ Books []string
+}
+
+func NewBookRecommender() tool.InvokableTool {
+ bookSearchTool, err := utils.InferTool("search_book", "Search books based on user preferences", func(ctx context.Context, input *BookSearchInput) (output *BookSearchOutput, err error) {
+ // search code
+ // ...
+ return &BookSearchOutput{Books: []string{"God's blessing on this wonderful world!"}}, nil
+ })
+ if err != nil {
+ log.Fatalf("failed to create search book tool: %v", err)
+ }
+ return bookSearchTool
+}
+```
+
+### 步骤 2: 创建 ChatModel
+
+Eino 提供了多种 ChatModel 封装(如 openai、gemini、doubao 等,详见 [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)),这里以 openai ChatModel 为例:
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/components/model"
+)
+
+func NewChatModel() model.ToolCallingChatModel {
+ ctx := context.Background()
+ apiKey := os.Getenv("OPENAI_API_KEY")
+ openaiModel := os.Getenv("OPENAI_MODEL")
+
+ cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: apiKey,
+ Model: openaiModel,
+ })
+ if err != nil {
+ log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
+ }
+ return cm
+}
+```
+
+### 步骤 3: 创建 ChatModelAgent
+
+除了配置 ChatModel 和工具外,还需要配置描述 Agent 功能用途的 Name 和 Description,以及指示 ChatModel 的 Instruction,Instruction 最终会作为 system message 被传递给 ChatModel。
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/compose"
+)
+
+func NewBookRecommendAgent() adk.Agent {
+ ctx := context.Background()
+
+ a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "BookRecommender",
+ Description: "An agent that can recommend books",
+ Instruction: `You are an expert book recommender. Based on the user's request, use the "search_book" tool to find relevant books. Finally, present the results to the user.`,
+ Model: NewChatModel(),
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{NewBookRecommender()},
+ },
+ },
+ })
+ if err != nil {
+ log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
+ }
+
+ return a
+}
+```
+
+###
+
+### 步骤 4: 通过 Runner 运行
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/adk"
+
+ "github.com/cloudwego/eino-examples/adk/intro/chatmodel/subagents"
+)
+
+func main() {
+ ctx := context.Background()
+ a := subagents.NewBookRecommendAgent()
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: a,
+ })
+ iter := runner.Query(ctx, "recommend a fiction book to me")
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+ msg, err := event.Output.MessageOutput.GetMessage()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("\nmessage:\n%v\n======", msg)
+ }
+}
+```
+
+## 运行结果
+
+```yaml
+message:
+assistant:
+tool_calls:
+{Index:
+
+上图展示了 DeepAgents 的核心组件与它们之间的调用关系:
+
+- 主 Agent: 系统的入口和总指挥,接收初始任务,以 ReAct 方式调用工具完成任务并负责最终结果的呈现。
+- ChatModel (ToolCallingChatModel): 通常是一个具备工具调用能力的大语言模型,负责理解任务、推理、选择并调用工具。
+- Tools: MainAgent 可用的一系列能力的集合,包括:
+ - WriteTodos: 内置的规划工具,用于将复杂任务拆解为结构化的待办事项列表。
+ - TaskTool: 一个特殊的工具,作为调用子 Agent 的统一入口。
+ - BuiltinTools、CustomTools: DeepAgents 内置的通用工具以及用户根据业务需求自定义的各类工具。
+- SubAgents: 负责执行具体、独立的子任务,与 MainAgent 上下文独立。
+ - GeneralPurpose: 通用子 Agent,具有与 MainAgent 相同的 Tools(除了 TaskTool),用于在“干净”的上下文中执行子任务。
+ - CustomSubAgents: 用户根据业务需求自定义的各种子 Agent。
+
+### 内置能力
+
+#### Filesystem
+
+> 💡
+> 目前处于 alpha 状态
+
+创建 DeepAgents 时配置相关 Backend,DeepAgents 会自动加载相应工具:
+
+```
+type Config struct {
+ // ...
+ Backend filesystem.Backend
+ Shell filesystem.Shell
+ StreamingShell filesystem.StreamingShell
+ // ...
+}
+```
+
+| 配置 | 功能 | 添加工具 |
| Backend | 提供文件系统访问能力,可选 | read_file, write_file, edit_file, glob, grep |
| Shell | 提供 Shell 能力,可选,与 StreamShell 互斥 | execute |
| StreamingShell | 提供可以流式返回结果的 Shell 能力,可选,与 Shell 互斥 | execute(streaming) |
+
+1. 模型接收用户输入。
+2. 模型调用 WriteTodos 工具,参数为依照 WriteTodos Description 产生的任务列表。这次工具调用被添加到上下文中,供后续参考。
+3. 模型依照上下文中的 todos,调用 TaskTool 完成第一个 todo。
+4. 再次调用 WriteTodos ,更新 Todos 执行进度。
+
+> 💡
+> 对简单任务来说,每次都调用 WriteTodos 可能会起到反效果。WriteTodos Description 中添加了一些比较通用的正反例子来避免不调用或过度调用 WriteTodos。使用 DeepAgents 时,可以根据实际业务场景添加更多 prompt 来让 WriteTodos 在合适的时候被调用。
+
+> 💡
+> WriteTodos 会被默认添加到 Agent 中,配置 `WithoutWriteTodos=true` 可以关闭 WriteTodos。
+
+### 任务委派与 SubAgents 调用
+
+**TaskTool**
+
+所有子 Agent 会被绑定到 TaskTool 上,当主 Agent 分配子任务给子 Agent 处理时,它会调用 TaskTool,并指明需要哪个子代理及执行的任务。TaskTool 随后将任务路由到指定的子代理,并在其执行完毕后,将结果返回给主 Agent。TaskTool 的默认 Description 会说明调用子 Agent 的通用规则并拼接每个子 Agent 的 Description,开发者可以通过配置 `TaskToolDescriptionGenerator` 来自定义 TaskTool 的 Description。
+
+> 当用户配置了 Config.SubAgents 时,这些 Agent 会基于 ChatModelAgent AgentAsTool 的能力绑定到 TaskTool 上
+
+**上下文隔离**
+
+Agent 之间的上下文隔离:
+
+- 信息传递: 主 Agent 与子 Agent 之间不共享上下文。子 Agent 仅接收主 Agent 分配的子任务目标,不会接收整个任务的处理过程;主 Agent 仅接收子 Agent 的处理结果,不会接受子 Agent 的处理过程。
+- 避免污染: 这种隔离确保了子 Agent 的执行过程(如大量的工具调用和中间步骤)不会“污染”主代理的上下文,主代理只接收简洁、明确的最终答案。
+
+**general-purpose**
+
+DeepAgents 会默认增加一个子 Agent:general-purpose。general-purpose 具有和主 Agent 相同的 system prompt 和工具(除了 TaskTool),当任务没有专门的子 Agent 来解决时,主 Agent 可以调用 general-purpose 来隔离上下文。开发者可以通过配置 `WithoutGeneralSubAgent=true` 去掉此 Agent。
+
+### 与其他 Agent 对比
+
+- 对比 ReAct Agent
+
+ - 优势:DeepAgents 通过内置 WriteTodos 强化任务拆解与规划;同时隔离多 Agents 上下文,在大规模、多步骤任务中通常效果更优。
+ - 劣势:制定计划与调用子 Agent 会带来额外的模型请求,增加耗时与 token 成本;若任务拆分不合理,可能对效果产生反作用。
+- 对比 Plan-and-Execute
+
+ - 优势:DeepAgents 将 Plan/RePlan 作为工具供主 Agent 自由调用,可以在任务中跳过不必要的规划,整体上减少模型调用次数、降低耗时与成本。
+ - 劣势:任务规划与委派由一次模型调用完成,对模型能力要求更高,提示词调优也相对更困难。
+
+## DeepAgents 使用示例
+
+### 场景说明
+
+Excel Agent 是一个“看得懂 Excel 的智能助手”,它先把问题拆解成步骤,再一步步执行并校验结果。它能理解用户问题与上传的文件内容,提出可行的解决方案,并选择合适的工具(系统命令、生成并运行 Python 代码、网络查询等等)完成任务。
+
+在真实业务里,你可以把 Excel Agent 当成一位“Excel 专家 + 自动化工程师”。当你交付一个原始表格和目标描述,它会给出方案并完成执行:
+
+- **数据清理与格式化**:从一个包含大量数据的 Excel 文件中完成去重、空值处理、日期格式标准化操作。
+- **数据分析与报告生成**:从销售数据中提取每月的销售总额,聚合统计、透视,最终生成并导出图表报告。
+- **自动化预算计算**:根据不同部门的预算申请,自动计算总预算并生成部门预算分配表。
+- **数据匹配与合并**:将多个不同来源的客户信息表进行匹配合并,生成完整的客户信息数据库。
+
+用 DeepAgents 搭建的 Excel Agent 结构如下:
+
+
+
+1. 在主 Agent 添加 ReadFile 工具,允许主 Agent 查看文件内容,辅助子任务制定
+2. 添加 Code 和 WebSearch 两个子 Agent:Code 可以编写 python 代码来操作 excel 表格;WebSearch 可以搜索信息并总结。
+
+### 代码实现
+
+[https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
+
+### 运行结果
+
+```yaml
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool name: task
+arguments: {"subagent_type":"CodeAgent","description":"请帮我处理一个CSV文件操作任务。具体要求如下:1. 读取当前目录下的questions.csv文件;2. 提取该CSV文件中的第一列数据;3. 将提取出的第一列数据保存到一个新的CSV文件中,新文件命名为first_column.csv。请使用pandas库来完成这些操作,确保代码能够正确执行并处理可能出现的文件路径问题。"}
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool name: python_runner
+arguments: {"code":"```python\nimport pandas as pd\nimport os\n\n# 获取当前工作目录并构建文件路径\nbase_dir = os.getcwd()\ninput_path = os.path.join(base_dir, 'questions.csv')\noutput_path = os.path.join(base_dir, 'first_column.csv')\n\ntry:\n # 读取CSV文件\n df = pd.read_csv(input_path)\n # 提取第一列数据\n first_column = df.iloc[:, 0]\n # 保存为新CSV文件(不包含索引)\n first_column.to_csv(output_path, index=False)\n print(f\"成功将第一列数据保存至: {output_path}\")\nexcept FileNotFoundError:\n print(f\"错误: 未找到文件 {input_path}\")\nexcept Exception as e:\n print(f\"处理过程中发生错误: {str(e)}\")\n```"}
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool response: 成功将第一列数据保存至: /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv
+
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+answer: 任务已完成。已成功读取当前目录下的 `questions.csv` 文件,提取第一列数据,并将结果保存至 `first_column.csv`。具体输出路径如下:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+
+代码已处理路径拼接和异常捕获(如文件不存在或格式错误),确保执行稳定性。
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool response: 任务已完成。已成功读取当前目录下的 `questions.csv` 文件,提取第一列数据,并将结果保存至 `first_column.csv`。具体输出路径如下:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+
+代码已处理路径拼接和异常捕获(如文件不存在或格式错误),确保执行稳定性。
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+answer: 已成功将 `questions.csv` 表格中的第一列数据提取至新文件 `first_column.csv`,文件保存路径为
+:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c4
+4b131973/first_column.csv`
+
+操作过程中已处理路径拼接和异常捕获(如文件不存在、格式错误等问题),确保数据
+提取完整性和文件生成稳定性。若需要调整文件路径或对数据格式有进一步要求,请随时告知
+。
+```
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_implementation/plan_execute.md b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/plan_execute.md
new file mode 100644
index 0000000..1f3ba69
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/plan_execute.md
@@ -0,0 +1,510 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Plan-Execute Agent
+weight: 4
+---
+
+## Plan-Execute Agent 概述
+
+### Import Path
+
+`import ``github.com/cloudwego/eino/adk/prebuilt/planexecute`
+
+### 什么是 Plan-Execute Agent?
+
+Plan-Execute Agent 是 Eino ADK 中一种基于「规划-执行-反思」范式的多智能体协作框架,旨在解决复杂任务的分步拆解、执行与动态调整问题。它通过 **Planner(规划器)**、**Executor(执行器)**和 **Replanner(重规划器)** 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态 replanning,最终达成用户目标。
+
+
+
+Plan-Execute Agent 适用于需要多步骤推理、工具集成或动态调整策略的场景(如研究分析、复杂问题解决、自动化工作流等),其核心优势在于:
+
+- **结构化规划**:将复杂任务拆解为清晰、可执行的步骤序列
+- **迭代执行**:基于工具调用完成单步任务,积累执行结果
+- **动态调整**:根据执行进度实时评估是否需要调整计划或终止任务
+- **模型与工具无关**:兼容任意支持工具调用的模型,可灵活集成外部工具
+
+### Plan-Execute Agent 结构
+
+Plan-Execute Agent 由三个核心智能体与一个协调器构成,基于 ADK 中提供的 ChatModelAgent 和 WorkflowAgents 能力共同完成构建:
+
+
+
+#### 1. Planner
+
+- **核心功能**:根据用户目标生成初始任务计划(结构化步骤序列)
+- **实现方式**:
+ - 使用支持工具调用的模型(如 GPT-4),通过 `PlanTool` 生成符合 JSON Schema 的步骤列表
+ - 或直接使用支持结构化输出的模型,直接生成 `Plan` 格式结果
+- **输出**:`Plan` 对象(包含有序步骤列表),存储于 Session 中供后续流程使用
+
+```go
+// PlannerConfig provides configuration options for creating a planner agent.
+// There are two ways to configure the planner to generate structured Plan output:
+// 1. Use ChatModelWithFormattedOutput: A model already configured to output in the Plan format
+// 2. Use ToolCallingChatModel + ToolInfo: A model that will be configured to use tool calling
+// to generate the Plan structure
+type PlannerConfig struct {
+ // ChatModelWithFormattedOutput is a model pre-configured to output in the Plan format.
+ // This can be created by configuring a model to output structured data directly.
+ // Can refer to https://github.com/cloudwego/eino-ext/blob/main/components/model/openai/examples/structured/structured.go.
+ ChatModelWithFormattedOutput model.BaseChatModel
+
+ // ToolCallingChatModel is a model that supports tool calling capabilities.
+ // When provided along with ToolInfo, the model will be configured to use tool calling
+ // to generate the Plan structure.
+ ToolCallingChatModel model.ToolCallingChatModel
+ // ToolInfo defines the schema for the Plan structure when using tool calling.
+ // If not provided, PlanToolInfo will be used as the default.
+ ToolInfo *schema.ToolInfo
+
+ // GenInputFn is a function that generates the input messages for the planner.
+ // If not provided, defaultGenPlannerInputFn will be used as the default.
+ GenInputFn GenPlannerInputFn
+
+ // NewPlan creates a new Plan instance for JSON.
+ // The returned Plan will be used to unmarshal the model-generated JSON output.
+ // If not provided, defaultNewPlan will be used as the default.
+ NewPlan NewPlan
+}
+```
+
+#### 2. Executor
+
+- **核心功能**:执行计划中的首个步骤,调用外部工具完成具体任务
+- **实现方式**:基于 `ChatModelAgent` 实现,配置工具集(如搜索、计算、数据库访问等)
+- **工作流**:
+ - 从 Session 中获取当前 `Plan` 和已执行步骤
+ - 提取计划中的第一个未执行步骤作为目标
+ - 调用工具执行该步骤,将结果存储于 Session
+- **关键能力**:支持多轮工具调用(通过 `MaxIterations` 控制),确保单步任务完成
+
+```go
+// ExecutorConfig provides configuration options for creating a executor agent.
+type ExecutorConfig struct {
+ // Model is the chat model used by the executor.
+ Model model.ToolCallingChatModel
+
+ // ToolsConfig is the tools configuration used by the executor.
+ ToolsConfig adk.ToolsConfig
+
+ // MaxIterations defines the upper limit of ChatModel generation cycles.
+ // The agent will terminate with an error if this limit is exceeded.
+ // Optional. Defaults to 20.
+ MaxIterations int
+
+ // GenInputFn is the function that generates the input messages for the Executor.
+ // Optional. If not provided, defaultGenExecutorInputFn will be used.
+ GenInputFn GenPlanExecuteInputFn
+}
+```
+
+#### 3. Replanner
+
+- **核心功能**:评估执行进度,决定继续执行(生成新计划)或终止任务(返回结果)
+- **实现方式**:基于工具调用模型,通过 `PlanTool`(生成新计划)或 `RespondTool`(返回结果)输出决策
+- **决策逻辑**:
+ - **继续执行**:若目标未达成,生成包含剩余步骤的新计划,更新 Session 中的 `Plan`
+ - **终止任务**:若目标已达成,调用 `RespondTool` 生成最终用户响应
+
+```go
+type ReplannerConfig struct {
+
+ // ChatModel is the model that supports tool calling capabilities.
+ // It will be configured with PlanTool and RespondTool to generate updated plans or responses.
+ ChatModel model.ToolCallingChatModel
+
+ // PlanTool defines the schema for the Plan tool that can be used with ToolCallingChatModel.
+ // If not provided, the default PlanToolInfo will be used.
+ PlanTool *schema.ToolInfo
+
+ // RespondTool defines the schema for the response tool that can be used with ToolCallingChatModel.
+ // If not provided, the default RespondToolInfo will be used.
+ RespondTool *schema.ToolInfo
+
+ // GenInputFn is the function that generates the input messages for the Replanner.
+ // if not provided, buildDefaultReplannerInputFn will be used.
+ GenInputFn GenPlanExecuteInputFn
+
+ // NewPlan creates a new Plan instance.
+ // The returned Plan will be used to unmarshal the model-generated JSON output from PlanTool.
+ // If not provided, defaultNewPlan will be used as the default.
+ NewPlan NewPlan
+}
+```
+
+#### 4. PlanExecuteAgent
+
+- **核心功能**:组合上述三个智能体,形成「规划 → 执行 → 重规划」的循环工作流
+- **实现方式**:通过 `SequentialAgent` 和 `LoopAgent` 组合:
+ - 外层 `SequentialAgent`:先执行 `Planner` 生成初始计划,再进入执行-重规划循环
+ - 内层 `LoopAgent`:循环执行 `Executor` 和 `Replanner`,直至任务完成或达到最大迭代次数
+
+```go
+// New creates a new plan execute agent with the given configuration.
+func New(ctx context.Context, cfg *PlanExecuteConfig) (adk.Agent, error)
+
+// Config provides configuration options for creating a plan execute agent.
+type Config struct {
+ Planner adk.Agent
+ Executor adk.Agent
+ Replanner adk.Agent
+ MaxIterations int
+}
+```
+
+### Plan-Execute Agent 运行流程
+
+Plan-Execute Agent 的完整工作流程如下:
+
+1. **初始化**:用户输入目标任务,启动 `PlanExecuteAgent`
+2. **规划阶段**:
+ - `Planner` 接收用户目标,生成初始 `Plan`(步骤列表)
+ - `Plan` 存储于 Session(`PlanSessionKey`)
+3. **执行-重规划循环**(由 `LoopAgent` 控制):
+ - **执行步骤**:`Executor` 从 `Plan` 中提取首个步骤,调用工具执行,结果存入 Session(`ExecutedStepsSessionKey`)
+ - **反思步骤**:`Replanner` 评估已执行步骤与结果:
+ - 若目标达成:调用 `RespondTool` 生成最终响应,退出循环
+ - 若需继续:生成新 `Plan` 并更新 Session,进入下一轮循环
+4. **终止条件**:任务完成(`Replanner` 返回结果)或达到最大迭代次数(`MaxIterations`)
+
+## Plan-Execute Agent 使用示例
+
+### 场景说明
+
+实现一个「调研」Agent:
+
+1. **Planner**:为调研目标规划详细步骤
+2. **Executor**:执行计划中的首个步骤,必要时使用搜索工具(duckduckgo)
+3. **Replanner**:评估执行结果,若信息不足则调整计划,否则生成最终总结
+
+### 代码实现
+
+#### 1. 初始化模型与工具
+
+```go
+// 初始化支持工具调用的 OpenAI 模型
+func newToolCallingModel(ctx context.Context) model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: "gpt-4o", // 需支持工具调用
+ })
+ if err != nil {
+ log.Fatalf("初始化模型失败: %v", err)
+ }
+ return cm
+}
+
+// 初始化搜索工具(用于 Executor 调用)
+func newSearchTool(ctx context.Context) tool.BaseTool {
+ config := &duckduckgo.Config{
+ MaxResults: 20, // Limit to return 20 results
+ Region: duckduckgo._RegionWT_,
+ Timeout: 10 * time._Second_,
+ }
+ tool, err := duckduckgo.NewTextSearchTool(ctx, config)
+ if err != nil {
+ log.Fatalf("初始化搜索工具失败: %v", err)
+ }
+ return tool
+}
+```
+
+#### 2. 创建 Planner(规划器)
+
+```go
+func newPlanner(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ planner, err := planexecute.NewPlanner(ctx, &planexecute.PlannerConfig{
+ ToolCallingChatModel: model, // 使用工具调用模型生成计划
+ ToolInfo: &planexecute.PlanToolInfo, // 默认 Plan 工具 schema
+ })
+ if err != nil {
+ log.Fatalf("创建 Planner 失败: %v", err)
+ }
+ return planner
+}
+```
+
+#### 3. 创建 Executor(执行器)
+
+```go
+func newExecutor(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ // 配置 Executor 工具集(仅包含搜索工具)
+ toolsConfig := adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{newSearchTool(ctx)},
+ },
+ }
+ executor, err := planexecute.NewExecutor(ctx, &planexecute.ExecutorConfig{
+ Model: model,
+ ToolsConfig: toolsConfig,
+ MaxIterations: 5, // ChatModel 最多运行 5 次
+ })
+ if err != nil {
+ log.Fatalf("创建 Executor 失败: %v", err)
+ }
+ return executor
+}
+```
+
+#### 4. 创建 Replanner(重规划器)
+
+```go
+func newReplanner(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ replanner, err := planexecute.NewReplanner(ctx, &planexecute.ReplannerConfig{
+ ChatModel: model, // 使用工具调用模型评估进度
+ })
+ if err != nil {
+ log.Fatalf("创建 Replanner 失败: %v", err)
+ }
+ return replanner
+}
+```
+
+#### 5. 组合为 PlanExecuteAgent
+
+```go
+func newPlanExecuteAgent(ctx context.Context) adk.Agent {
+ model := newToolCallingModel(ctx)
+
+ // 实例化三大核心智能体
+ planner := newPlanner(ctx, model)
+ executor := newExecutor(ctx, model)
+ replanner := newReplanner(ctx, model)
+
+ // 组合为 PlanExecuteAgent(固定 execute - replan 最大迭代 10 次)
+ planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{
+ Planner: planner,
+ Executor: executor,
+ Replanner: replanner,
+ MaxIterations: 10,
+ })
+ if err != nil {
+ log.Fatalf("组合 PlanExecuteAgent 失败: %v", err)
+ }
+ return planExecuteAgent
+}
+```
+
+#### 6. 运行与输出
+
+```go
+import (
+ "context"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/adk/prebuilt/planexecute"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+ agent := newPlanExecuteAgent(ctx)
+
+ // 创建 Runner 执行智能体
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent, EnableStreaming: true})
+
+ // 用户输入目标任务
+ userInput := []adk.Message{
+ schema.UserMessage("Research and summarize the latest developments in AI for healthcare in 2024, including key technologies, applications, and industry trends."),
+ }
+
+ // 执行并打印结果
+ events := runner.Run(ctx, userInput)
+ for {
+ event, ok := events.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Printf("执行错误: %v", event.Err)
+ break
+ }
+ // 打印智能体输出(计划、执行结果、最终响应等)
+ if msg, err := event.Output.MessageOutput.GetMessage(); err == nil && msg.Content != "" {
+ log.Printf("\n=== Agent Output ===\n%s\n", msg.Content)
+ }
+ }
+}
+```
+
+### 运行结果
+
+```markdown
+2025/09/08 11:47:42
+=== Agent:Planner Output ===
+{"steps":["Identify the most recent and credible sources for AI developments in healthcare in 2024, such as scientific journals, industry reports, news articles, and expert analyses.","Extract and compile the key technologies emerging or advancing in AI for healthcare in 2024, including machine learning models, diagnostic tools, robotic surgery, personalized medicine, and data management solutions.","Analyze the main applications of AI in healthcare during 2024, focusing on areas such as diagnostics, patient care, drug discovery, medical imaging, and healthcare administration.","Investigate current industry trends related to AI in healthcare for 2024, including adoption rates, regulatory changes, ethical considerations, funding landscape, and market forecasts.","Synthesize the gathered information into a comprehensive summary covering the latest developments in AI for healthcare in 2024, highlighting key technologies, applications, and industry trends with examples and implications."]}
+2025/09/08 11:47:47
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.researchgate.net/publication/389402322_Artificial_Intelligence_in_Healthcare_2024_Year_in_Review","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in..."},{"title":"AI in Healthcare - Nature","url":"https://www.nature.com/collections/hacjaaeafj","summary":"\"AI in Healthcare\" encompasses the use of AI technologies to enhance various aspects of healthcare delivery, from diagnostics to treatment personalization, ultimately aiming to improve..."},{"title":"Evolution of artificial intelligence in healthcare: a 30-year ...","url":"https://www.frontiersin.org/journals/medicine/articles/10.3389/fmed.2024.1505692/full","summary":"Conclusion: This study reveals a sustained explosive growth trend in AI technologies within the healthcare sector in recent years, with increasingly profound applications in medicine. Additionally, medical artificial intelligence research is dynamically evolving with the advent of new technologies."},{"title":"The Impact of Artificial Intelligence on Healthcare: A Comprehensive ...","url":"https://onlinelibrary.wiley.com/doi/full/10.1002/hsr2.70312","summary":"This review analyzes the impact of AI on healthcare using data from the Web of Science (2014-2024), focusing on keywords like AI, ML, and healthcare applications."},{"title":"Artificial intelligence in healthcare (Review) - PubMed","url":"https://pubmed.ncbi.nlm.nih.gov/39583770/","summary":"Furthermore, the barriers and constraints that may impede the use of AI in healthcare are outlined, and the potential future directions of AI-augmented healthcare systems are discussed."},{"title":"Full article: Towards new frontiers of healthcare systems research ...","url":"https://www.tandfonline.com/doi/full/10.1080/20476965.2024.2402128","summary":"In this editorial, we begin by taking a quick look at the recent past of AI and its use in health. We then present the current landscape of AI research in health. We further discuss promising avenues for novel innovations in health systems research."},{"title":"AI in healthcare: New research shows promise and limitations of ...","url":"https://www.sciencedaily.com/releases/2024/10/241028164534.htm","summary":"Researchers have studied how well doctors used GPT-4 -- an artificial intelligence (AI) large language model system -- for diagnosing patients."},{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.medrxiv.org/content/10.1101/2025.02.26.25322978v2","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in healthcare."},{"title":"Investigating the Key Trends in Applying Artificial Intelligence to ...","url":"https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0322197","summary":"The findings of this review are useful for healthcare professionals to acquire deeper knowledge on the use of medical AI from design to implementation stage. However, a thorough assessment is essential to gather more insights into whether AI benefits outweigh its risks."},{"title":"Revolutionizing healthcare and medicine: The impact of modern ...","url":"https://pubmed.ncbi.nlm.nih.gov/39479277/","summary":"Wearable technology, the Internet of Medical Things, and sensor technologies have empowered individuals to take an active role in tracking and managing their health. These devices facilitate real-time data collection, enabling preventive and personalized care."}]}
+2025/09/08 11:47:52
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Generative AI in healthcare: Current trends and future outlook","url":"https://www.mckinsey.com/industries/healthcare/our-insights/generative-ai-in-healthcare-current-trends-and-future-outlook","summary":"The latest survey, conducted in the fourth quarter of 2024, found that 85 percent of respondents—healthcare leaders from payers, health systems, and healthcare services and technology (HST) groups—were exploring or had already adopted gen AI capabilities."},{"title":"AI in healthcare - statistics & facts | Statista","url":"https://www.statista.com/topics/10011/ai-in-healthcare/","summary":"Distribution of confidence in using a new technology and AI in healthcare among health professionals in Denmark, France, Germany, and the United Kingdom as of 2024"},{"title":"Medscape and HIMSS Release 2024 Report on AI Adoption in Healthcare","url":"https://www.prnewswire.com/news-releases/medscape-and-himss-release-2024-report-on-ai-adoption-in-healthcare-302324936.html","summary":"The full \"AI Adoption in Healthcare Report 2024\" is now available on both Medscape and HIMSS websites offering detailed analysis and insights into the current state of AI adoption in..."},{"title":"AI in Healthcare Market Size, Share | Growth Report [2025-2032]","url":"https://www.fortunebusinessinsights.com/industry-reports/artificial-intelligence-in-healthcare-market-100534","summary":"The global AI in healthcare market research report delivers an in-depth market analysis, highlighting essential elements such as an overview of advanced technologies, the regulatory landscape in key countries, and the challenges encountered in adopting and implementing AI-based solutions."},{"title":"Artificial Intelligence in Healthcare Market Size to Hit USD 613.81 Bn ...","url":"https://www.precedenceresearch.com/artificial-intelligence-in-healthcare-market","summary":"The global artificial intelligence (AI) in healthcare market size reached USD 26.69 billion in 2024 and is projected to hit around USD 613.81 billion by 2034, at a CAGR of 36.83%."},{"title":"AI In Healthcare Market Size, Share | Industry Report, 2033","url":"https://www.globalmarketstatistics.com/market-reports/artificial-intelligence-in-healthcare-market-12394","summary":"Market Size and Growth: The Artificial Intelligence in Healthcare Market Market size was USD 5011.24 Million in 2024, is projected to grow to USD 5762.41 Million by 2025 and exceed USD 8966.05 Million by 2033, with a CAGR of 21.4% from 2025-2033."},{"title":"AI in healthcare statistics: 62 findings from 18 research reports - Keragon","url":"https://www.keragon.com/blog/ai-in-healthcare-statistics","summary":"Bringing together the data — 12 data-driven insights from 6 different research reports — we revealed a range of concerns surrounding AI in healthcare. The key obstacles the data unpacked are misdiagnoses, transparency, data accuracy, and human oversight."},{"title":"AI in Healthcare Statistics By Market Share And Technology","url":"https://www.sci-tech-today.com/stats/ai-in-healthcare-statistics/","summary":"According to AI in Healthcare Statistics, the US will lead the global AI healthcare market in 2024, which is projected to reach USD 24.7 billion. In the same year, US healthcare AI..."},{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.researchgate.net/publication/389402322_Artificial_Intelligence_in_Healthcare_2024_Year_in_Review","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in..."},{"title":"19+ AI in Healthcare Statistics for 2024: Insights & Projections","url":"https://www.allaboutai.com/resources/ai-statistics/healthcare/","summary":"Discover 19+ AI in healthcare statistics for 2024, covering public perception, market trends, and revenue projections with expert insights."}]}
+2025/09/08 11:47:58
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.researchgate.net/publication/389402322_Artificial_Intelligence_in_Healthcare_2024_Year_in_Review","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in..."},{"title":"Trustworthy AI in Healthcare Insights from IQVIA 2024 Report","url":"https://aipressroom.com/trustworthy-ai-healthcare-insights-iqvia-2024/","summary":"Discover how AI is advancing healthcare with trusted frameworks, real-world impact, and strategies for ethical, scalable adoption."},{"title":"The Impact of Artificial Intelligence on Healthcare: A Comprehensive ...","url":"https://onlinelibrary.wiley.com/doi/full/10.1002/hsr2.70312","summary":"This review analyzes the impact of AI on healthcare using data from the Web of Science (2014-2024), focusing on keywords like AI, ML, and healthcare applications."},{"title":"Generative AI in Healthcare: 2024's Breakthroughs and What's Next for ...","url":"https://www.signifyresearch.net/insights/generative-ai-news-round-up-december-2024/","summary":"As 2024 draws to a close, generative AI in healthcare has achieved remarkable milestones. This year has been defined by both groundbreaking innovation and insightful exploration, with AI transforming workflows in medical imaging and elevating patient care across digital health solutions."},{"title":"Generative AI in healthcare: Current trends and future outlook","url":"https://www.mckinsey.com/industries/healthcare/our-insights/generative-ai-in-healthcare-current-trends-and-future-outlook","summary":"The latest survey, conducted in the fourth quarter of 2024, found that 85 percent of respondents—healthcare leaders from payers, health systems, and healthcare services and technology (HST) groups—were exploring or had already adopted gen AI capabilities."},{"title":"Artificial Intelligence in Healthcare: 2024 Year in Review","url":"https://www.medrxiv.org/content/10.1101/2025.02.26.25322978v2","summary":"The adoption of LLMs and text data types amongst various healthcare specialties, especially for education and administrative tasks, is unlocking new potential for AI applications in healthcare."},{"title":"How AI is improving diagnostics and health outcomes","url":"https://www.weforum.org/stories/2024/09/ai-diagnostics-health-outcomes/","summary":"By leveraging the power of AI for diagnostics, we can improve health outcomes and contribute to a future where healthcare is more accessible and effective for everyone, particularly in the communities that need it the most."},{"title":"Artificial Intelligence in Healthcare: 2024 Developments and Lega","url":"https://natlawreview.com/article/healthy-ai-2024-year-review","summary":"This publication provides an overview of important developments at the intersection of AI, healthcare and the law in 2024."},{"title":"What's next in AI and healthcare? | McKinsey & Company","url":"https://www.mckinsey.com/featured-insights/themes/whats-next-in-ai-and-healthcare","summary":"In healthcare—with patient well-being and lives at stake—the advancement of AI seems particularly momentous. In an industry battling staffing shortages and increasing costs, health system leaders need to consider all possible solutions, including AI technologies."},{"title":"AI in Healthcare: An Expert Analysis on Driving Transformational ...","url":"https://www.historytools.org/ai/healthcare-ai","summary":"Artificial intelligence (AI) has emerged as a disruptive force across industries, but few sectors are seeing more dramatic change than healthcare. Fueled by vast data growth, urgent cost pressures and new technological capabilities, AI adoption in health is accelerating rapidly."}]}
+2025/09/08 11:48:01
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Deep Dive: AI 2024 | pharmaphorum","url":"https://pharmaphorum.com/digital/deep-dive-ai-2024","summary":"In this issue, we delve into the transformative impact of AI on healthcare and pharma, featuring insights on key AI trends from the floor of Frontiers Health, the ongoing battle against..."},{"title":"Artificial Intelligence - Healthcare IT News","url":"https://www.healthcareitnews.com/topics/artificial-intelligence","summary":"Dr. Ethan Goh, executive director of Stanford ARISE, the AI Research and Science Evaluation Network, describes a new study to explore models' diagnostic and management reasoning capabilities - and what that could mean for clinicians and patients."},{"title":"7 ways AI is transforming healthcare | World Economic Forum","url":"https://www.weforum.org/stories/2025/08/ai-transforming-global-health/","summary":"While healthcare lags in AI adoption, these game-changing innovations - from spotting broken bones to assessing ambulance needs - show what's possible."},{"title":"Artificial Intelligence (AI) in Health Care | NEJM Catalyst","url":"https://catalyst.nejm.org/browse/catalyst-topic/ai-in-healthcare","summary":"As AI technology rapidly evolves, health care professionals grapple with the ethical implications of data ownership, privacy concerns, and the actionable insights derived from AI."},{"title":"From Robots to Healthcare: The Real Story Behind 2024's AI Investments","url":"https://www.algorithm-research.com/post/from-robots-to-healthcare-the-real-story-behind-2024-s-ai-investments","summary":"AI continues to reshape industries across the globe, with capital flowing into areas that promise the highest long-term impact. According to the 2025 AI Index Report by Stanford University, global AI investments in 2024 reached new highs, but they were far from evenly distributed."},{"title":"2024 Medical Breakthroughs Revolutionizing Healthcare","url":"https://medicalnewscorner.com/2024-medical-breakthroughs-revolutionizing-healthcare/","summary":"The medical field is set for transformative advancements in 2024, with breakthroughs in gene editing, cancer treatment, artificial intelligence, telemedicine, mental health, and wearable technology, promising to enhance patient care and outcomes globally."},{"title":"Artificial Intelligence - JAMA Network","url":"https://jamanetwork.com/collections/44024/artificial-intelligence","summary":"Explore the latest in AI in medicine, including studies of how chatbots, large language models (LLMs), natural language processing, and machine learning are transforming medicine and health care."},{"title":"Ai医疗技术:2024年及以后的发展趋势-家医大健康","url":"https://www.familydoctor.cn/news/ai-yiliao-jishu-yihou-fazhanqushi-192483.html","summary":"本文深入探讨了新一代健康AI技术在2024年的发展前景,包括先进诊断工具和个性化治疗计划等创新应用。 文章指出,通过机器学习和深度学习技术的突破,AI将在疾病早期检测、患者数据实时分析和医疗资源优化分配方面发挥关键作用。"},{"title":"AI in Healthcare | Artificial intelligence in healthcare news","url":"https://aiin.healthcare/","summary":"AI in Healthcare is the leading source of information on the latest developments in the use of artificial intelligence in healthcare. We provide coverage of AI-powered medical devices, software, and algorithms, as well as the ethical and regulatory challenges surrounding the use of AI in healthcare."},{"title":"19+ AI in Healthcare Statistics for 2024: Insights & Projections","url":"https://www.allaboutai.com/resources/ai-statistics/healthcare/","summary":"Discover 19+ AI in healthcare statistics for 2024, covering public perception, market trends, and revenue projections with expert insights."}]}
+2025/09/08 11:48:08
+=== Agent:Executor Output ===
+Here are some of the most recent and credible sources identified for AI developments in healthcare in 2024:
+
+Scientific Journals:
+- "Artificial Intelligence in Healthcare: 2024 Year in Review" (ResearchGate)
+- "AI in Healthcare - Nature" (nature.com collection)
+- "Evolution of artificial intelligence in healthcare: a 30-year study" (frontiersin.org)
+- "The Impact of Artificial Intelligence on Healthcare: A Comprehensive Review" (Wiley online library)
+- "Artificial intelligence in healthcare (Review)" (PubMed)
+- "Artificial Intelligence - JAMA Network" (jamanetwork.com collection)
+
+Industry Reports:
+- "Generative AI in healthcare: Current trends and future outlook" (McKinsey report, Q4 2024)
+- "Medscape and HIMSS Release 2024 Report on AI Adoption in Healthcare"
+- "AI in Healthcare Market Size, Share | Growth Report [2025-2032]" (Fortune Business Insights)
+- "Artificial Intelligence in Healthcare Market Size to Hit USD 613.81 Bn" (Precedence Research)
+
+News Articles:
+- "AI in healthcare: New research shows promise and limitations of GPT-4" (ScienceDaily, Oct 2024)
+- "Deep Dive: AI 2024" (pharmaphorum.com)
+- "Artificial Intelligence - Healthcare IT News"
+- "7 ways AI is transforming healthcare" (World Economic Forum, 2024)
+- "2024 Medical Breakthroughs Revolutionizing Healthcare" (medicalnewscorner.com)
+
+Expert Analyses:
+- "Trustworthy AI in Healthcare Insights from IQVIA 2024 Report"
+- "Generative AI in Healthcare: 2024's Breakthroughs and What's Next" (SignifyResearch)
+- "AI in Healthcare: An Expert Analysis on Driving Transformational Change"
+
+These sources cover a broad spectrum including peer-reviewed journals, authoritative market research reports, reputable news publications, and expert thought leadership on the latest AI innovations, applications, and trends in healthcare for 2024. Shall I proceed to extract and compile key technologies emerging or advancing in AI for healthcare in 2024 from these sources?
+2025/09/08 11:48:15
+=== Agent:Replanner Output ===
+{"steps":["Extract and compile the key technologies emerging or advancing in AI for healthcare in 2024, focusing on machine learning models, diagnostic tools, robotic surgery, personalized medicine, and data management solutions.","Analyze the main applications of AI in healthcare during 2024, concentrating on diagnostics, patient care, drug discovery, medical imaging, and healthcare administration.","Investigate current industry trends related to AI in healthcare for 2024, including adoption rates, regulatory changes, ethical considerations, funding landscape, and market forecasts.","Synthesize the gathered information into a comprehensive summary covering the latest developments in AI for healthcare in 2024, highlighting key technologies, applications, and industry trends with examples and implications."]}
+2025/09/08 11:48:20
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Five Machine Learning Innovations Shaping Healthcare in 2024","url":"https://healthmanagement.org/c/artificial-intelligence/News/five-machine-learning-innovations-shaping-healthcare-in-2024","summary":"Discover 5 AI & ML trends transforming UK healthcare, from explainable AI to edge AI, enhancing patient care and operational efficiency."},{"title":"How AI is improving diagnostics and health outcomes","url":"https://www.weforum.org/stories/2024/09/ai-diagnostics-health-outcomes/","summary":"Effective and ethical AI solutions in diagnostics require collaboration. Artificial intelligence (AI) is transforming healthcare by improving diagnostic accuracy, enabling earlier disease detection and enhancing patient outcomes."},{"title":"The Impact of Artificial Intelligence on Healthcare: A Comprehensive ...","url":"https://onlinelibrary.wiley.com/doi/full/10.1002/hsr2.70312","summary":"The study aims to describe AI in healthcare, including important technologies like robotics, machine learning (ML), deep learning (DL), and natural language processing (NLP), and to investigate how these technologies are used in patient interaction, predictive analytics, and remote monitoring."},{"title":"Unveiling the potential of artificial intelligence in revolutionizing ...","url":"https://eurjmedres.biomedcentral.com/articles/10.1186/s40001-025-02680-7","summary":"The rapid advancement of Machine Learning (ML) and Deep Learning (DL) technologies has revolutionized healthcare, particularly in the domains of disease prediction and diagnosis."},{"title":"Trends in AI for Disease and Diagnostic Prediction: A Healthcare ...","url":"https://link.springer.com/chapter/10.1007/978-3-031-84404-1_5","summary":"This chapter explores the transformative impact of artificial intelligence (AI) on the healthcare system, particularly in enhancing the accuracy, efficiency, and speed of disease diagnostics. A key advantage of AI integration in healthcare lies in its capacity to..."},{"title":"Unleashing the potential of AI in modern healthcare: Machine learning ...","url":"https://www.researchgate.net/publication/385135063_Unleashing_the_potential_of_AI_in_modern_healthcare_Machine_learning_algorithms_and_intelligent_medical_robots","summary":"Overall, AI, through machine learning algorithms and intelligent medical robots, is revolutionizing healthcare by offering promising improvements in diagnostics, surgical precision,..."},{"title":"The impact of artificial intelligence on remote healthcare: Enhancing ...","url":"https://www.sciencedirect.com/science/article/pii/S2949866X24001230","summary":"Key applications of AI, such as AI-enabled diagnostic systems, predictive analytics, and teleconsultation platforms, are reviewed for their strengths in overcoming the limitations of the traditional models of remote healthcare."},{"title":"15 AI Tools Transforming Healthcare in 2025 | The Future of Medicine","url":"https://healthcarereaders.com/insights/top-ai-tools-in-healthcare","summary":"From predictive analytics to generative AI (GenAI), these technologies are improving patient outcomes while reducing costs. AI is not new to healthcare—predictive analytics and machine learning have been used for years in diagnostics, treatment planning, and patient monitoring."},{"title":"How AI Is Transforming Healthcare: Diagnosis, Treatment, and Beyond","url":"https://www.sciencenewstoday.org/how-ai-is-transforming-healthcare-diagnosis-treatment-and-beyond","summary":"In this sweeping journey into the AI-healthcare nexus, we will explore how artificial intelligence is reshaping diagnosis, revolutionizing treatment, enhancing patient care, and opening doors to possibilities once thought impossible."},{"title":"Top 10 AI in Healthcare Applications 2025 | Diagnosis to Patient Care","url":"https://www.guvi.in/blog/ai-in-healthcare-applications/","summary":"What is Artificial intelligence in Healthcare? AI in healthcare refers to the use of advanced algorithms and machine learning models that analyze complex medical data and support clinical decisions. It helps doctors identify patterns in imaging results, predict disease risks through patient records, and recommend treatment options tailored to individual needs. Top 10 Applications of AI in ..."}]}
+2025/09/08 11:48:28
+=== Agent:Executor Output ===
+Key technologies emerging or advancing in AI for healthcare in 2024, based on recent findings, include:
+
+1. Machine Learning Models:
+- Advanced machine learning (ML) and deep learning (DL) technologies are revolutionizing disease prediction, diagnostics accuracy, and patient outcome improvements.
+- Innovations include explainable AI models that enhance transparency and trust in clinical decision-making.
+- Edge AI is gaining traction, enabling real-time data processing at healthcare facilities for faster diagnostics and response.
+
+2. Diagnostic Tools:
+- AI-enabled diagnostic systems are enhancing speed, accuracy, and early detection of diseases.
+- Integration of AI in medical imaging allows for better identification of abnormalities and tailored treatment plans.
+- Use of natural language processing (NLP) to analyze patient records and literature supports predictive analytics and diagnostics.
+
+3. Robotic Surgery:
+- Intelligent medical robots equipped with AI algorithms are improving surgical precision and reducing invasiveness.
+- AI facilitates real-time guidance and adaptive control during surgeries, increasing safety and effectiveness.
+
+4. Personalized Medicine:
+- AI models analyze individual patient data to recommend customized treatment plans.
+- Predictive analytics support identification of patient-specific risk factors and therapeutic responses.
+- AI-driven genomics and biomarker analysis accelerate personalized drug development.
+
+5. Data Management Solutions:
+- AI-powered data management platforms enable integration and analysis of large-scale heterogeneous healthcare data.
+- Predictive analytics and remote monitoring systems optimize patient care and hospital operations.
+- Secure and compliant AI solutions address privacy and ethical concerns in managing healthcare information.
+
+These technologies collectively contribute to enhancing diagnostics, treatment precision, patient care, and operational efficiency in healthcare settings in 2024. Would you like me to proceed with analyzing the main applications of AI in healthcare during 2024 next?
+2025/09/08 11:48:33
+=== Agent:Replanner Output ===
+{"steps":["Analyze the main applications of AI in healthcare during 2024, concentrating on diagnostics, patient care, drug discovery, medical imaging, and healthcare administration.","Investigate current industry trends related to AI in healthcare for 2024, including adoption rates, regulatory changes, ethical considerations, funding landscape, and market forecasts.","Synthesize the gathered information into a comprehensive summary covering the latest developments in AI for healthcare in 2024, highlighting key technologies, applications, and industry trends with examples and implications."]}
+2025/09/08 11:48:39
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"How AI is improving diagnostics and health outcomes","url":"https://www.weforum.org/stories/2024/09/ai-diagnostics-health-outcomes/","summary":"By leveraging the power of AI for diagnostics, we can improve health outcomes and contribute to a future where healthcare is more accessible and effective for everyone, particularly in the communities that need it the most."},{"title":"14 Top Use Cases for AI in Healthcare in 2024","url":"https://www.cake.ai/blog/top-ai-healthcare-use-cases","summary":"We will explore the 14 top use cases for AI in healthcare, demonstrating how these technologies are improving patient outcomes and streamlining operations from the front desk to the operating room."},{"title":"Artificial Intelligence (AI) Applications in Drug Discovery and Drug ...","url":"https://pubmed.ncbi.nlm.nih.gov/39458657/","summary":"In this review article, we will present a comprehensive overview of AI's applications in the pharmaceutical industry, covering areas such as drug discovery, target optimization, personalized medicine, drug safety, and more."},{"title":"Top 10 AI in Healthcare Applications 2025 | Diagnosis to Patient Care","url":"https://www.guvi.in/blog/ai-in-healthcare-applications/","summary":"Unravel the top 10 AI in healthcare applications transforming 2025, from diagnosis accuracy to patient care, drug discovery, monitoring, and cost reduction."},{"title":"AI in Healthcare: Enhancing Patient Care and Diagnosis","url":"https://www.park.edu/blog/ai-in-healthcare-enhancing-patient-care-and-diagnosis/","summary":"Below, we delve into the various applications of AI in healthcare and examine how it enhances patient care and diagnosis — along with the challenges and opportunities that lie ahead."},{"title":"Generative Artificial Intelligence in Healthcare: Applications ...","url":"https://www.mdpi.com/2673-7426/5/3/37","summary":"These generative AI models have shown widespread applications in clinical practice and research. Such applications range from medical documentation and diagnostics to patient communication and drug discovery."},{"title":"The Impact of Artificial Intelligence on Healthcare: A Comprehensive ...","url":"https://onlinelibrary.wiley.com/doi/full/10.1002/hsr2.70312","summary":"Core applications like remote monitoring and predictive analytics improve operational effectiveness and patient involvement. However, there are major obstacles to the mainstream implementation of AI in healthcare, including issues with data security and budget constraints."},{"title":"Reviewing Artificial Intelligence Applications in Healthcare ...","url":"https://worldscientificnews.com/reviewing-artificial-intelligence-applications-in-healthcare-diagnostics-benefits-challenges-and-future-directions/","summary":"Artificial intelligence (AI) transforms healthcare diagnostics by enabling faster, more accurate, cost-effective disease detection and treatment planning. This paper reviews AI's applications, benefits, challenges, and future directions in diagnostics."},{"title":"Artificial Intelligence in Healthcare: Transforming Diagnostics, Drug ...","url":"https://medtechnews.uk/research-reports/artificial-intelligence-in-healthcare-transforming-diagnostics-drug-discovery-and-ethical-considerations/","summary":"This report embarks on a detailed exploration of the diverse and expanding applications of AI in healthcare, with particular emphasis on its transformative contributions to disease diagnostics and the intricate, multi-stage process of drug discovery."},{"title":"AI in Healthcare Applications - admedica.com","url":"https://admedica.com/2024/12/23/real-world-applications-of-ai-in-healthcare/","summary":"From streamlining diagnostics to enhancing patient care, AI is making medical services faster, more accurate, and more personalized. Here's a closer look at how AI is being applied in healthcare, with real-world examples that highlight its impact."}]}
+2025/09/08 11:48:46
+=== Agent:Executor Output ===
+In 2024, AI applications in healthcare are notably advancing across several main areas:
+
+1. Diagnostics:
+- AI-powered diagnostic tools are enabling faster, more accurate, and cost-effective disease detection and treatment planning.
+- Techniques such as machine learning and generative AI improve early detection of conditions and enhance health outcomes.
+- AI supports analysis of medical imaging and patient records for precise diagnostics.
+- Real-world implementations demonstrate improved accessibility and effectiveness, particularly benefiting underserved communities.
+
+2. Patient Care:
+- AI enhances personalized patient care through predictive analytics, remote monitoring, and data integration.
+- Generative AI assists in medical documentation and patient communication, improving care coordination.
+- AI-driven tools help in chronic disease management, patient monitoring, and decision support.
+- These technologies facilitate more tailored, responsive healthcare experiences.
+
+3. Drug Discovery:
+- AI expedites drug discovery processes including target optimization, safety assessment, and personalized medicine development.
+- Generative AI and machine learning models aid in identifying new drug candidates and predicting therapeutic responses.
+- AI contributes to reducing costs and timelines in pharmaceutical research.
+
+4. Medical Imaging:
+- AI integration in medical imaging allows better identification of abnormalities and supports customized treatment planning.
+- Enhanced imaging diagnostics rely on deep learning models interpreting complex data with a higher degree of precision.
+- These advances enable earlier disease detection and more effective interventions.
+
+5. Healthcare Administration:
+- AI applications streamline healthcare operations such as scheduling, resource allocation, and billing.
+- Predictive analytics optimize hospital workflows and patient flow management.
+- AI solutions improve operational efficiency and reduce administrative burdens on healthcare providers.
+
+Overall, these AI applications in 2024 are driving transformative changes in healthcare delivery by improving diagnostic accuracy, enhancing patient care personalization, accelerating drug development, refining medical imaging analysis, and optimizing administrative processes. Would you like me to proceed with investigating the current industry trends related to AI in healthcare for 2024 next?
+2025/09/08 11:48:49
+=== Agent:Replanner Output ===
+{"steps":["Investigate current industry trends related to AI in healthcare for 2024, including adoption rates, regulatory changes, ethical considerations, funding landscape, and market forecasts.","Synthesize the gathered information into a comprehensive summary covering the latest developments in AI for healthcare in 2024, highlighting key technologies, applications, and industry trends with examples and implications."]}
+2025/09/08 11:48:55
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Ethical and legal considerations in healthcare AI: innovation and ...","url":"https://royalsocietypublishing.org/doi/10.1098/rsos.241873","summary":"Artificial intelligence (AI) is transforming healthcare by enhancing diagnostics, personalizing medicine and improving surgical precision. However, its integration into healthcare systems raises significant ethical and legal challenges."},{"title":"Ethical Considerations in AI-Enabled Healthcare","url":"https://link.springer.com/chapter/10.1007/978-3-031-80813-5_18","summary":"Integrating Artificial Intelligence (AI) in healthcare has revolutionized patient care and operational workflows, yet it introduces significant ethical considerations. This chapter explores the impact of AI on key ethical principles—beneficence, nonmaleficence, autonomy, and justice."},{"title":"Ethical implications of AI-driven clinical decision support systems on ...","url":"https://bmcmedethics.biomedcentral.com/articles/10.1186/s12910-024-01151-8","summary":"Artificial intelligence-driven Clinical Decision Support Systems (AI-CDSS) are increasingly being integrated into healthcare for various purposes, including resource allocation. While these systems promise improved efficiency and decision-making, they also raise significant ethical concerns."},{"title":"Ethical debates amidst flawed healthcare artificial intelligence ...","url":"https://www.nature.com/articles/s41746-024-01242-1","summary":"Healthcare AI faces an ethical dilemma between selective and equitable deployment, exacerbated by flawed performance metrics. These metrics inadequately capture real-world complexities and..."},{"title":"Ethical Implications in AI-Based Health Care Decision Making: A ...","url":"https://liebertpub.com/doi/abs/10.1089/aipo.2024.0007","summary":"This critical analysis explores the ethical implications of AI-based health care decision making, examining the existing literature, methodological approaches, and ethical frameworks."},{"title":"AI ethics in medical research: the 2024 Declaration of Helsinki","url":"https://www.thelancet.com/journals/lancet/article/PIIS0140-6736(24)02376-6/fulltext","summary":"The recent update to the World Medical Association's Declaration of Helsinki,1 adopted at the 75th World Medical Association General Assembly in October, 2024, signals yet another milestone in the ongoing effort to safeguard ethical standards in medical research involving human participants."},{"title":"Navigating ethical considerations in the use of artificial intelligence ...","url":"https://pubmed.ncbi.nlm.nih.gov/39545614/","summary":"Results: The review highlighted critical ethical challenges, such as data privacy and security, accountability for AI-driven decisions, transparency in AI decision-making, and maintaining the human touch in care."},{"title":"The 5 Biggest Ethical Issues with AI in Healthcare","url":"https://www.keragon.com/blog/ethical-issues-with-ai-in-healthcare","summary":"What are the ethical issues with AI in healthcare? Dive into complex debates and considerations surrounding the ethical use of healthcare AI."},{"title":"Frontiers | Ethical-legal implications of AI-powered healthcare in ...","url":"https://www.frontiersin.org/journals/artificial-intelligence/articles/10.3389/frai.2025.1619463/full","summary":"It argues that by prioritizing ethical considerations in the development and deployment of AI, medical professionals can enhance health outcomes and cultivate patient trust, thereby bridging the gap between technological advancements and nuanced healthcare realities (Collins et al., 2024)."},{"title":"(PDF) Ethical framework for artificial intelligence in healthcare ...","url":"https://www.researchgate.net/publication/381669447_Ethical_framework_for_artificial_intelligence_in_healthcare_research_A_path_to_integrity","summary":"This article sets out to introduce a detailed framework designed to steer governance and offer a systematic method for assuring that AI applications in healthcare research are developed and..."}]}
+2025/09/08 11:49:01
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Medscape and HIMSS Release 2024 Report on AI Adoption in Healthcare","url":"https://www.prnewswire.com/news-releases/medscape-and-himss-release-2024-report-on-ai-adoption-in-healthcare-302324936.html","summary":"The full \"AI Adoption in Healthcare Report 2024\" is now available on both Medscape and HIMSS websites offering detailed analysis and insights into the current state of AI adoption..."},{"title":"AI in Healthcare Statistics 2025: Overview of Trends","url":"https://docus.ai/blog/ai-healthcare-statistics","summary":"As we step into 2025, let's see how AI in healthcare statistics from 2024 are shaping trends in patient care, diagnostics, and innovation."},{"title":"AI in healthcare - statistics & facts | Statista","url":"https://www.statista.com/topics/10011/ai-in-healthcare/","summary":"Distribution of confidence in using a new technology and AI in healthcare among health professionals in Denmark, France, Germany, and the United Kingdom as of 2024"},{"title":"AI In Healthcare Stats 2025: Adoption, Accuracy & Market","url":"https://www.demandsage.com/ai-in-healthcare-stats/","summary":"Get insights into AI in healthcare stats, including adoption rate, performance accuracy, and the rapidly growing market valuation."},{"title":"HIMSS and Medscape Unveil Groundbreaking Report on AI Adoption at ...","url":"https://gkc.himss.org/news/himss-and-medscape-unveil-groundbreaking-report-ai-adoption-health-systems","summary":"The findings, highlighted in the Medscape & HIMSS AI Adoption by Health Systems Report 2024, reveal that 86% of respondents already leverage AI in their medical organizations, with 60% recognizing its ability to uncover health patterns and diagnoses beyond human detection."},{"title":"Adoption of artificial intelligence in healthcare: survey of health ...","url":"https://academic.oup.com/jamia/article/32/7/1093/8125015","summary":"To evaluate the current state of AI adoption in US healthcare systems, assess successes and barriers to implementation during the early generative AI era. This cross-sectional survey was conducted in Fall 2024, and included 67 health systems members of the Scottsdale Institute, a collaborative of US non-profit healthcare organizations."},{"title":"19+ AI in Healthcare Statistics for 2024: Insights & Projections","url":"https://www.allaboutai.com/resources/ai-statistics/healthcare/","summary":"Discover 19+ AI in healthcare statistics for 2024, covering public perception, market trends, and revenue projections with expert insights."},{"title":"AI in Healthcare Statistics By Market Share And Technology","url":"https://www.sci-tech-today.com/stats/ai-in-healthcare-statistics/","summary":"In the second quarter of 2024, the US held a dominant position with a 58% revenue share, reflecting its strong focus on AI development and deployment. Similarly, the rest of the world followed..."},{"title":"AI in healthcare statistics: 62 findings from 18 research reports - Keragon","url":"https://www.keragon.com/blog/ai-in-healthcare-statistics","summary":"⚪️ Consumer adoption of gen AI for health reasons has remained flat, with just 37% of consumers using it in 2024 versus 40% in 2023. Source: Deloitte Center for Health Solutions's' 2024 Health Care Consumer Survey"},{"title":"New AMA report highlights physician optimism about AI in health care","url":"https://www.medicaleconomics.com/view/new-ama-report-highlights-physician-optimism-about-ai-in-health-care","summary":"The adoption of artificial intelligence (AI) in health care nearly doubled in 2024 compared to 2023 — a reflection of growing enthusiasm and decreasing apprehension toward the technology, despite some lingering concerns, according to a new report from the American Medical Association (AMA)."}]}
+2025/09/08 11:49:04
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"AI in Healthcare: Funding Resurgence for Biotech Startups in 2024","url":"https://techainews.digital/2024/12/12/ai-in-healthcare-funding-resurgence-for-biotech-startups-in-2024/","summary":"In summary, the funding landscape for AI-driven biotech and healthcare startups in 2024 is showing a marked revival after a challenging previous year. With an influx of capital reflecting strong investor interest, companies harnessing AI to revolutionize drug discovery and enhance healthcare processes are at the forefront of this resurgence."},{"title":"AI Healthcare Startups: Investment & Funding Trends","url":"https://www.delveinsight.com/blog/ai-healthcare-startups-funding-trends","summary":"Discover how AI healthcare startups are attracting billions in funding and reshaping the future of healthcare and pharma."},{"title":"How healthcare AI led a 'paradigm shift' in a $23B year for startups","url":"https://carta.com/data/industry-spotlight-healthcare-2024/","summary":"The rate of all new healthcare investments in which valuations were lower than that of the previous round declined slightly over the course of 2024, settling at 19% in the final quarter of the year. Still, down rounds remain a persistent aspect of the healthcare fundraising landscape."},{"title":"AI-Healthcare Startups Surge with Record Funding: A Look at 2025's ...","url":"https://opentools.ai/news/ai-healthcare-startups-surge-with-record-funding-a-look-at-2025s-promising-landscape","summary":"Notably, the landscape of AI-healthcare startup funding has demonstrated robust growth, amounting to $7.5 billion worldwide in 2024, with an additional $1.68 billion earmarked for early 2025."},{"title":"The State of the Funding Market for AI Companies: A 2024 - 2025 Outlook","url":"https://www.mintz.com/insights-center/viewpoints/2166/2025-03-10-state-funding-market-ai-companies-2024-2025-outlook","summary":"In 2024, these AI-driven companies captured a substantial share of venture capital funding. Overall, venture capital investment in healthcare rose to $23 billion, up from $20 billion in 2023, with nearly 30% of the 2024 funding directed toward AI-focused startups."},{"title":"2024 year-end market overview: Davids and Goliaths - Rock Health","url":"https://rockhealth.com/insights/2024-year-end-market-overview-davids-and-goliaths/","summary":"These dual trends—early-stage startup activity amidst big moves by large healthcare players—have created a David and Goliath dynamic in the healthcare innovation landscape. We see a future where David and Goliath can coexist, and even thrive together, to drive impactful change in healthcare."},{"title":"AI and TechBio Funding Lead the Charge: 2024 Digital Health Funding ...","url":"https://www.galengrowth.com/ai-and-techbio-funding-lead-the-charge-2024-digital-health-funding-resurgence/","summary":"Global digital health funding rebounded to $25.1 billion in 2024, marking a 5.5% year-on-year increase. Europe saw the highest regional growth, with funding surging by 27% from 2023, while the US retained its dominance, contributing $17.2 billion in funding."},{"title":"Healthcare venture capital investment amplified by AI in 2024: report","url":"https://www.healthcaredive.com/news/healthcare-venture-captial-funding-ai-boost-2024-silicon-valley-bank/736870/","summary":"Venture capital investment in healthcare increased in 2024, spurred by enthusiasm for artificial intelligence startups, according to a report by Silicon Valley Bank. Funding for U.S...."},{"title":"These 10 healthcare AI startups have pulled in the most cash","url":"https://pitchbook.com/news/articles/healthcare-ai-startups-vc-funding","summary":"As of Q3 2024, funding had surpassed 2023's total with $10.5 billion generated across 511 deals. These 10 healthcare AI startups have raised the most capital from VC investors, leading the space to new heights while capitalizing on the AI frenzy."},{"title":"AI Finance for Healthcare 2024 - byteplus.com","url":"https://www.byteplus.com/en/topic/394250","summary":"Explore AI finance trends in healthcare for 2024, including investment insights, market growth, and emerging technologies."}]}
+2025/09/08 11:49:05
+=== Agent:Executor Output ===
+{"message":"Found 10 results successfully.","results":[{"title":"Artificial Intelligence in Healthcare Market Size to Hit USD 613.81 Bn ...","url":"https://www.precedenceresearch.com/artificial-intelligence-in-healthcare-market","summary":"The global artificial intelligence (AI) in healthcare market size reached USD 26.69 billion in 2024 and is projected to hit around USD 613.81 billion by 2034, at a CAGR of 36.83%."},{"title":"AI in Healthcare Market Size, Share | Growth Report [2025-2032]","url":"https://www.fortunebusinessinsights.com/industry-reports/artificial-intelligence-in-healthcare-market-100534","summary":"The global AI in healthcare market size was valued at $29.01 billion in 2024 & is projected to grow from $39.25 billion in 2025 to $504.17 billion by 2032"},{"title":"AI in Healthcare Statistics 2025: Overview of Trends","url":"https://docus.ai/blog/ai-healthcare-statistics","summary":"As we step into 2025, let's see how AI in healthcare statistics from 2024 are shaping trends in patient care, diagnostics, and innovation."},{"title":"Artificial Intelligence (AI) in Healthcare Market Size to","url":"https://www.globenewswire.com/news-release/2025/04/02/3054390/0/en/Artificial-Intelligence-AI-in-Healthcare-Market-Size-to-Hit-USD-613-81-Bn-by-2034.html","summary":"Ottawa, April 02, 2025 (GLOBE NEWSWIRE) -- According to Precedence Research, the artificial intelligence (AI) in healthcare market size was valued at USD 26.69 billion in 2024, calculated..."},{"title":"19+ AI in Healthcare Statistics for 2024: Insights & Projections","url":"https://www.allaboutai.com/resources/ai-statistics/healthcare/","summary":"Discover 19+ AI in healthcare statistics for 2024, covering public perception, market trends, and revenue projections with expert insights."},{"title":"AI in Healthcare Market Leads 37.66% Healthy CAGR by 2034","url":"https://www.towardshealthcare.com/insights/ai-in-healthcare-market","summary":"According to market projections, the AI in healthcare sector is expected to grow from USD 27.59 billion in 2024 to USD 674.19 billion by 2034, reflecting a CAGR of 37.66%."},{"title":"AI In Healthcare Market Size, Share | Industry Report, 2033","url":"https://www.globalmarketstatistics.com/market-reports/artificial-intelligence-in-healthcare-market-12394","summary":"Market Size and Growth: The Artificial Intelligence in Healthcare Market Market size was USD 5011.24 Million in 2024, is projected to grow to USD 5762.41 Million by 2025 and exceed USD 8966.05 Million by 2033, with a CAGR of 21.4% from 2025-2033."},{"title":"AI in Healthcare Market Outlook 2024-2033: Trends ... - LinkedIn","url":"https://www.linkedin.com/pulse/ai-healthcare-market-outlook-20242033-trends-nil8c","summary":"AI in Healthcare Market size was valued at USD 11.6 Billion in 2024 and is forecasted to grow at a CAGR of 24% from 2026 to 2033, reaching USD 64.5 Billion by 2033."},{"title":"AI In Healthcare Market Size to Reach $187.7 Billion by 2030 at CAGR 38 ...","url":"https://www.prnewswire.com/news-releases/ai-in-healthcare-market-size-to-reach-187-7-billion-by-2030-at-cagr-38-5---grand-view-research-inc-302439558.html","summary":"SAN FRANCISCO, April 28, 2025 /PRNewswire/ -- The global AI in healthcare market size is expected to reach USD 187.7 billion by 2030, registering a CAGR of 38.5% from 2024 to 2030,..."},{"title":"AI in Healthcare Market to Soar to USD 629 B by 2032 with 51.87% CAGR","url":"https://www.medboundtimes.com/medbound-blog/ai-healthcare-market-set-to-soar-to-usd629b","summary":"The global artificial intelligence in healthcare market was valued at USD 22.23 billion in 2024 and is projected to skyrocket to USD 629.09 billion by 2032, exhibiting a compound annual growth rate (CAGR) of 51.87% from 2025 to 2032, according to Data Bridge Market Research."}]}
+```
+
+## 总结
+
+Plan-Execute Agent 通过「规划-执行-反思」的闭环工作流,将复杂任务拆解为可执行步骤,结合工具调用与动态调整,有效提升了任务完成的可靠性与效率。其核心优势在于:
+
+- **结构化任务拆解**:降低复杂问题的认知负荷
+- **工具集成能力**:无缝对接外部工具(搜索、计算、数据库等)
+- **动态适应性**:根据执行反馈实时调整策略,应对不确定性
+
+通过 Eino ADK 提供的 `PlanExecuteAgent`,开发者可快速搭建具备复杂任务处理能力的智能体系统,适用于研究分析、自动化办公、智能客服等多种场景。
+
+## 常见问题
+
+### 报错 [NodeRunError] no tool call
+
+Planner / Replanner 必须通过工具调用生成计划,出现此报错时请检查:
+
+1. 所使用的模型是否支持强制工具调用(例如 openai tool_choice="required")
+2. 所使用的模型 eino-ext 封装是否升级到最新(例如旧版本 ark sdk 不支持强制工具调用)
+
+### 报错 [NodeRunError] unexpected tool call
+
+Replanner 注册的 ChatModel 不应该通过 WithTools 方法携带额外工具,如有该情况请清空工具
+
+### 报错 [NodeRunError] unmarshal plan error
+
+Planner / Replanner config 中基于 PlanTool 和 NewPlan 两个字段共同生成计划:
+
+- PlanTool 作为向模型提供的 Plan 描述
+- NewPlan 方法作为框架构建 plan 的 builder,用于将模型返回的 Plan unmarshal 到该 struct 上供后续步骤运行
+
+当出现该错误时,请检查 PlanTool 中提供的字段描述是否和 NewPlan 方法中返回的结构体字段匹配,对齐后重新运行即可。
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_implementation/supervisor.md b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/supervisor.md
new file mode 100644
index 0000000..f583790
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/supervisor.md
@@ -0,0 +1,499 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Supervisor Agent
+weight: 3
+---
+
+## Supervisor Agent 概述
+
+### Import Path
+
+`import ``github.com/cloudwego/eino/adk/prebuilt/supervisor`
+
+### 什么是 Supervisor Agent?
+
+Supervisor Agent 是一种中心化多 Agent 协作模式,由一个监督者(Supervisor Agent) 和多个子 Agent(SubAgents)组成。Supervisor 负责任务的分配、子 Agent 执行过程的监控,以及子 Agent 完成后的结果汇总与下一步决策;子 Agent 则专注于执行具体任务,并在完成后通过 WithDeterministicTransferTo 自动将任务控制权交回 Supervisor。
+
+
+
+该模式适用于需要动态协调多个专业 Agent 完成复杂任务的场景,例如:
+
+- 科研项目管理(Supervisor 分配调研、实验、报告撰写任务给不同子 Agent)。
+- 客户服务流程(Supervisor 根据用户问题类型,分配给技术支持、售后、销售等子 Agent)。
+
+### Supervisor Agent 结构
+
+Supervisor 模式的核心结构如下:
+
+- **Supervisor Agent**:作为协作核心,具备任务分配逻辑(如基于规则或 LLM 决策),可通过 `SetSubAgents` 将子 Agent 纳入管理。
+- **SubAgents**:每个子 Agent 被 WithDeterministicTransferTo 增强,预设 `ToAgentNames` 为 Supervisor 名称,确保任务完成后自动转让回 Supervisor。
+
+### Supervisor Agent 特点
+
+1. **确定性回调**:子 Agent 执行完毕(未中断)后,通过 WithDeterministicTransferTo 自动触发 Transfer 事件,将任务控制权交回 Supervisor,避免协作流程中断。
+2. **中心化控制**:Supervisor 统一管理子 Agent,可根据子 Agent 的执行结果动态调整任务分配(如分配给其他子 Agent 或直接生成最终结果)。
+3. **松耦合扩展**:子 Agent 可独立开发、测试和替换,只需确保实现 Agent 接口并绑定到 Supervisor,即可接入协作流程。
+4. **支持中断与恢复**:若子 Agent 或 Supervisor 支持 `ResumableAgent` 接口,协作流程可在中断后恢复,保持任务上下文连续性。
+
+### Supervisor Agent 运行流程
+
+Supervisor 模式的典型协作流程如下:
+
+1. **任务启动**:Runner 触发 Supervisor 运行,输入初始任务(如“完成一份 LLM 发展历史报告”)。
+2. **任务分配**:Supervisor 根据任务需求,通过 Transfer 事件将任务转让给指定子 Agent(如“调研 Agent”)。
+3. **子 Agent 执行**:子 Agent 执行具体任务(如调研 LLM 关键里程碑),并生成执行结果事件。
+4. **自动回调**:子 Agent 完成后,WithDeterministicTransferTo 触发 Transfer 事件,将任务转让回 Supervisor。
+5. **结果处理**:Supervisor 接收子 Agent 的结果,决定下一步(如分配给“报告撰写 Agent”继续处理,或直接输出最终结果)。
+
+## Supervisor Agent 使用示例
+
+### 场景说明
+
+创建一个科研报告生成系统:
+
+- **Supervisor**:基于用户输入的研究主题,分配任务给“调研 Agent”和“撰写 Agent”,并汇总最终报告。
+- **调研 Agent**:负责生成研究计划(如 LLM 发展的关键阶段)。
+- **撰写 Agent**:负责根据调研计划撰写完整报告。
+
+### 代码实现
+
+#### 步骤 1:实现子 Agent
+
+首先创建两个子 Agent,分别负责调研和撰写任务:
+
+```go
+// 调研 Agent:生成研究计划
+func NewResearchAgent(model model.ToolCallingChatModel) adk.Agent {
+ agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ResearchAgent",
+ Description: "Generates a detailed research plan for a given topic.",
+ Instruction: `
+You are a research planner. Given a topic, output a step-by-step research plan with key stages and milestones.
+Output ONLY the plan, no extra text.`,
+ Model: model,
+ })
+ return agent
+}
+
+// 撰写 Agent:根据研究计划撰写报告
+func NewWriterAgent(model model.ToolCallingChatModel) adk.Agent {
+ agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "WriterAgent",
+ Description: "Writes a report based on a research plan.",
+ Instruction: `
+You are an academic writer. Given a research plan, expand it into a structured report with details and analysis.
+Output ONLY the report, no extra text.`,
+ Model: model,
+ })
+ return agent
+}
+```
+
+#### 步骤 2:实现 Supervisor Agent
+
+创建 Supervisor Agent,定义任务分配逻辑(此处简化为基于规则:先分配给调研 Agent,再分配给撰写 Agent):
+
+```go
+// Supervisor Agent:协调调研和撰写任务
+func NewReportSupervisor(model model.ToolCallingChatModel) adk.Agent {
+ agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ReportSupervisor",
+ Description: "Coordinates research and writing to generate a report.",
+ Instruction: `
+You are a project supervisor. Your task is to coordinate two sub-agents:
+- ResearchAgent: generates a research plan.
+- WriterAgent: writes a report based on the plan.
+
+Workflow:
+1. When receiving a topic, first transfer the task to ResearchAgent.
+2. After ResearchAgent finishes, transfer the task to WriterAgent with the plan as input.
+3. After WriterAgent finishes, output the final report.`,
+ Model: model,
+ })
+ return agent
+}
+```
+
+#### 步骤 3:组合 Supervisor 与子 Agent
+
+使用 `NewSupervisor` 将 Supervisor 和子 Agent 组合:
+
+```go
+import (
+ "context"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/adk/prebuilt/supervisor"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // 1. 创建 LLM 模型(如 GPT-4o)
+ model, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: "YOUR_API_KEY",
+ Model: "gpt-4o",
+ })
+
+ // 2. 创建子 Agent 和 Supervisor
+ researchAgent := NewResearchAgent(model)
+ writerAgent := NewWriterAgent(model)
+ reportSupervisor := NewReportSupervisor(model)
+
+ // 3. 组合 Supervisor 与子 Agent
+ supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{
+ Supervisor: reportSupervisor,
+ SubAgents: []adk.Agent{researchAgent, writerAgent},
+ })
+
+ // 4. 运行 Supervisor 模式
+ iter := supervisorAgent.Run(ctx, &adk.AgentInput{
+ Messages: []adk.Message{
+ schema.UserMessage("Write a report on the history of Large Language Models."),
+ },
+ EnableStreaming: true,
+ })
+
+ // 5. 消费事件流(打印结果)
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Output != nil && event.Output.MessageOutput != nil {
+ msg, _ := event.Output.MessageOutput.GetMessage()
+ println("Agent[" + event.AgentName + "]:\n" + msg.Content + "\n===========")
+ }
+ }
+}
+```
+
+### 运行结果
+
+```markdown
+Agent[ReportSupervisor]:
+
+===========
+Agent[ReportSupervisor]:
+successfully transferred to agent [ResearchAgent]
+===========
+Agent[ResearchAgent]:
+1. **Scope Definition & Background Research**
+ - Task: Define "Large Language Model" (LLM) for the report (e.g., size thresholds, key characteristics: transformer-based, large-scale pretraining, general-purpose).
+ - Task: Identify foundational NLP/AI concepts pre-LLMs (statistical models, early neural networks, word embeddings) to contextualize origins.
+ - Milestone: 3-day literature review of academic definitions, industry reports, and AI historiographies to finalize scope.
+
+2. **Chronological Periodization**
+ - Task: Divide LLM history into distinct eras (e.g., Pre-2017: Pre-transformer foundations; 2017-2020: Transformer revolution & early LLMs; 2020-Present: Scaling & mainstream adoption).
+ - Task: Map key events, models, and breakthroughs per era (e.g., 2017: "Attention Is All You Need"; 2018: GPT-1/BERT; 2020: GPT-3; 2022: ChatGPT; 2023: Llama 2).
+ - Milestone: 10-day timeline draft with annotated model releases, research papers, and technological shifts.
+
+3. **Key Technical Milestones**
+ - Task: Deep-dive into critical innovations (transformer architecture, pretraining-fine-tuning paradigm, scaling laws, in-context learning).
+ - Task: Extract details from seminal papers (authors, institutions, methodologies, performance benchmarks).
+ - Milestone: 1-week analysis of 5-7 foundational papers (e.g., Vaswani et al. 2017; Radford et al. 2018; Devlin et al. 2018) with technical summaries.
+
+4. **Stakeholder Mapping**
+ - Task: Identify key organizations (OpenAI, Google DeepMind, Meta AI, Microsoft Research) and academic labs (Stanford, Berkeley) driving LLM development.
+ - Task: Document institutional contributions (e.g., OpenAI’s GPT series, Google’s BERT/PaLM, Meta’s Llama) and research priorities (open vs. closed models).
+ - Milestone: 5-day stakeholder profile draft with org-specific timelines and model lineages.
+
+5. **Technical Evolution & Innovation Trajectory**
+ - Task: Analyze shifts in architecture (from RNNs/LSTMs to transformers), training paradigms (pretraining + fine-tuning → instruction tuning → RLHF), and compute scaling (parameters, data size, GPU usage over time).
+ - Task: Link technical changes to performance improvements (e.g., GPT-1 (124M params) vs. GPT-4 (100B+ params): task generalization, emergent abilities).
+ - Milestone: 1-week technical trajectory report with data visualizations (param scaling, benchmark scores over time).
+
+6. **Impact & Societal Context**
+ - Task: Research LLM impact on NLP tasks (translation, summarization, QA) and beyond (education, content creation, policy).
+ - Task: Document cultural/industry shifts (rise of prompt engineering, "AI-native" products, public perception post-ChatGPT).
+ - Milestone: 5-day impact analysis integrating case studies (e.g., GitHub Copilot, healthcare LLMs) and media/scholarly discourse.
+
+7. **Challenges & Critiques (Historical Perspective)**
+ - Task: Track historical limitations (pre-2020: data sparsity, task specificity; post-2020: bias, misinformation, energy use) and responses (e.g., 2019: BERT bias audits; 2023: EU AI Act).
+ - Task: Cite key critiques (e.g., "On the Dangers of Stochastic Parrots," 2021) and industry/academic reactions.
+ - Milestone: 5-day challenge timeline linking issues to their emergence and mitigation efforts.
+
+8. **Synthesis & Narrative Drafting**
+ - Task: Integrate chronological, technical, and societal data into a coherent narrative (origins → revolution → scaling → mainstream impact).
+ - Task: Outline report structure (Abstract, Introduction, Era-by-Era Analysis, Key Innovations, Stakeholders, Impact, Challenges, Conclusion).
+ - Milestone: 1-week first draft of full report (8,000–10,000 words).
+
+9. **Validation & Fact-Checking**
+ - Task: Verify model release dates, paper citations, parameter counts, and stakeholder claims via primary sources (original papers, official press releases, archived GitHub repos).
+ - Task: Cross-check with secondary sources (AI history books, expert interviews, peer-reviewed historiographies).
+ - Milestone: 3-day validation report flagging/correcting inaccuracies.
+
+10. **Finalization & Revision**
+ - Task: Edit for clarity, narrative flow, and consistency; refine visuals (timelines, param scaling charts).
+ - Task: Format references (APA/MLA) and appendices (model comparison table, key paper list).
+ - Milestone: 2-day final report submission.
+===========
+Agent[ResearchAgent]:
+
+===========
+Agent[ResearchAgent]:
+successfully transferred to agent [ReportSupervisor]
+===========
+Agent[ReportSupervisor]:
+
+===========
+Agent[ReportSupervisor]:
+successfully transferred to agent [WriterAgent]
+===========
+Agent[WriterAgent]:
+# The History of Large Language Models: From Foundations to Mainstream Revolution
+
+
+## Abstract
+Large Language Models (LLMs) represent one of the most transformative technological innovations of the 21st century, enabling machines to understand, generate, and manipulate human language with unprecedented fluency. This report traces the historical trajectory of LLMs, from their conceptual roots in early natural language processing (NLP) to their current status as mainstream tools. It examines key technical milestones—including the invention of the transformer architecture, the rise of pretraining-fine-tuning paradigms, and the scaling of model parameters—and contextualizes these within the contributions of academic labs and tech giants. The report also analyzes societal impacts, from revolutionizing NLP tasks to sparking debates over bias, misinformation, and AI regulation. By synthesizing chronological, technical, and cultural data, this history reveals how LLMs evolved from niche research experiments to agents of global change.
+
+
+## 1. Introduction: Defining Large Language Models
+A **Large Language Model (LLM)** is a type of machine learning model designed to process and generate human language by learning patterns from massive text datasets. Key characteristics include: (1) a transformer-based architecture, enabling parallel processing of text sequences; (2) large-scale pretraining on diverse corpora (e.g., books, websites, articles); (3) general-purpose functionality, allowing adaptation to tasks like translation, summarization, or dialogue without task-specific engineering; and (4) scale, typically defined by billions (or tens of billions) of parameters (adjustable weights that capture linguistic patterns).
+
+LLMs emerged from decades of NLP research, building on foundational concepts like statistical models (e.g., n-grams), early neural networks (e.g., recurrent neural networks [RNNs]), and word embeddings (e.g., Word2Vec, GloVe). By the 2010s, these predecessors had laid groundwork for "language understanding," but were limited by task specificity (e.g., a model trained for translation could not summarize text) and data sparsity. LLMs addressed these gaps by prioritizing scale, generality, and architectural innovation—ultimately redefining the boundaries of machine language capability.
+
+
+## 2. Era-by-Era Analysis: The Evolution of LLMs
+
+### 2.1 Pre-2017: Pre-Transformer Foundations (1950s–2016)
+The roots of LLMs lie in mid-20th-century NLP, when researchers first sought to automate language tasks. Early efforts relied on rule-based systems (e.g., 1950s machine translation using syntax rules) and statistical methods (e.g., 1990s n-gram models for speech recognition). By the 2010s, neural networks gained traction: RNNs and long short-term memory (LSTM) models (Hochreiter & Schmidhuber, 1997) enabled sequence modeling, while word embeddings (Mikolov et al., 2013) represented words as dense vectors, capturing semantic relationships.
+
+Despite progress, pre-2017 models faced critical limitations: RNNs/LSTMs processed text sequentially, making them slow to train and unable to handle long-range dependencies (e.g., linking "it" in a sentence to a noun paragraphs earlier). Data was also constrained: models like Word2Vec trained on millions, not billions, of tokens. These bottlenecks set the stage for a paradigm shift.
+
+
+### 2.2 2017–2020: The Transformer Revolution and Early LLMs
+The year 2017 marked the dawn of the LLM era with the publication of *"Attention Is All You Need"* (Vaswani et al.), which introduced the **transformer architecture**. Unlike RNNs, transformers use "self-attention" mechanisms to weigh the importance of different words in a sequence simultaneously, enabling parallel computation and capturing long-range dependencies. This breakthrough reduced training time and improved performance on language tasks.
+
+#### Key Models and Breakthroughs:
+- **2018**: OpenAI released **GPT-1** (Radford et al.), the first transformer-based LLM. With 124 million parameters, it introduced the "pretraining-fine-tuning" paradigm: pretraining on a large unlabeled corpus (BooksCorpus) to learn general language patterns, then fine-tuning on task-specific labeled data (e.g., sentiment analysis).
+- **2018**: Google published **BERT** (Devlin et al.), a bidirectional transformer that processed text from left-to-right *and* right-to-left, outperforming GPT-1 on context-dependent tasks like question answering. BERT’s success popularized "contextual embeddings," where word meaning depends on surrounding text (e.g., "bank" as a financial institution vs. a riverbank).
+- **2019**: OpenAI scaled up with **GPT-2** (1.5 billion parameters), demonstrating improved text generation but sparking early concerns about misuse (OpenAI initially delayed full release over fears of disinformation).
+- **2020**: Google’s **T5** (Text-to-Text Transfer Transformer) unified NLP tasks under a single "text-to-text" framework (e.g., translating "translate English to French: Hello" to "Bonjour"), simplifying model adaptation.
+
+
+### 2.3 2020–Present: Scaling, Emergence, and Mainstream Adoption
+The 2020s saw LLMs transition from research curiosities to global phenomena, driven by exponential scaling of parameters, data, and compute.
+
+#### Key Developments:
+- **2020**: OpenAI’s **GPT-3** (175 billion parameters) marked a turning point. Trained on 45 terabytes of text, it exhibited "few-shot" and "zero-shot" learning—adapting to tasks with minimal examples (e.g., "Write a poem about AI" with no prior poetry training). GPT-3’s release via API (OpenAI Playground) introduced LLMs to developers, enabling early applications like chatbots and code generation.
+- **2022**: **ChatGPT** (based on GPT-3.5) brought LLMs to the public. Launched in November, its user-friendly interface and conversational ability sparked a viral explosion (100 million users by January 2023). ChatGPT refined training with **Reinforcement Learning from Human Feedback (RLHF)**, aligning outputs with human preferences (e.g., helpfulness, safety).
+- **2023**: Meta released **Llama 2** (7B–70B parameters), an open-source LLM that lowered barriers to entry, allowing researchers and startups to fine-tune models without proprietary access. Meanwhile, OpenAI’s **GPT-4** (100B+ parameters) expanded multimodality (text + images) and improved reasoning (e.g., solving math problems, coding).
+- **2023–2024**: The "race to scale" continued with models like Google’s **PaLM 2** (540B parameters), Anthropic’s **Claude 2** (200B+ parameters), and open-source alternatives (e.g., Mistral, Falcon). Compute usage skyrocketed: training GPT-3 required ~3.14e23 floating-point operations (FLOPs), equivalent to 355 years of a single GPU’s work.
+
+
+## 3. Key Technical Milestones
+### 3.1 The Transformer Architecture (2017)
+Vaswani et al.’s *"Attention Is All You Need"* (Google, University of Toronto) replaced RNNs with self-attention, a mechanism that computes "attention scores" between every pair of words in a sequence. For example, in "The cat sat on the mat; it purred," self-attention links "it" to "cat." This parallel processing reduced training time from weeks (for RNNs) to days, enabling larger models.
+
+### 3.2 Pretraining-Fine-Tuning Paradigm (2018)
+GPT-1 and BERT established the now-standard workflow: (1) Pretrain on a large, unlabeled corpus (e.g., Common Crawl, a web scrape of 1.1 trillion tokens) to learn syntax, semantics, and world knowledge; (2) Fine-tune on task-specific data (e.g., GLUE, a benchmark of 10 NLP tasks). This decoupled language learning from task engineering, enabling generalization.
+
+### 3.3 Scaling Laws and Emergent Abilities (2020s)
+In 2020, OpenAI researchers articulated **scaling laws**: model performance improves predictably with increased parameters, data, and compute. By 2022, this led to "emergent abilities"—skills not present in smaller models, such as GPT-3’s in-context learning or GPT-4’s multi-step reasoning.
+
+### 3.4 Instruction Tuning and RLHF (2022)
+Post-2020, training shifted from task-specific fine-tuning to **instruction tuning** (training on natural language instructions like "Summarize this article") and **RLHF** (rewarding models for human-preferred outputs). These methods made LLMs more usable: ChatGPT, for instance, follows prompts like "Explain quantum physics like I’m 5" without explicit fine-tuning.
+
+
+## 4. Stakeholders: The Ecosystem of LLM Development
+LLM evolution has been driven by a mix of tech giants, academic labs, and startups, each with distinct priorities:
+
+### 4.1 Tech Giants: Closed vs. Open Models
+- **OpenAI** (founded 2015, backed by Microsoft): Pioneered the GPT series, prioritizing commercialization via closed APIs (e.g., ChatGPT Plus, GPT-4 API). Focus: user-friendliness and safety (via RLHF).
+- **Google DeepMind**: Developed BERT, T5, and PaLM, integrating LLMs into products like Google Search (via BERT) and Bard. Balances closed (PaLM) and open (T5) models.
+- **Meta AI**: Advocated for open science with Llama 1/2 (2023), releasing weights for research and commercial use. Meta’s "open" approach aims to democratize LLM access and accelerate safety research.
+- **Microsoft**: Partnered with OpenAI (2019–present), providing Azure compute and integrating GPT into Bing (search), Office (Copilot), and GitHub (Copilot X for coding).
+
+### 4.2 Academic Labs
+- **Stanford NLP**: Contributed to BERT and T5 research; developed HELM (Holistic Evaluation of Language Models), a benchmark for LLM safety and fairness.
+- **UC Berkeley**: Studied LLM bias (e.g., 2021 paper "On the Dangers of Stochastic Parrots," critiquing LLMs as "statistical mimics" lacking true understanding).
+
+
+## 5. Impact & Societal Context
+### 5.1 Transforming NLP and Beyond
+LLMs have redefined NLP performance: By 2023, GPT-4 outperformed humans on the MMLU benchmark (a test of 57 subjects, including math, law, and biology), scoring 86.4% vs. 86.5% for humans. Beyond NLP, they have revolutionized:
+- **Content Creation**: Tools like Jasper and Copy.ai automate marketing copy; artists use DALL-E (paired with LLMs) for text-to-image generation.
+- **Education**: Khan Academy’s Khanmigo tutors students; Coursera uses LLMs for personalized feedback.
+- **Coding**: GitHub Copilot (2021) generates code from comments, boosting developer productivity by 55% (Microsoft, 2023).
+
+### 5.2 Cultural Shifts
+- **Prompt Engineering**: The rise of "prompt engineers"—professionals skilled in crafting text inputs to elicit desired LLM outputs—became a new career path.
+- **AI-Native Products**: Startups like Character.AI (chatbots with distinct personalities) and Perplexity (AI-powered search) emerged as "LLM-first" services.
+- **Public Perception**: Post-ChatGPT, LLMs shifted from "AI hype" to tangible utility, though skepticism persists (e.g., 62% of U.S. adults worry about job displacement, Pew Research, 2023).
+
+
+## 6. Challenges & Critiques: A Historical Perspective
+### 6.1 Technical Limitations
+- **Pre-2020**: Data sparsity (small corpora limited generalization); task specificity (models like BERT required retraining for new tasks).
+- **Post-2020**: **Hallucinations** (fabricating facts, e.g., GPT-3 citing fake research papers); **energy use** (training GPT-3 emitted ~500 tons of CO₂, equivalent to 125 round-trip flights from NYC to London); **computational inequality** (only tech giants can afford 100B+ parameter models).
+
+### 6.2 Societal Risks
+- **Bias**: Early LLMs mirrored training data biases (e.g., BERT associated "doctor" with "male" in 2019 audits). Responses included bias mitigation datasets (e.g., WinoBias) and audits (e.g., Stanford’s Gender Shades).
+- **Misinformation**: GPT-2’s realistic text generation prompted calls for regulation; by 2023, deepfakes (e.g., AI-generated political speeches) became a policy focus.
+- **Regulation**: The EU AI Act (2024) classified LLMs as "high-risk," requiring transparency (e.g., disclosing AI-generated content) and safety testing.
+
+
+## 7. Conclusion: A Revolution in Five Years
+The history of LLMs is a story of exponential progress: from the transformer’s 2017 invention to ChatGPT’s 2022 viral explosion, a mere five years. What began as an academic breakthrough—parallelizing text processing with self-attention—evolved into a technology that writes code, tutors students, and shapes global policy.
+
+Yet challenges persist: scaling has outpaced our understanding of how LLMs "think," and debates over bias, energy use, and access (closed vs. open models) intensify. As we look to the future, this history reminds us that LLMs are not just technical achievements, but mirrors of society—reflecting both our ingenuity and our flaws. Their next chapter will depend on balancing innovation with responsibility, ensuring these models serve as tools for collective progress.
+
+
+## References
+- Devlin, J., et al. (2018). *BERT: Pre-training of deep bidirectional transformers for language understanding*. NAACL.
+- Hochreiter, S., & Schmidhuber, J. (1997). *Long short-term memory*. Neural Computation.
+- Mikolov, T., et al. (2013). *Efficient estimation of word representations in vector space*. ICLR.
+- Radford, A., et al. (2018). *Improving language understanding by generative pre-training*. OpenAI.
+- Vaswani, A., et al. (2017). *Attention is all you need*. NeurIPS.
+- Weidinger, L., et al. (2021). *On the dangers of stochastic parrots: Can language models be too big?*. ACM FAccT.
+===========
+Agent[WriterAgent]:
+
+===========
+Agent[WriterAgent]:
+successfully transferred to agent [ReportSupervisor]
+===========
+```
+
+## WithDeterministicTransferTo
+
+### 什么是 WithDeterministicTransferTo?
+
+`WithDeterministicTransferTo` 是 Eino ADK 提供的 Agent 增强工具,用于为 Agent 注入任务转让(Transfer)能力 。它允许开发者为目标 Agent 预设固定的任务转让路径,当该 Agent 完成任务(未被中断)时,会自动生成 Transfer 事件,将任务流转到预设的目标 Agent。
+
+这一能力是构建 Supervisor Agent 协作模式的基础,确保子 Agent 在执行完毕后能可靠地将任务控制权交回监督者(Supervisor),形成“分配-执行-反馈”的闭环协作流程。
+
+### WithDeterministicTransferTo 核心实现
+
+#### 配置结构
+
+通过 `DeterministicTransferConfig` 定义任务转让的核心参数:
+
+```go
+// 包装方法
+func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent
+
+// 配置详情
+type DeterministicTransferConfig struct {
+ Agent Agent // 被增强的目标 Agent
+ ToAgentNames []string // 任务完成后转让的目标 Agent 名称列表
+}
+```
+
+- `Agent`:需要添加转让能力的原始 Agent。
+- `ToAgentNames`:当 `Agent` 完成任务且未中断时,自动转让任务的目标 Agent 名称列表(按顺序转让)。
+
+#### Agent 包装
+
+WithDeterministicTransferTo 会对原始 Agent 进行包装,根据其是否实现 `ResumableAgent` 接口(支持中断与恢复),分别返回 `agentWithDeterministicTransferTo` 或 `resumableAgentWithDeterministicTransferTo` 实例,确保增强能力与 Agent 原有功能(如 `Resume` 方法)兼容。
+
+包装后的 Agent 会覆盖 `Run` 方法(对 `ResumableAgent` 还会覆盖 `Resume` 方法),在原始 Agent 的事件流基础上追加 Transfer 事件:
+
+```go
+// 对普通 Agent 的包装
+type agentWithDeterministicTransferTo struct {
+ agent Agent // 原始 Agent
+ toAgentNames []string // 目标 Agent 名称列表
+}
+
+// Run 方法:执行原始 Agent 任务,并在任务完成后追加 Transfer 事件
+func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] {
+ aIter := a.agent.Run(ctx, input, options...)
+
+ iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
+
+ // 异步处理原始事件流,并追加 Transfer 事件
+ go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
+
+ return iterator
+}
+```
+
+对于 `ResumableAgent`,额外实现 `Resume` 方法,确保恢复执行后仍能触发确定性转让:
+
+```go
+type resumableAgentWithDeterministicTransferTo struct {
+ agent ResumableAgent // 支持恢复的原始 Agent
+ toAgentNames []string // 目标 Agent 名称列表
+}
+
+// Resume 方法:恢复执行原始 Agent 任务,并在完成后追加 Transfer 事件
+func (a *resumableAgentWithDeterministicTransferTo) Resume(ctx context.Context, info *ResumeInfo, opts ...AgentRunOption) *AsyncIterator[*AgentEvent] {
+ aIter := a.agent.Resume(ctx, info, opts...)
+ iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
+ go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
+ return iterator
+}
+```
+
+#### 事件流追加 Transfer 事件
+
+`appendTransferAction` 是实现确定性转让的核心逻辑,它会消费原始 Agent 的事件流,在 Agent 任务正常结束(未中断)后,自动生成并发送 Transfer 事件到目标 Agent:
+
+```go
+func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent], generator *AsyncGenerator[*AgentEvent], toAgentNames []string) {
+ defer func() {
+ // 异常处理:捕获 panic 并通过事件传递错误
+ if panicErr := recover(); panicErr != nil {
+ generator.Send(&AgentEvent{Err: safe.NewPanicErr(panicErr, debug.Stack())})
+ }
+ generator.Close() // 事件流结束,关闭生成器
+ }()
+
+ interrupted := false
+
+ // 1. 转发原始 Agent 的所有事件
+ for {
+ event, ok := aIter.Next()
+ if !ok { // 原始事件流结束
+ break
+ }
+ generator.Send(event) // 转发事件给调用方
+
+ // 检查是否发生中断(如 InterruptAction)
+ if event.Action != nil && event.Action.Interrupted != nil {
+ interrupted = true
+ } else {
+ interrupted = false
+ }
+ }
+
+ // 2. 若未中断且存在目标 Agent,生成 Transfer 事件
+ if !interrupted && len(toAgentNames) > 0 {
+ for _, toAgentName := range toAgentNames {
+ // 生成转让消息(系统提示 + Transfer 动作)
+ aMsg, tMsg := GenTransferMessages(ctx, toAgentName)
+ // 发送系统提示事件(告知用户任务转让)
+ aEvent := EventFromMessage(aMsg, nil, schema.Assistant, "")
+ generator.Send(aEvent)
+ // 发送 Transfer 动作事件(触发任务转让)
+ tEvent := EventFromMessage(tMsg, nil, schema.Tool, tMsg.ToolName)
+ tEvent.Action = &AgentAction{
+ TransferToAgent: &TransferToAgentAction{
+ DestAgentName: toAgentName, // 目标 Agent 名称
+ },
+ }
+ generator.Send(tEvent)
+ }
+ }
+}
+```
+
+**关键逻辑**:
+
+- **事件转发**:原始 Agent 产生的所有事件(如思考、工具调用、输出结果)会被完整转发,确保业务逻辑不受影响。
+- **中断检查**:若 Agent 执行过程中被中断(如 `InterruptAction`),则不触发 Transfer(中断视为任务未正常完成)。
+- **Transfer 事件生成**:任务正常结束后,为每个 `ToAgentNames` 生成两条事件:
+ 1. 系统提示事件(`schema.Assistant` 角色):告知用户任务将转让给目标 Agent。
+ 2. Transfer 动作事件(`schema.Tool` 角色):携带 `TransferToAgentAction`,触发 ADK 运行时将任务转让给 `DestAgentName` 对应的 Agent。
+
+## 总结
+
+WithDeterministicTransferTo 为 Agent 提供了可靠的任务转让能力,是构建 Supervisor 模式的核心基石;而 Supervisor 模式通过中心化协调与确定性回调,实现了多 Agent 之间的高效协作,显著降低了复杂任务的开发与维护成本。结合两者,开发者可快速搭建灵活、可扩展的多 Agent 系统。
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_implementation/workflow.md b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/workflow.md
new file mode 100644
index 0000000..f03c7c3
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_implementation/workflow.md
@@ -0,0 +1,1265 @@
+---
+Description: ""
+date: "2026-03-09"
+lastmod: ""
+tags: []
+title: Workflow Agents
+weight: 2
+---
+
+# Workflow Agents 概述
+
+## 导入路径
+
+`import ``github.com/cloudwego/eino/adk`
+
+## 什么是 Workflow Agents
+
+Workflow Agents 是 eino ADK 中的一种特殊 Agent 类型,它允许开发者以预设的流程来组织和执行多个子 Agent。
+
+与基于 LLM 自主决策的 Transfer 模式不同,Workflow Agents 采用**预设决策**的方式,按照代码中定义好的执行流程来运行子 Agent,提供了更可预测和可控的多 Agent 协作方式。
+
+Eino ADK 提供了三种基础的 Workflow Agent 类型:
+
+- **SequentialAgent**:按顺序依次执行子 Agent
+- **LoopAgent**:循环执行子 Agent 序列
+- **ParallelAgent**:并发执行多个子 Agent
+
+这些 Workflow Agent 可以相互嵌套,构建更复杂的执行流程,满足各种业务场景需求。
+
+# SequentialAgent
+
+## 功能
+
+SequentialAgent 是最基础的 Workflow Agent,它按照配置中提供的顺序,依次执行一系列子 Agent。每个子 Agent 执行完成后,其输出会通过 History 机制传递给下一个子 Agent,形成一个线性的执行链。
+
+
+
+```go
+type SequentialAgentConfig struct {
+ Name string // Agent 名称
+ Description string // Agent 描述
+ SubAgents []Agent // 子 Agent 列表,按执行顺序排列
+}
+
+func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
+```
+
+SequentialAgent 的执行遵循以下设定:
+
+1. **线性执行**:严格按照 SubAgents 数组的顺序执行
+2. **History 传递**:每个 Agent 的执行结果都会被添加到 History 中,后续 Agent 可以访问前面 Agent 的执行历史
+3. **提前退出**:如果任何一个子 Agent 产生 ExitAction / Interrupt,整个 Sequential 流程会立即终止
+
+SequentialAgent 适用于以下场景:
+
+- **多步骤处理流程**:如数据预处理 -> 分析 -> 生成报告
+- **管道式处理**:每个步骤的输出作为下个步骤的输入
+- **有依赖关系的任务序列**:后续任务依赖前面任务的结果
+
+## 示例
+
+示例展示了如何使用 SequentialAgent 创建一个三步骤的文档处理流水线:
+
+1. **DocumentAnalyzer**:分析文档内容
+2. **ContentSummarizer**:总结分析结果
+3. **ReportGenerator**:生成最终报告
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+)
+
+// 创建 ChatModel 实例
+func newChatModel() model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return cm
+}
+
+// 文档分析 Agent
+func NewDocumentAnalyzerAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "DocumentAnalyzer",
+ Description: "分析文档内容并提取关键信息",
+ Instruction: "你是一个文档分析专家。请仔细分析用户提供的文档内容,提取其中的关键信息、主要观点和重要数据。",
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 内容总结 Agent
+func NewContentSummarizerAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ContentSummarizer",
+ Description: "对分析结果进行总结",
+ Instruction: "基于前面的文档分析结果,生成一个简洁明了的总结,突出最重要的发现和结论。",
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 报告生成 Agent
+func NewReportGeneratorAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ReportGenerator",
+ Description: "生成最终的分析报告",
+ Instruction: "基于前面的分析和总结,生成一份结构化的分析报告,包含执行摘要、详细分析和建议。",
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+func main() {
+ ctx := context.Background()
+
+ // 创建三个处理步骤的 Agent
+ analyzer := NewDocumentAnalyzerAgent()
+ summarizer := NewContentSummarizerAgent()
+ generator := NewReportGeneratorAgent()
+
+ // 创建 SequentialAgent
+ sequentialAgent, err := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ Name: "DocumentProcessingPipeline",
+ Description: "文档处理流水线:分析 → 总结 → 报告生成",
+ SubAgents: []adk.Agent{analyzer, summarizer, generator},
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // 创建 Runner
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: sequentialAgent,
+ })
+
+ // 执行文档处理流程
+ input := "请分析以下市场报告:2024年第三季度,公司营收增长15%,主要得益于新产品线的成功推出。但运营成本也上升了8%,需要优化效率。"
+
+ fmt.Println("开始执行文档处理流水线...")
+ iter := runner.Query(ctx, input)
+
+ stepCount := 1
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+
+ if event.Output != nil && event.Output.MessageOutput != nil {
+ fmt.Printf("\n=== 步骤 %d: %s ===\n", stepCount, event.AgentName)
+ fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
+ stepCount++
+ }
+ }
+
+ fmt.Println("\n文档处理流水线执行完成!")
+}
+```
+
+运行结果为:
+
+```markdown
+开始执行文档处理流水线...
+
+=== 步骤 1: DocumentAnalyzer ===
+市场报告关键信息分析:
+
+1. 营收增长情况:
+ - 2024年第三季度,公司营收同比增长15%。
+ - 营收增长的主要驱动力是新产品线的成功推出。
+
+2. 成本情况:
+ - 运营成本上涨了8%。
+ - 成本上升提醒公司需要进行效率优化。
+
+主要观点总结:
+- 新产品线推出显著推动了营收增长,显示公司在产品创新方面取得良好成果。
+- 虽然营收提升,但运营成本的增加在一定程度上影响了盈利能力,指出了提升运营效率的重要性。
+
+重要数据:
+- 营收增长率:15%
+- 运营成本增长率:8%
+
+=== 步骤 2: ContentSummarizer ===
+总结:2024年第三季度,公司实现了15%的营收增长,主要归功于新产品线的成功推出,体现了公司产品创新能力的显著提升。然而,运营成本同时上涨了8%,对盈利能力构成一定压力,强调了优化运营效率的迫切需求。整体来看,公司在增长与成本控制之间需寻求更好的平衡以保障持续健康发展。
+
+=== 步骤 3: ReportGenerator ===
+分析报告
+
+一、执行摘要
+2024年第三季度,公司实现营收同比增长15%,主要得益于新产品线的成功推出,展现了强劲的产品创新能力。然而,运营成本也同比提升了8%,对利润空间形成一定压力。为确保持续的盈利增长,需重点关注运营效率的优化,推动成本控制与收入增长的平衡发展。
+
+二、详细分析
+1. 营收增长分析
+- 公司营收增长15%,反映出新产品线市场接受度良好,有效拓展了收入来源。
+- 新产品线的推出体现了公司研发及市场响应能力的提升,为未来持续增长奠定基础。
+
+2. 运营成本情况
+- 运营成本上升8%,可能来自原材料价格上涨、生产效率下降或销售推广费用增加等多个方面。
+- 该成本提升在一定程度上抵消了收入增长带来的利润增益,影响整体盈利能力。
+
+3. 盈利能力及效率考量
+- 营收与成本增长的不匹配显示出当前运营效率存在改进空间。
+- 优化供应链管理、提升生产自动化及加强成本控制将成为关键措施。
+
+三、建议
+1. 加强新产品线后续支持,包括市场推广和客户反馈机制,持续推动营收增长。
+2. 深入分析运营成本构成,识别主要成本驱动因素,制定针对性降低成本的策略。
+3. 推动内部流程优化与技术升级,提升生产及运营效率,缓解成本压力。
+4. 建立动态的财务监控体系,实现对营收与成本的实时跟踪与调整,确保公司财务健康。
+
+四、结论
+公司在2024年第三季度展现出了良好的增长动力,但同时面临成本上升带来的挑战。通过持续的产品创新结合有效的成本管理,未来有望实现盈利能力和市场竞争力的双重提升,推动公司稳健发展。
+
+文档处理流水线执行完成!
+```
+
+# LoopAgent
+
+## 功能
+
+LoopAgent 基于 SequentialAgent 实现,它会重复执行配置的子 Agent 序列,直到达到最大迭代次数或某个子 Agent 产生 ExitAction。LoopAgent 特别适用于需要迭代优化、反复处理或持续监控的场景。
+
+
+
+```go
+type LoopAgentConfig struct {
+ Name string // Agent 名称
+ Description string // Agent 描述
+ SubAgents []Agent // 子 Agent 列表
+ MaxIterations int // 最大迭代次数,0 表示无限循环
+}
+
+func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
+```
+
+LoopAgent 的执行遵循以下设定:
+
+1. **循环执行**:重复执行 SubAgents 序列,每次循环都是一个完整的 Sequential 执行过程
+2. **History 累积**:每次迭代的结果都会累积到 History 中,后续迭代可以访问所有历史信息
+3. **条件退出**:支持通过 ExitAction 或达到最大迭代次数来终止循环,配置 `MaxIterations=0` 时表示无限循环
+
+LoopAgent 适用于以下场景:
+
+- **迭代优化**:如代码优化、参数调优等需要反复改进的任务
+- **持续监控**:定期检查状态并执行相应操作
+- **反复处理**:需要多轮处理才能达到满意结果的任务
+- **自我改进**:Agent 根据前面的执行结果不断改进自己的输出
+
+## 示例
+
+示例展示了如何使用 LoopAgent 创建一个代码优化循环:
+
+1. **CodeAnalyzer**:分析代码问题
+2. **CodeOptimizer**:根据分析结果优化代码
+3. **ExitController**:判断是否需要退出循环
+
+循环会持续执行直到代码质量达到标准或达到最大迭代次数。
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+)
+
+func newChatModel() model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return cm
+}
+
+// 代码分析 Agent
+func NewCodeAnalyzerAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "CodeAnalyzer",
+ Description: "分析代码质量和性能问题",
+ Instruction: `你是一个代码分析专家。请分析提供的代码,识别以下问题:
+1. 性能瓶颈
+2. 代码重复
+3. 可读性问题
+4. 潜在的 bug
+5. 不符合最佳实践的地方
+
+如果代码已经足够优秀,请输出 "EXIT: 代码质量已达到标准" 来结束优化流程。`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 代码优化 Agent
+func NewCodeOptimizerAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "CodeOptimizer",
+ Description: "根据分析结果优化代码",
+ Instruction: `基于前面的代码分析结果,对代码进行优化改进:
+1. 修复识别出的性能问题
+2. 消除代码重复
+3. 提高代码可读性
+4. 修复潜在 bug
+5. 应用最佳实践
+
+请提供优化后的完整代码。`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 创建一个特殊的 Agent 来处理退出逻辑
+func NewExitControllerAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "ExitController",
+ Description: "控制优化循环的退出",
+ Instruction: `检查前面的分析结果,如果代码分析师认为代码质量已达到标准(包含"EXIT"关键词),
+则输出 "TERMINATE" 并生成退出动作来结束循环。否则继续下一轮优化。`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+func main() {
+ ctx := context.Background()
+
+ // 创建优化流程的 Agent
+ analyzer := NewCodeAnalyzerAgent()
+ optimizer := NewCodeOptimizerAgent()
+ controller := NewExitControllerAgent()
+
+ // 创建 LoopAgent,最多执行 5 轮优化
+ loopAgent, err := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
+ Name: "CodeOptimizationLoop",
+ Description: "代码优化循环:分析 → 优化 → 检查退出条件",
+ SubAgents: []adk.Agent{analyzer, optimizer, controller},
+ MaxIterations: 5, // 最多 5 轮优化
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // 创建 Runner
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: loopAgent,
+ })
+
+ // 待优化的代码示例
+ codeToOptimize := `
+func processData(data []int) []int {
+ result := []int{}
+ for i := 0; i < len(data); i++ {
+ for j := 0; j < len(data); j++ {
+ if data[i] > data[j] {
+ result = append(result, data[i])
+ break
+ }
+ }
+ }
+ return result
+}
+`
+
+ fmt.Println("开始代码优化循环...")
+ iter := runner.Query(ctx, "请优化以下 Go 代码:\n"+codeToOptimize)
+
+ iteration := 1
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+
+ if event.Output != nil && event.Output.MessageOutput != nil {
+ fmt.Printf("\n=== 第 %d 轮 - %s ===\n", iteration, event.AgentName)
+ fmt.Printf("%s\n", event.Output.MessageOutput.Message.Content)
+
+ // 检查是否需要退出
+ if event.AgentName == "ExitController" {
+ if event.Action != nil && event.Action.Exit {
+ fmt.Println("\n优化循环提前结束!")
+ break
+ }
+ iteration++
+ }
+ }
+ }
+
+ fmt.Println("\n代码优化循环执行完成!")
+}
+```
+
+运行结果为:
+
+```java
+开始代码优化循环...
+
+=== 第 1 轮 - CodeAnalyzer ===
+分析提供的代码:
+
+```go
+func processData(data []int) []int {
+ result := []int{}
+ for i := 0; i < len(data); i++ {
+ for j := 0; j < len(data); j++ {
+ if data[i] > data[j] {
+ result = append(result, data[i])
+ break
+ }
+ }
+ }
+ return result
+}
+```
+
+### 1. 性能瓶颈
+
+- 双层循环,时间复杂度为 O(n²),对于较大的数据量,性能不佳。
+- 内层循环当条件满足时立即 break,减少了部分不必要的比较,但整体仍然是二次复杂度。
+
+### 2. 代码重复
+
+- 代码没有明显重复,但逻辑可以简化。
+
+### 3. 可读性问题
+
+- 代码的意图不太明确。根据代码逻辑,函数筛选出那些在数组里至少有一个元素比它小的元素。换句话说,剔除了数组里所有等于或最小的数字。
+- 可以通过注释或重命名来增加可读性。
+
+### 4. 潜在的 bug
+
+- 目前没有明显的 bug,但输入为空数组时,返回空数组符合预期。
+
+### 5. 不符合最佳实践的地方
+
+- 过程较为低效,未使用 Go 语言的特性优化性能。
+- 缺少注释或文档说明。
+
+---
+
+## 优化建议
+
+如果目的确实是要返回所有比数组中某些元素大的元素(排除数组中的最小元素),可以改为:
+
+- 找出数组中的最小元素 minVal。
+- 遍历数组,将大于 minVal 的元素加入结果。
+- 这样时间复杂度降为 O(n)。
+
+优化代码:
+
+```go
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+### 优化点说明
+
+- 使用一次遍历确定最小值,O(n)。
+- 再次遍历构造结果,O(n)。
+- 适当使用切片容量初始化,减少内存分配。
+- 代码意图明确且性能提升明显。
+
+---
+
+如果你期望的是其他逻辑,请告知我进一步调整。
+
+=== 第 1 轮 - CodeOptimizer ===
+
+```go
+// processData 返回输入切片中所有大于最小元素的值。
+// 如果输入为空,则返回 nil。
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ // 找出切片中的最小值
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ // 收集所有大于最小值的元素
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+=== 第 1 轮 - ExitController ===
+TERMINATE
+
+=== 第 2 轮 - CodeAnalyzer ===
+EXIT: 代码质量已达到标准
+
+=== 第 2 轮 - CodeOptimizer ===
+
+```go
+// processData 返回输入切片中所有大于最小元素的值。
+// 如果输入为空,则返回 nil。
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ // 找出切片中的最小值
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ // 收集所有大于最小值的元素
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+=== 第 2 轮 - ExitController ===
+TERMINATE
+
+=== 第 3 轮 - CodeAnalyzer ===
+EXIT: 代码质量已达到标准
+
+=== 第 3 轮 - CodeOptimizer ===
+
+```go
+// processData 返回输入切片中所有大于最小元素的值。
+// 如果输入为空,则返回 nil。
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+=== 第 3 轮 - ExitController ===
+TERMINATE
+
+=== 第 4 轮 - CodeAnalyzer ===
+EXIT: 代码质量已达到标准
+
+=== 第 4 轮 - CodeOptimizer ===
+
+```go
+// processData 返回切片中所有大于最小元素的元素。
+// 如果输入为空,则返回 nil。
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+=== 第 4 轮 - ExitController ===
+TERMINATE
+
+=== 第 5 轮 - CodeAnalyzer ===
+EXIT: 代码质量已达到标准
+
+=== 第 5 轮 - CodeOptimizer ===
+
+```go
+// processData 返回输入切片中所有大于最小元素的值。
+// 如果输入为空,则返回 nil。
+func processData(data []int) []int {
+ if len(data) == 0 {
+ return nil
+ }
+
+ minVal := data[0]
+ for _, v := range data[1:] {
+ if v < minVal {
+ minVal = v
+ }
+ }
+
+ result := make([]int, 0, len(data))
+ for _, v := range data {
+ if v > minVal {
+ result = append(result, v)
+ }
+ }
+
+ return result
+}
+```
+
+=== 第 5 轮 - ExitController ===
+TERMINATE
+
+代码优化循环执行完成!
+
+```
+
+
+
+
+## BreakLoop
+
+
+在 Loop Agent 中,当某个 Agent 需要中断循环运行时,您可以使用 ADK 提供的对应 Break Action。
+
+```go
+// BreakLoopAction is a programmatic-only agent action used to prematurely
+// terminate the execution of a loop workflow agent.
+// When a loop workflow agent receives this action from a sub-agent, it will stop its
+// current iteration and will not proceed to the next one.
+// It will mark the BreakLoopAction as Done, signalling to any 'upper level' loop agent
+// that this action has been processed and should be ignored further up.
+// This action is not intended to be used by LLMs.
+type BreakLoopAction struct {
+ // From records the name of the agent that initiated the break loop action.
+ From string
+ // Done is a state flag that can be used by the framework to mark when the
+ // action has been handled.
+ Done bool
+ // CurrentIterations is populated by the framework to record at which
+ // iteration the loop was broken.
+ CurrentIterations int
+}
+
+// NewBreakLoopAction creates a new BreakLoopAction, signaling a request
+// to terminate the current loop.
+func NewBreakLoopAction(agentName string) *AgentAction {
+ return &AgentAction{BreakLoop: &BreakLoopAction{
+ From: agentName,
+ }}
+}
+```
+
+Break Action 在达到中断目的的同时不影响 Loop Agent 外的其他 Agent 运行,而 Exit Action 会立刻中断所有后续的 Agent 运行。
+
+以下图为例:
+
+
+
+- 当 Agent1 发出 BreakAction 时,Loop Agent 将中断,Sequential 继续运行 Agent3
+- 当 Agent1 发出 ExitAction 时,Sequential 运行流程整体终止,Agent2 / Agent3 均不会运行
+
+# ParallelAgent
+
+## 功能
+
+ParallelAgent 允许多个子 Agent 基于相同的输入上下文并发执行,所有子 Agent 同时开始执行,并等待全部完成后结束。这种模式特别适用于可以独立并行处理的任务,能够显著提高执行效率。
+
+
+
+```go
+type ParallelAgentConfig struct {
+ Name string // Agent 名称
+ Description string // Agent 描述
+ SubAgents []Agent // 并发执行的子 Agent 列表
+}
+
+func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
+```
+
+ParallelAgent 的执行遵循以下设定:
+
+1. **并发执行**:所有子 Agent 同时启动,在独立的 goroutine 中并行执行
+2. **共享输入**:所有子 Agent 接收相同的初始输入和上下文
+3. **等待与结果聚合**:内部使用 sync.WaitGroup 等待所有子 Agent 执行完成,收集所有子 Agent 的执行结果并按接收顺序输出
+
+另外 Parallel 内部默认包含异常处理机制:
+
+- **Panic 恢复**:每个 goroutine 都有独立的 panic 恢复机制
+- **错误隔离**:单个子 Agent 的错误不会影响其他子 Agent 的执行
+- **中断处理**:支持子 Agent 的中断和恢复机制
+
+ParallelAgent 适用于以下场景:
+
+- **独立任务并行处理**:多个不相关的任务可以同时执行
+- **多角度分析**:从不同角度同时分析同一个问题
+- **性能优化**:通过并行执行减少总体执行时间
+- **多专家咨询**:同时咨询多个专业领域的 Agent
+
+## 示例
+
+示例展示了如何使用 ParallelAgent 同时从四个不同角度分析产品方案:
+
+1. **TechnicalAnalyst**:技术可行性分析
+2. **BusinessAnalyst**:商业价值分析
+3. **UXAnalyst**:用户体验分析
+4. **SecurityAnalyst**:安全风险分析
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "sync"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/model"
+)
+
+func newChatModel() model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return cm
+}
+
+// 技术分析 Agent
+func NewTechnicalAnalystAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "TechnicalAnalyst",
+ Description: "从技术角度分析内容",
+ Instruction: `你是一个技术专家。请从技术实现、架构设计、性能优化等技术角度分析提供的内容。
+重点关注:
+1. 技术可行性
+2. 架构合理性
+3. 性能考量
+4. 技术风险
+5. 实现复杂度`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 商业分析 Agent
+func NewBusinessAnalystAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "BusinessAnalyst",
+ Description: "从商业角度分析内容",
+ Instruction: `你是一个商业分析专家。请从商业价值、市场前景、成本效益等商业角度分析提供的内容。
+重点关注:
+1. 商业价值
+2. 市场需求
+3. 竞争优势
+4. 成本分析
+5. 盈利模式`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 用户体验分析 Agent
+func NewUXAnalystAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "UXAnalyst",
+ Description: "从用户体验角度分析内容",
+ Instruction: `你是一个用户体验专家。请从用户体验、易用性、用户满意度等角度分析提供的内容。
+重点关注:
+1. 用户友好性
+2. 操作便利性
+3. 学习成本
+4. 用户满意度
+5. 可访问性`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+// 安全分析 Agent
+func NewSecurityAnalystAgent() adk.Agent {
+ a, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
+ Name: "SecurityAnalyst",
+ Description: "从安全角度分析内容",
+ Instruction: `你是一个安全专家。请从信息安全、数据保护、隐私合规等安全角度分析提供的内容。
+重点关注:
+1. 数据安全
+2. 隐私保护
+3. 访问控制
+4. 安全漏洞
+5. 合规要求`,
+ Model: newChatModel(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ return a
+}
+
+func main() {
+ ctx := context.Background()
+
+ // 创建四个不同角度的分析 Agent
+ techAnalyst := NewTechnicalAnalystAgent()
+ bizAnalyst := NewBusinessAnalystAgent()
+ uxAnalyst := NewUXAnalystAgent()
+ secAnalyst := NewSecurityAnalystAgent()
+
+ // 创建 ParallelAgent,同时进行多角度分析
+ parallelAgent, err := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
+ Name: "MultiPerspectiveAnalyzer",
+ Description: "多角度并行分析:技术 + 商业 + 用户体验 + 安全",
+ SubAgents: []adk.Agent{techAnalyst, bizAnalyst, uxAnalyst, secAnalyst},
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // 创建 Runner
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: parallelAgent,
+ })
+
+ // 要分析的产品方案
+ productProposal := `
+产品方案:智能客服系统
+
+概述:开发一个基于大语言模型的智能客服系统,能够自动回答用户问题,处理常见业务咨询,并在必要时转接人工客服。
+
+主要功能:
+1. 自然语言理解和回复
+2. 多轮对话管理
+3. 知识库集成
+4. 情感分析
+5. 人工客服转接
+6. 对话历史记录
+7. 多渠道接入(网页、微信、APP)
+
+技术架构:
+- 前端:React + TypeScript
+- 后端:Go + Gin 框架
+- 数据库:PostgreSQL + Redis
+- AI模型:GPT-4 API
+- 部署:Docker + Kubernetes
+`
+
+ fmt.Println("开始多角度并行分析...")
+ iter := runner.Query(ctx, "请分析以下产品方案:\n"+productProposal)
+
+ // 使用 map 来收集不同分析师的结果
+ results := make(map[string]string)
+ var mu sync.Mutex
+
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Printf("分析过程中出现错误: %v", event.Err)
+ continue
+ }
+
+ if event.Output != nil && event.Output.MessageOutput != nil {
+ mu.Lock()
+ results[event.AgentName] = event.Output.MessageOutput.Message.Content
+ mu.Unlock()
+
+ fmt.Printf("\n=== %s 分析完成 ===\n", event.AgentName)
+ }
+ }
+
+ // 输出所有分析结果
+ fmt.Println("\n" + "============================================================")
+ fmt.Println("多角度分析结果汇总")
+ fmt.Println("============================================================")
+
+ analysisOrder := []string{"TechnicalAnalyst", "BusinessAnalyst", "UXAnalyst", "SecurityAnalyst"}
+ analysisNames := map[string]string{
+ "TechnicalAnalyst": "技术分析",
+ "BusinessAnalyst": "商业分析",
+ "UXAnalyst": "用户体验分析",
+ "SecurityAnalyst": "安全分析",
+ }
+
+ for _, agentName := range analysisOrder {
+ if result, exists := results[agentName]; exists {
+ fmt.Printf("\n【%s】\n", analysisNames[agentName])
+ fmt.Printf("%s\n", result)
+ fmt.Println("----------------------------------------")
+ }
+ }
+
+ fmt.Println("\n多角度并行分析完成!")
+ fmt.Printf("共收到 %d 个分析结果\n", len(results))
+}
+```
+
+运行结果为:
+
+```markdown
+开始多角度并行分析...
+
+=== BusinessAnalyst 分析完成 ===
+
+=== UXAnalyst 分析完成 ===
+
+=== SecurityAnalyst 分析完成 ===
+
+=== TechnicalAnalyst 分析完成 ===
+
+============================================================
+多角度分析结果汇总
+============================================================
+
+【技术分析】
+针对该智能客服系统方案,下面从技术实现、架构设计及性能优化等角度进行详细分析:
+
+---
+
+### 一、技术可行性
+
+1. **自然语言理解和回复**
+ - 利用 GPT-4 API 实现自然语言理解和自动回复是当前成熟且可行的方案。GPT-4具备强大的语言理解和生成能力,适合处理复杂、多样的问题。
+
+2. **多轮对话管理**
+ - 依赖后端维护上下文状态,结合GPT-4模型能够较好处理多轮交互。需要设计合理的上下文管理机制(例如对话历史维护、关键槽位抽取等),确保上下文信息完整性。
+
+3. **知识库集成**
+ - 可通过向GPT-4 API添加特定的知识库检索结果(检索增强生成),或者通过本地检索接口集成知识库。技术上可行,但对于实时性和准确性有较高要求。
+
+4. **情感分析**
+ - 情感分析功能可以用独立的轻量模型实现(例如基于BERT微调),也可尝试利用GPT-4输出,但成本较高。情感分析能力帮助智能客服更好地理解用户情绪,提升用户体验。
+
+5. **人工客服转接**
+ - 技术上通过建立事件触发规则(如轮次数、情绪阈值、关键词检测)实现自动转人工。系统需支持工单或会话传递机制,并保障会话无缝切换。
+
+6. **多渠道接入**
+ - 网页、微信、App等多渠道接入均可通过统一API网关实现,技术成熟,同时需要处理渠道差异性(消息格式、认证、推送机制等)。
+
+---
+
+### 二、架构合理性
+
+- **前端 React + TypeScript**
+ 非常适合搭建响应式客服界面,生态成熟,方便多渠道共享组件。
+
+- **后端 Go + Gin**
+ Go语言性能优异,Gin框架轻量且性能高,适合高并发场景。后端承担对接 GPT-4 API、管理状态、多渠道消息转发等职责,选择合理。
+
+- **数据库 PostgreSQL + Redis**
+ - PostgreSQL 负责存储结构化数据,如用户信息、对话历史、知识库元数据。
+ - Redis 负责缓存会话状态、热点知识库、限流等,提升访问性能。
+ 架构设计符合常见大型互联网产品模式,组件分工明确。
+
+- **AI模型 GPT-4 API**
+ 使用成熟API降低开发难度和模型维护成本;缺点是对网络和API调用依赖度高。
+
+- **部署 Docker + Kubernetes**
+ 容器化和K8s编排能保证系统弹性伸缩、高可用和灰度发布,适合生产环境,符合现代微服务架构趋势。
+
+---
+
+### 三、性能考量
+
+1. **响应时间**
+ - GPT-4 API调用本身有一定延迟(通常几百毫秒到1秒不等),对响应时间影响较大。需要做好接口异步处理与前端体验设计(如加载动画、部分渐进响应)。
+
+2. **并发处理能力**
+ - 后端Go具有高并发处理优势,配合Redis缓存热点数据,能大幅提升整体吞吐能力。
+ - 但GPT-4 API调用受限于OpenAI服务的QPS限制与调用成本,需合理设计调用频率与降级策略。
+
+3. **缓存策略**
+ - 对用户对话上下文和常见问题答案进行缓存,减少重复API调用。
+ - 如关键问题先做本地匹配,失败后才调用GPT-4,提升效率。
+
+4. **多渠道负载均衡**
+ - 需要设计统一消息总线和可靠的异步队列,防止某渠道流量突增影响整体系统稳定。
+
+---
+
+### 四、技术风险
+
+1. **GPT-4 API依赖**
+ - 高度依赖第三方API,风险包括服务中断、接口变更及成本波动。
+ - 建议设计本地缓存和有限的替代回答逻辑以应对API异常。
+
+2. **多轮对话上下文管理难度**
+ - 上下文过长或复杂会导致回答质量降低,需要设计限制上下文长度、选择性保留重要信息机制。
+
+3. **知识库集成复杂度**
+ - 如何做到知识库与
+----------------------------------------
+
+【商业分析】
+以下是对智能客服系统产品方案的商业角度分析:
+
+1. 商业价值
+- 提升客户服务效率:自动解答用户问题和常见咨询,减少人工客服压力,降低用人成本。
+- 提升用户体验:多轮对话和情感分析使交互更自然,增强客户满意度和粘性。
+- 数据驱动决策支持:对话历史与知识库集成为企业提供宝贵的用户反馈和行为数据,优化产品和服务。
+- 支持业务扩展:多渠道接入(网页、微信、APP)满足不同客户接入习惯,提升覆盖率。
+
+2. 市场需求
+- 市场对智能客服的需求持续增长,特别是在电商、金融、医疗、教育等行业,客户服务自动化是企业数字化转型的重要方向。
+- 随着AI技术的成熟,企业期望借助大语言模型提升客服智能化水平。
+- 用户对即时响应、全天候服务的需求增加,推动智能客服系统的广泛采用。
+
+3. 竞争优势
+- 采用先进的GPT-4大语言模型,拥有较强的自然语言理解与生成能力,提升问答准确率和对话自然度。
+- 情感分析功能有助于精准识别用户情绪,动态调整回复策略,提高客户满意度。
+- 多渠道接入设计满足企业多元化客户触达需求,增强产品适用性。
+- 技术架构采用微服务、容器化部署,便于弹性扩展和维护,提升系统稳定性和扩展能力。
+
+4. 成本分析
+- AI模型调用成本较高,依赖GPT-4 API,需根据调用量和响应速度调整预算。
+- 技术研发投入较大,涉及前后端、多渠道融合、AI和知识库管理。
+- 运维和服务器成本需考虑多渠道并发访问。
+- 长期来看,人工客服人数可显著减少,节省人力成本。
+- 可通过云服务降低硬件初期投入,但云资源使用需精细管理以控制费用。
+
+5. 盈利模式
+- SaaS订阅服务:按月/年向企业客户收取服务费,基于接入渠道数、并发量和功能级别分层定价。
+- 按调用次数或对话数收费,适合业务波动较大的客户。
+- 增值服务:数据分析报告定制、行业知识库集成、人工客服协同工具等收费。
+- 中大型客户可提供定制开发和技术支持,收取项目费用。
+- 通过持续优化模型和服务,增加客户留存和续费率。
+
+综上,该智能客服系统基于成熟技术与AI优势,具备良好的商业价值和市场潜力。其多渠道接入和情感分析等功能增强竞争力,但需合理控制AI调用成本和运营费用。建议重点推进SaaS订阅和增值服务,结合市场推广,快速占领客户资源,提升盈利能力。
+----------------------------------------
+
+【用户体验分析】
+针对该智能客服系统方案,我将从用户体验、易用性、用户满意度及可访问性等角度进行分析:
+
+1. 用户友好性
+- 自然语言理解和回复能力提升了用户与系统的沟通体验,使用户能够用自然话语表达需求,降低交流障碍。
+- 多轮对话管理允许系统理解上下文,减少重复解释,增强对话连贯性,进一步提升用户体验。
+- 情感分析功能有助于系统识别用户情绪,做出更贴心的回应,提高互动的个性化和人性化。
+- 多渠道接入覆盖用户常用的访问途径,方便用户随时随地获取服务,提升友好度。
+
+2. 操作便利性
+- 自动回答常见业务咨询能够减轻用户等待时间和操作负担,提高响应速度。
+- 人工客服转接机制确保复杂问题可被及时处理,保障服务连续性和操作的无缝衔接。
+- 对话历史记录方便用户回顾咨询内容,避免重复查询,提升操作便利。
+- 使用现代技术栈(React、TypeScript)为前端交互提供良好性能和响应速度,间接增强操作流畅性。
+
+3. 学习成本
+- 基于自然语言处理,用户无需学习特殊指令,降低使用门槛。
+- 多轮对话自然衔接,让用户更易理解系统响应逻辑,减少迷惑和挫败感。
+- 不同渠道的一致性界面(如在网页和微信中保持类似体验)有助于用户迅速上手。
+- 通过情感分析提供的更精准反馈,减少用户因误解而频繁尝试的时间成本。
+
+4. 用户满意度
+- 快速准确的自动回复和多轮对话减少用户等待和重复输入,提升满意度。
+- 情感分析让系统更懂用户情绪,带来更温暖的交互体验,增加用户粘性。
+- 人工客服介入保障复杂问题得到妥善处理,提高服务质量感知。
+- 多渠道覆盖满足不同用户的使用场景,增强整体满意度。
+
+5. 可访问性
+- 多渠道接入覆盖网页、微信、APP,适应不同用户的设备和环境,提升可访问性。
+- 方案未明确提及无障碍设计(如屏幕阅读器兼容、高对比度模式等),这可能是未来需要补充的部分。
+- 前端采用React和TypeScript,有利于实现响应式设计和无障碍功能,但需确保开发规范落地。
+- 后端架构和部署方案保证系统的稳定性和扩展性,间接提升用户持续可访问性。
+
+总结:
+该智能客服系统方案在用户体验和易用性方面考虑较为充分,利用大语言模型实现自然多轮对话、情感分析和知识库集成,满足用户多样化需求。同时,多渠道接入增强了系统的覆盖能力。建议在具体落地时,强化无障碍设计,实现更全面的可访问性保障,同时继续优化对话策略以提升用户满意度。
+----------------------------------------
+
+【安全分析】
+针对该智能客服系统方案,结合信息安全、数据保护及隐私合规等方面,展开如下分析:
+
+一、数据安全
+
+1. 数据传输安全
+- 建议系统所有客户端与服务器间通信均采用TLS/SSL加密,保障数据在传输过程中的机密性与完整性。
+- 由于支持多渠道接入(网页、微信、APP),需确保每个入口均严格实施加密传输。
+
+2. 数据存储安全
+- PostgreSQL存储对话历史、用户资料等敏感信息,需启用数据库加密(如透明数据加密TDE或字段级加密),防止数据泄露。
+- Redis作为缓存,可能存储临时会话数据,也需开启访问认证与加密传输。
+- 对用户敏感数据实行最小存储原则,避免无关数据超范围保存。
+- 数据备份过程中需加密保存,且备份访问同样受控。
+
+3. API调用安全
+- GPT-4 API调用产生大量用户数据交互,应评估其数据处理及存储政策,确保符合数据安全要求。
+- 增加调用权限管理,限制API密钥访问范围和权限,避免被滥用。
+
+4. 日志安全
+- 系统日志中避免存储明文敏感信息,尤其是个人身份信息、对话内容。日志访问需严格控制。
+
+二、隐私保护
+
+1. 个人数据处理
+- 采集和存储用户个人数据(姓名、联系方式、账务信息等)必须明确告知用户,并征得用户同意。
+- 实施数据匿名化/去标识化技术,尤其是对话历史中的身份信息处理。
+
+2. 用户隐私权利
+- 满足相关法律法规(例如《个人信息保护法》、《GDPR》)中用户的访问、更正、删除数据的权利。
+- 提供隐私政策明确披露数据收集、使用和共享情况。
+
+3. 交互隐私
+- 多轮对话和情感分析等功能应考虑避免过度侵犯用户隐私,例如敏感情绪数据的使用透明告知和限制。
+
+4. 第三方合规
+- GPT-4 API由第三方提供,需确保其服务符合相关隐私合规要求及数据保护标准。
+
+三、访问控制
+
+1. 用户身份验证
+- 系统中涉及用户身份信息查询和管理时,需建立可靠的身份认证机制。
+- 支持多因素认证增强安全性。
+
+2. 权限管理
+- 后端管理接口及人工客服转接模块需采用基于角色的访问控制(RBAC),确保操作权限最小化。
+- 对访问敏感数据的操作需有详细审计和监控。
+
+3. 会话管理
+- 对多渠道的会话要有有效的会话管理机制,防止会话劫持。
+- 对话历史访问权限应限制仅允许相关用户或授权人员访问。
+
+四、安全漏洞
+
+1. 应用安全
+- 前端React+TypeScript应防止XSS、CSRF攻击,合理使用Content Security Policy(CSP)。
+- 后端Go应用需防止SQL注入、请求伪造和权限缺失。Gin框架提供中间件支持,建议充分利用安全模块。
+
+2. AI模型风险
+- GPT-4 API本身输入输出可能存在敏感信息泄露或模型误用风险,需限制输入内容、过滤敏感信息。
+- 防止生成恶意回答或信息泄露,建立内容审核机制。
+
+3. 容器和部署安全
+- Docker容器须采用安全镜像,及时打补丁。Kubernetes集群网络策略和访问控制需完善。
+- 容器运行权限最小化,避免容器逃逸风险。
+
+五、合规要求
+
+1. 数据保护法规
+- 根据运营地域,需符合《个人信息保护法》(PIPL)、《欧盟通用数据保护条例》(GDPR)或其他相关法律要求。
+- 明确用户数据的采集、处理、传输和存储流程符合法规。
+
+2. 用户隐私告知及同意
+- 应提供清晰的隐私政策和使用条款,说明数据用途及处理方式。
+- 实现用户同意管理(Consent Management)机制。
+
+3. 数据跨境传输合规
+- 若系统涉及跨境数据流,需评估合规风险和采取相应技术
+----------------------------------------
+
+多角度并行分析完成!
+共收到 4 个分析结果
+```
+
+# 总结
+
+Workflow Agents 为 Eino ADK 提供了强大的多 Agent 协作能力,通过合理选择和组合这些 Workflow Agent,开发者可以构建出高效、可靠的多 Agent 协作系统,满足各种复杂的业务需求。
diff --git a/docs/Eino/docs/core_modules/eino_adk/agent_interface.md b/docs/Eino/docs/core_modules/eino_adk/agent_interface.md
new file mode 100644
index 0000000..a3629db
--- /dev/null
+++ b/docs/Eino/docs/core_modules/eino_adk/agent_interface.md
@@ -0,0 +1,390 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Agent 抽象
+weight: 3
+---
+
+# Agent 定义
+
+Eino 定义了 Agent 的基础接口,实现此接口的 Struct 可被视为一个 Agent:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, opts ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+| Method | 说明 |
| Name | Agent 的名称,作为 Agent 的标识 |
| Description | Agent 的职能描述信息,主要用于让其他的 Agent 了解和判断该 Agent 的职责或功能 |
| Run | Agent 的核心执行方法,返回一个迭代器,调用者可以通过这个迭代器持续接收 Agent 产生的事件 |
+
+## AgentRunOption
+
+`AgentRunOption` 由 Agent 实现定义,可以在请求维度修改 Agent 配置或者控制 Agent 行为。
+
+Eino ADK 提供了一些通用定义的 Option,供用户使用:
+
+- `WithSessionValues`:设置跨 Agent 读写数据
+- `WithSkipTransferMessages`:配置后,当 Event 为 Transfer SubAgent 时,Event 中的消息不会追加到 History 中
+
+Eino ADK 提供了 `WrapImplSpecificOptFn` 和 `GetImplSpecificOptions` 两个方法,供 Agent 包装与读取自定义的 `AgentRunOption`。
+
+当使用 `GetImplSpecificOptions` 方法读取 `AgentRunOptions` 时,与所需类型(如例子中的 options)不符的 AgentRunOption 会被忽略。
+
+例如可以定义 `WithModelName`,在请求维度要求 Agent 修改调用的模型:
+
+```go
+// github.com/cloudwego/eino/adk/call_option.go
+// func WrapImplSpecificOptFn[T any](optFn func(*T)) AgentRunOption
+// func GetImplSpecificOptions[T any](base *T, opts ...AgentRunOption) *T
+
+import "github.com/cloudwego/eino/adk"
+
+type options struct {
+ modelName string
+}
+
+func WithModelName(name string) adk.AgentRunOption {
+ return adk.WrapImplSpecificOptFn(func(t *options) {
+ t.modelName = name
+ })
+}
+
+func (m *MyAgent) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
+ o := &options{}
+ o = adk.GetImplSpecificOptions(o, opts...)
+ // run code...
+}
+```
+
+除此之外,AgentRunOption 具有一个 `DesignateAgent` 方法,调用该方法可以在调用多 Agent 系统时指定 Option 生效的 Agent:
+
+```go
+func genOpt() {
+ // 指定 option 仅对 agent_1 和 agent_2 生效
+ opt := adk.WithSessionValues(map[string]any{}).DesignateAgent("agent_1", "agent_2")
+}
+```
+
+## AsyncIterator
+
+`Agent.Run` 返回了一个迭代器 `AsyncIterator[*AgentEvent]`:
+
+```go
+// github.com/cloudwego/eino/adk/utils.go
+
+type AsyncIterator[T any] struct {
+ ...
+}
+
+func (ai *AsyncIterator[T]) Next() (T, bool) {
+ ...
+}
+```
+
+它代表一个异步迭代器(异步指生产与消费之间没有同步控制),允许调用者以一种有序、阻塞的方式消费 Agent 在运行过程中产生的一系列事件。
+
+- `AsyncIterator` 是一个泛型结构体,可以用于迭代任何类型的数据。当前在 Agent 接口中, Run 方法返回的迭代器类型被固定为 `AsyncIterator[*AgentEvent]` 。这意味着,你从这个迭代器中获取的每一个元素,都将是一个指向 `AgentEvent` 对象的指针。`AgentEvent` 会在后续章节中详细说明。
+- 迭代器的主要交互方式是通过调用其 `Next()` 方法。这个方法的行为是 阻塞式 的,每次调用 `Next()` ,程序会暂停执行,直到以下两种情况之一发生:
+ - Agent 产生了一个新的 `AgentEvent` : `Next()` 方法会返回这个事件,调用者可以立即对其进行处理。
+ - Agent 主动关闭了迭代器 : 当 Agent 不会再产生任何新的事件时(通常是 Agent 运行结束),它会关闭这个迭代器。此时 `Next()` 调用会结束阻塞并在第二个返回值返回 false,告知调用者迭代已经结束。
+
+通常情况下,你需要使用 for 循环处理 `AsyncIterator`:
+
+```go
+iter := myAgent.Run(xxx) // get AsyncIterator from Agent.Run
+
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ // handle event
+}
+```
+
+`AsyncIterator` 可以由 `NewAsyncIteratorPair` 创建,该函数返回的另一个参数 `AsyncGenerator` 用来生产数据:
+
+```go
+// github.com/cloudwego/eino/adk/utils.go
+
+func NewAsyncIteratorPair[T any]() (*AsyncIterator[T], *AsyncGenerator[T])
+```
+
+Agent.Run 返回 AsyncIterator 旨在让调用者实时地接收到 Agent 产生的一系列 AgentEvent,因此 Agent.Run 通常会在 Goroutine 中运行 Agent 从而立刻返回 AsyncIterator 供调用者监听:
+
+```go
+import "github.com/cloudwego/eino/adk"
+
+func (m *MyAgent) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
+ // handle input
+ iter, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()
+ go func() {
+ defer func() {
+ // recover code
+ gen.Close()
+ }()
+ // agent run code
+ // gen.Send(event)
+ }()
+ return iter
+}
+```
+
+## AgentWithOptions
+
+使用 `AgentWithOptions` 方法可以在 Eino ADK Agent 中进行一些通用配置。
+
+与 `AgentRunOption` 不同的是,`AgentWithOptions` 在运行前生效,并且不支持自定义 option。
+
+```go
+// github.com/cloudwego/eino/adk/flow.go
+func AgentWithOptions(ctx context.Context, agent Agent, opts ...AgentOption) Agent
+```
+
+Eino ADK 当前内置支持的配置有:
+
+- `WithDisallowTransferToParent`:配置该 SubAgent 不允许 Transfer 到 ParentAgent,会触发该 SubAgent 的 `OnDisallowTransferToParent` 回调方法
+- `WithHistoryRewriter`:配置后该 Agent 在执行前会通过该方法重写接收到的上下文信息
+
+# AgentEvent
+
+AgentEvent 是 Agent 在其运行过程中产生的核心事件数据结构。其中包含了 Agent 的元信息、输出、行为和报错:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+
+type AgentEvent struct {
+ AgentName string
+
+ RunPath []RunStep
+
+ Output *AgentOutput
+
+ Action *AgentAction
+
+ Err error
+}
+
+// EventFromMessage 构建普通 event
+func EventFromMessage(msg Message, msgStream MessageStream, role schema.RoleType, toolName string) *AgentEvent
+```
+
+## AgentName & RunPath
+
+`AgentName` 和 `RunPath` 字段是由框架自动进行填充,它们提供了关于事件来源的重要上下文信息,在复杂的、由多个 Agent 构成的系统中至关重要。
+
+```go
+type RunStep struct {
+ agentName string
+}
+```
+
+- `AgentName` 标明了是哪一个 Agent 实例产生了当前的 AgentEvent 。
+- `RunPath` 记录了到达当前 Agent 的完整调用链路。`RunPath` 是一个 `RunStep` 切片,它按顺序记录了从最初的入口 Agent 到当前产生事件的 Agent 的所有 `AgentName`。
+
+## AgentOutput
+
+`AgentOutput` 封装了 Agent 产生的输出。
+
+Message 输出设置在 MessageOutput 字段中,其他类型的自定义输出设置在 CustomizedOutput 字段中:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+
+type AgentOutput struct {
+ MessageOutput *MessageVariant
+
+ CustomizedOutput any
+}
+
+type MessageVariant struct {
+ IsStreaming bool
+
+ Message Message
+ MessageStream MessageStream
+ // message role: Assistant or Tool
+ Role schema.RoleType
+ // only used when Role is Tool
+ ToolName string
+}
+```
+
+`MessageOutput` 字段的类型 `MessageVariant` 是一个核心数据结构,主要功能为:
+
+1. 统一处理流式与非流式消息:`IsStreaming` 是一个标志位。值为 true 表示当前 `MessageVariant` 包含的是一个流式消息(从 MessageStream 读取),为 false 则表示包含的是一个非流式消息(从 Message 读取):
+
+ - 流式 : 随着时间的推移,逐步返回一系列消息片段,最终构成一个完整的消息(MessageStream)。
+ - 非流式 : 一次性返回一个完整的消息(Message)。
+2. 提供便捷的元数据访问:Message 结构体内部包含了一些重要的元信息,如消息的 Role(Assistant 或 Tool),为了方便快速地识别消息类型和来源, MessageVariant 将这些常用的元数据提升到了顶层:
+
+ - `Role`:消息的角色,Assistant / Tool
+ - `ToolName`:如果消息角色是 Tool ,这个字段会直接提供工具的名称。
+
+这样做的好处是,代码在需要根据消息类型进行路由或决策时, 无需深入解析 Message 对象的具体内容 ,可以直接从 MessageVariant 的顶层字段获取所需信息,从而简化了逻辑,提高了代码的可读性和效率。
+
+## AgentAction
+
+Agent 产生包含 AgentAction 的 Event 可以控制多 Agent 协作,比如立刻退出、中断、跳转等:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+
+type AgentAction struct {
+ Exit bool
+
+ Interrupted *InterruptInfo
+
+ TransferToAgent *TransferToAgentAction
+
+ BreakLoop *BreakLoopAction
+
+ CustomizedAction any
+}
+
+type InterruptInfo struct {
+ Data any
+}
+
+type TransferToAgentAction struct {
+ DestAgentName string
+}
+```
+
+Eino ADK 当前预设 Action 有四种:
+
+1. 退出:当 Agent 产生 Exit Action 时,Multi-Agent 会立刻退出
+
+```go
+func NewExitAction() *AgentAction {
+ return &AgentAction{Exit: true}
+}
+```
+
+1. 跳转:当 Agent 产生 Transfer Action 时,会跳转到目标 Agent 运行
+
+```go
+func NewTransferToAgentAction(destAgentName string) *AgentAction {
+ return &AgentAction{TransferToAgent: &TransferToAgentAction{DestAgentName: destAgentName}}
+}
+```
+
+1. 中断:当 Agent 产生 Interrupt Action 时,会中断 Runner 的运行。由于中断可能发生在任何位置,同时中断时需要向外传递独特的信息,Action 中提供了 `Interrupted` 字段供 Agent 设置自定义数据,Runner 接收到 Interrupted 不为空的 Action 时则认为产生了中断。Interrupt & Resume 内部机制较为复杂,在 【Eino ADK: Agent Runner】-【Eino ADK: Interrupt & Resume】章节会展开详述。
+
+```go
+// 例如 ChatModelAgent 中断时,会发送如下的 AgentEvent:
+h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{
+ Interrupted: &InterruptInfo{
+ Data: &ChatModelAgentInterruptInfo{Data: data, Info: info},
+ },
+}})
+```
+
+4. 中止循环:当 LoopAgent 的一个子 Agent 发出 BreakLoopAction 时,对应的 LoopAgent 会停止循环并正常退出。
+
+# 语言设置
+
+ADK 提供了 `SetLanguage` 函数用于设置内置提示词(prompt)的语言。这影响所有 ADK 内置组件和中间件生成的提示词语言。本能力在 [alpha/08](https://github.com/cloudwego/eino/releases/tag/v0.8.0-alpha.13) 版本引入。
+
+## API
+
+```go
+// Language 表示 ADK 内置提示词的语言设置
+type Language uint8
+
+const (
+ // LanguageEnglish 表示英文(默认)
+ LanguageEnglish Language = iota
+ // LanguageChinese 表示中文
+ LanguageChinese
+)
+
+// SetLanguage 设置 ADK 内置提示词的语言
+// 默认语言是英文(如果未显式设置)
+func SetLanguage(lang Language) error
+```
+
+## 使用示例
+
+```go
+import "github.com/cloudwego/eino/adk"
+
+// 设置为中文
+err := adk.SetLanguage(adk.LanguageChinese)
+if err != nil {
+ // 处理错误
+}
+
+// 设置为英文(默认)
+err = adk.SetLanguage(adk.LanguageEnglish)
+```
+
+## 影响范围
+
+语言设置会影响以下组件的内置提示词:
+
+| 组件/中间件 | 影响的提示词 |
| FileSystem Middleware | 文件系统工具描述、系统提示词、执行工具提示词 |
| Reduction Middleware | 工具结果截断/清理的提示文字 |
| Skill Middleware | 技能系统提示词、技能工具描述 |
| ChatModelAgent | 内置系统提示词 |
+
+## Agent Interface
+
+Eino ADK 的核心是 Agent 抽象(Agent Interface),ADK 的所有功能设计均围绕 Agent 抽象展开。详解请见 [Eino ADK: Agent 抽象 [New]](/zh/docs/eino/core_modules/eino_adk/agent_interface)
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+
+ // Run runs the agent.
+ // The returned AgentEvent within the AsyncIterator must be safe to modify.
+ // If the returned AgentEvent within the AsyncIterator contains MessageStream,
+ // the MessageStream MUST be exclusive and safe to be received directly.
+ // NOTE: it's recommended to use SetAutomaticClose() on the MessageStream of AgentEvents emitted by AsyncIterator,
+ // so that even the events are not processed, the MessageStream can still be closed.
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+`Agent.Run` 的定义为:
+
+1. 从入参 AgentInput、AgentRunOption 和可选的 Context Session 中获取任务详情及相关数据
+2. 执行任务,并将执行过程、执行结果写入到 AgentEvent Iterator
+
+`Agent.Run` 要求 Agent 的实现以 Future 模式异步执行,核心分成三步,具体可参考 ChatModelAgent 中 Run 方法的实现:
+
+1. 创建一对 Iterator、Generator
+2. 启动 Agent 的异步任务,并传入 Generator,处理 AgentInput。Agent 在这个异步任务执行核心逻辑(例如 ChatModelAgent 调用 LLM),并在产生新的事件时写入到 Generator 中,供 Agent 调用方在 Iterator 中消费
+3. 启动 2 中的任务后立即返回 Iterator
+
+## 多 Agent 协作
+
+围绕 Agent 抽象,Eino ADK 提供多种简单易用、场景丰富的组合原语,可支撑开发丰富多样的 Multi-Agent 协同策略,比如 Supervisor、Plan-Execute、Group-Chat 等 Multi-Agent 场景。从而实现不同的 Agent 分工合作模式,处理更复杂的任务。详解请见 [Eino ADK: Agent 组合](/zh/docs/eino/core_modules/eino_adk/agent_collaboration)
+
+Eino ADK 定义的 Agent 协作过程中的协作原语如下:
+
+- Agent 间协作方式
+
+| 协助方式 | 描述 |
| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
| 上下文策略 | 描述 |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 |
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
| 类别 | ChatModel Agent | Workflow Agents | Custom Logic | EinoBuiltInAgent(supervisor, plan-execute) |
| 功能 | 思考,生成,工具调用 | 控制 Agent 之间的执行流程 | 运行自定义逻辑 | 开箱即用的 Multi-agent 模式封装 |
| 核心 | LLM | 预确定的执行流程(顺序,并发,循环) | 自定义代码 | 基于 Eino 实践积累的经验,对前三者的高度封装 |
| 用途 | 生成,动态决策 | 结构化处理,编排 | 定制需求 | 特定场景内的开箱即用 |
+
+# ADK Examples
+
+[Eino-examples](https://github.com/cloudwego/eino-examples/tree/main/adk) 项目中提供了多种 ADK 的实施样例,您可以参考样例代码与简介,对 adk 能力构建初步的认知:
+
+| 项目路径 | 简介 | 结构图 |
| 顺序工作流案例 | 该示例代码展示了基于 eino adk 的 Workflow 模式构建的一个顺序执行的多智能体工作流。 | ![]() |
| 循环工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 LoopAgent,构建了一个反思迭代型智能体框架。 | ![]() |
| 并行工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 ParallelAgent,构建了一个并发信息搜集框架: | ![]() |
| supervisor | 该用例采用单层 Supervisor 管理两个功能较为综合的子 Agent:Research Agent 负责检索任务,Math Agent 负责多种数学运算(加、乘、除),但所有数学运算均由同一个 Math Agent 内部统一处理,而非拆分为多个子 Agent。此设计简化了代理层级,适合任务较为集中且不需要过度拆解的场景,便于快速部署和维护。 | ![]() |
| layered-supervisor | 该用例实现了多层级智能体监督体系,顶层 Supervisor 管理 Research Agent 和 Math Agent,Math Agent 又进一步细分为 Subtract、Multiply、Divide 三个子 Agent。顶层 Supervisor 负责将研究任务和数学任务分配给下级 Agent,Math Agent 作为中层监督者再将具体数学运算任务分派给其子 Agent。 | ![]() |
| plan-execute 案例 | 本示例基于 eino adk 实现 plan-execute-replan 模式的多 Agent 旅行规划系统,核心功能是处理用户复杂旅行请求(如 “3 天北京游,需从纽约出发的航班、酒店推荐、必去景点”),通过 “计划 - 执行 - 重新计划” 循环完成任务:1. 计划(Plan):Planner Agent基于大模型生成分步执行计划(如 “第一步查北京天气,第二步搜纽约到北京航班”);2. 执行(Execute): Executor Agent调用 ** 天气(get_weather)、航班(search_flights)、酒店(search_hotels)、景点(search_attractions)** 等 Mock 工具执行每一步,若用户输入信息缺失(如未说明预算),则调用 ask_for_clarification工具追问;3. 重新计划(Replan): Replanner Agent根据工具执行结果评估是否需要调整计划(如航班无票则重新选日期)。Execute 和 Replan 不断循环运行,直至完成计划中的所有步骤;4. 支持会话轨迹跟踪(CozeLoop 回调)和状态管理,最终输出完整旅行方案。从结构上看,plan-execute-replan 分为两层: | ![]() |
| 书籍推荐 agent(运行中断与恢复) | 该代码展示了基于 eino adk 框架构建的一个书籍推荐聊天智能体实现,体现了 Agent 运行中断与恢复功能。 | ![]() |
diff --git a/docs/Eino/docs/core_modules/flow_integration_components/_index.md b/docs/Eino/docs/core_modules/flow_integration_components/_index.md
new file mode 100644
index 0000000..4e10646
--- /dev/null
+++ b/docs/Eino/docs/core_modules/flow_integration_components/_index.md
@@ -0,0 +1,135 @@
+---
+Description: ""
+date: "2025-07-21"
+lastmod: ""
+tags: []
+title: Flow 集成
+weight: 3
+---
+
+大模型应用是存在**通用场景和模式**的,若把这些场景进行抽象,就能提供一些可以帮助开发者快速构建大模型应用的模版。Eino 的 Flow 模块就是在做这件事。
+
+目前 Eino 已经集成了 `react agent`、`host multi agent` 两个常用的 Agent 模式,以及 MultiQueryRetriever, ParentIndexer 等。
+
+- React Agent: [Eino: React Agent 使用手册](/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual)
+- Multi Agent: [Eino Tutorial: Host Multi-Agent ](/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
+
+## Flow 进编排
+
+Flow 集成组件自身一般是由一个或多个 graph 编排而成。同时,这些 flow 也可以作为节点进入其他 graph 的编排之中,方式有三种:
+
+1. 如果一个 flow 实现了某个组件的 interface,可用该组件对应的 AddXXXNode 等方法加入编排,如 multiquery retriever:
+
+ ```go
+ // instantiate the flow: multiquery.NewRetriever
+
+ vk, err := newVikingDBRetriever(ctx, vikingDBHost, vikingDBRegion, vikingDBAK, vikingDBSK)
+ if err != nil {
+ logs.Errorf("newVikingDBRetriever failed, err=%v", err)
+ return
+ }
+
+ llm, err := newChatModel(ctx, openAIBaseURL, openAIAPIKey, openAIModelName)
+ if err != nil {
+ logs.Errorf("newChatModel failed, err=%v", err)
+ return
+ }
+
+ // rewrite query by llm
+ mqr, err := multiquery.NewRetriever(ctx, &multiquery.Config{
+ RewriteLLM: llm,
+ RewriteTemplate: nil, // use default
+ QueryVar: "", // use default
+ LLMOutputParser: nil, // use default
+ MaxQueriesNum: 3,
+ OrigRetriever: vk,
+ FusionFunc: nil, // use default fusion, just deduplicate by doc id
+ })
+ if err != nil {
+ logs.Errorf("NewMultiQueryRetriever failed, err=%v", err)
+ return
+ }
+
+ // add the flow to graph
+ graph := compose.NewGraph[string, *schema.Message]()
+ _ = graph.AddRetrieverNode("multi_query_retriever", mqr, compose.WithOutputKey("context"))
+ _ = graph.AddEdge(compose._START_, "multi_query_retriever")
+ _ = graph.AddChatTemplateNode("template", prompt.FromMessages(schema._FString_, schema.UserMessage("{context}")))
+
+ // ...
+ ```
+2. 如果一个 flow 内部是由单个 graph 编排而成,且 flow 的功能可完全等价于这个 graph 的运行(没有不能转化成 graph run 的定制逻辑),则可以将该 flow 的 graph 导出,通过 AddGraphNode 等方法加入编排,如 ReAct Agent 和 Host Multi-Agent:
+
+ ```go
+ // instantiate the host multi-agent
+ hostMA, err := NewMultiAgent(ctx, &MultiAgentConfig{
+ Host: Host{
+ ChatModel: mockHostLLM,
+ },
+ Specialists: []*Specialist{
+ specialist1,
+ specialist2,
+ },
+ })
+ assert.Nil(t, err)
+
+ // export graph and []GraphAddNodeOption from host multi-agent
+ maGraph, opts := hostMA.ExportGraph()
+
+ // add to another graph
+ fullGraph, err := compose.NewChain[map[string]any, *schema.Message]().
+ AppendChatTemplate(prompt.FromMessages(schema._FString_, schema.UserMessage("what's the capital city of {country_name}"))).
+ AppendGraph(maGraph, append(opts, compose.WithNodeKey("host_ma_node"))...).
+ Compile(ctx)
+ assert.Nil(t, err)
+
+ // invoke the other graph
+ // convert the flow's own option to compose.Option if needed
+ // assign options to flow's nodes if needed
+ out, err := fullGraph.Invoke(ctx, map[string]any{"country_name": "China"},
+ compose.WithCallbacks(ConvertCallbackHandlers(mockCallback)).
+ DesignateNodeWithPath(compose.NewNodePath("host_ma_node", hostMA.HostNodeKey())))
+ ```
+3. 所有 flow 应当都可以封装成 Lambda,通过 AddLambdaNode 等方法加入编排。目前所有的 flow 都可以通过 1 或 2 加入编排,所以不需要降级到使用 Lambda。如果要用,使用姿势是:
+
+ ```go
+ // instantiate the flow
+ a, err := NewAgent(ctx, &AgentConfig{
+ Model: cm,
+ ToolsConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{fakeTool, &fakeStreamToolGreetForTest{}},
+ },
+
+ MaxStep: 40,
+ })
+ assert.Nil(t, err)
+
+ chain := compose.NewChain[[]*schema.Message, string]()
+
+ // convert the flow to Lambda
+ agentLambda, err := compose.AnyLambda(a.Generate, a.Stream, nil, nil)
+ assert.Nil(t, err)
+
+ // add lambda to another graph
+ chain.
+ AppendLambda(agentLambda).
+ AppendLambda(compose.InvokableLambda(func(ctx context.Context, input *schema.Message) (string, error) {
+ t.Log("got agent response: ", input.Content)
+ return input.Content, nil
+ }))
+ r, err := chain.Compile(ctx)
+ assert.Nil(t, err)
+
+ // invoke the graph
+ res, err := r.Invoke(ctx, []*schema.Message{{Role: schema._User_, Content: "hello"}},
+ compose.WithCallbacks(callbackForTest))
+ ```
+
+三个方法的对比如下:
+
+| 方式 | 适用场景 | 优势 |
| 作为组件 | 需实现组件的 interface | 简单直接,语义清晰 |
| 作为 Graph | 由单个 graph 编排而成,功能不超出这个 graph 的范围 | graph 内节点对外层 graph 暴露,可统一分配运行时 option,相比 Lambda 少一层转化,可通过 GraphCompileCallback 获取上下级 graph 关系 |
| 作为 Lambda | 所有 | 普适 |
+
+当 Tools 列表中至少有一个 Tool 配置了 ReturnDirectly 时,ReAct Agent 结构会更复杂:在 ToolsNode 之后会增加一个 Branch,判断是否调用了一个 ReturnDirectly 的 Tool,如果是,直接 END,否则照旧进入 ChatModel。
+
+## 初始化
+
+提供了 ReactAgent 初始化函数,必填参数为 Model 和 ToolsConfig,选填参数为 MessageModifier, MaxStep, ToolReturnDirectly 和 StreamToolCallChecker.
+
+```bash
+go get github.com/cloudwego/eino-ext/components/model/openai@latest
+go get github.com/cloudwego/eino@latest
+```
+
+```go
+import (
+ "github.com/cloudwego/eino-ext/components/model/openai"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/flow/agent/react"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ // 先初始化所需的 chatModel
+ toolableChatModel, err := openai.NewChatModel(...)
+
+ // 初始化所需的 tools
+ tools := compose.ToolsNodeConfig{
+ InvokableTools: []tool.InvokableTool{mytool},
+ StreamableTools: []tool.StreamableTool{myStreamTool},
+ }
+
+ // 创建 agent
+ agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ ToolCallingModel: toolableChatModel,
+ ToolsConfig: tools,
+ ...
+ }
+}
+```
+
+### Model
+
+由于 ReAct Agent 需要进行工具调用,Model 需要拥有 ToolCall 的能力,因此需要配置一个 ToolCallingChatModel。
+
+在 Agent 内部,会调用 WithTools 接口向模型注册 Agent 的工具列表,定义为:
+
+```go
+// BaseChatModel defines the basic interface for chat models.
+// It provides methods for generating complete outputs and streaming outputs.
+// This interface serves as the foundation for all chat model implementations.
+//
+//go:generate mockgen -destination ../../internal/mock/components/model/ChatModel_mock.go --package model -source interface.go
+type BaseChatModel interface {
+ Generate(ctx context.Context, input []*schema.Message, opts ...Option) (*schema.Message, error)
+ Stream(ctx context.Context, input []*schema.Message, opts ...Option) (
+ *schema.StreamReader[*schema.Message], error)
+}
+
+// ToolCallingChatModel extends BaseChatModel with tool calling capabilities.
+// It provides a WithTools method that returns a new instance with
+// the specified tools bound, avoiding state mutation and concurrency issues.
+type ToolCallingChatModel interface {
+ BaseChatModel
+
+ // WithTools returns a new ToolCallingChatModel instance with the specified tools bound.
+ // This method does not modify the current instance, making it safer for concurrent use.
+ WithTools(tools []*schema.ToolInfo) (ToolCallingChatModel, error)
+}
+```
+
+目前,eino 提供了 openai, ark 等实现,只要底层模型支持 tool call 即可。
+
+```bash
+go get github.com/cloudwego/eino-ext/components/model/openai@latest
+go get github.com/cloudwego/eino-ext/components/model/ark@latest
+```
+
+```go
+import (
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/ark"
+)
+
+func openaiExample() {
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ Key: os.Getenv("OPENAI_ACCESS_KEY"),
+ ByAzure: true,
+ Model: "{{model name which support tool call}}",
+ })
+
+ agent, err := react.NewAgent(ctx, react.AgentConfig{
+ ToolCallingModel: chatModel,
+ ToolsConfig: ...,
+ })
+}
+
+func arkExample() {
+ arkModel, err := ark.NewChatModel(context.Background(), ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL"),
+ })
+
+ agent, err := react.NewAgent(ctx, react.AgentConfig{
+ ToolCallingModel: arkModel,
+ ToolsConfig: ...,
+ })
+}
+```
+
+### ToolsConfig
+
+toolsConfig 类型为 `compose.ToolsNodeConfig`, 在 eino 中,若要构建一个 Tool 节点,则需要提供 Tool 的信息,以及调用 Tool 的 function。tool 的接口定义如下:
+
+```go
+type InvokableRun func(ctx context.Context, arguments string, opts ...Option) (content string, err error)
+type StreamableRun func(ctx context.Context, arguments string, opts ...Option) (content *schema.StreamReader[string], err error)
+
+type BaseTool interface {
+ Info() *schema.ToolInfo
+}
+
+// InvokableTool the tool for ChatModel intent recognition and ToolsNode execution.
+type InvokableTool interface {
+ BaseTool
+ Run() InvokableRun
+}
+
+// StreamableTool the stream tool for ChatModel intent recognition and ToolsNode execution.
+type StreamableTool interface {
+ BaseTool
+ Run() StreamableRun
+}
+```
+
+用户可以根据 tool 的接口定义自行实现所需的 tool,同时框架也提供了更简便的构建 tool 的方法:
+
+```go
+userInfoTool := utils.NewTool(
+ &schema.ToolInfo{
+ Name: "user_info",
+ Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "用户的姓名",
+ },
+ "email": {
+ Type: "string",
+ Desc: "用户的邮箱",
+ },
+ }),
+ },
+ func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
+ return &userInfoResponse{
+ Name: input.Name,
+ Email: input.Email,
+ Company: "Cool Company LLC.",
+ Position: "CEO",
+ Salary: "9999",
+ }, nil
+ })
+
+toolConfig := &compose.ToolsNodeConfig{
+ InvokableTools: []tool.InvokableTool{invokeTool},
+}
+```
+
+### MessageModifier
+
+MessageModifier 会在每次把所有历史消息传递给 ChatModel 之前执行,定义为:
+
+```go
+// modify the input messages before the model is called.
+type MessageModifier func(ctx context.Context, input []*schema.Message) []*schema.Message
+```
+
+在 Agent 中配置 MessageModifier 可以修改传入模型的 messages,常用于添加前置的 system message:
+
+```go
+import (
+ "github.com/cloudwego/eino/flow/agent/react"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ Model: toolableChatModel,
+ ToolsConfig: tools,
+
+ MessageModifier: func(ctx context.Context, input []*schema.Message) []*schema.Message {
+ res := make([]*schema.Message, 0, len(input)+1)
+
+ res = append(res, schema.SystemMessage("你是一个 golang 开发专家."))
+ res = append(res, input...)
+ return res
+ },
+ })
+
+ agent.Generate(ctx, []*schema.Message{schema.UserMessage("写一个 hello world 的代码")})
+ // 模型得到的实际输入为:
+ // []*schema.Message{
+ // {Role: schema.System, Content:"你是一个 golang 开发专家."},
+ // {Role: schema.Human, Content: "写一个 hello world 的代码"}
+ //}
+}
+```
+
+### MessageRewriter
+
+MessageRewriter 在每次 ChatModel 之前执行,会修改并更新保存全局状态中的历史消息:
+
+```go
+// MessageRewriter modifies message in the state, before the ChatModel is called.
+// It takes the messages stored accumulated in state, modify them, and put the modified version back into state.
+// Useful for compressing message history to fit the model context window,
+// or if you want to make changes to messages that take effect across multiple model calls.
+// NOTE: if both MessageModifier and MessageRewriter are set, MessageRewriter will be called before MessageModifier.
+MessageRewriter MessageModifier
+```
+
+常用于上下文压缩这种在多轮 ReAct 循环中需要一直生效的消息变更。
+
+对比 MessageModifier(只变更不持久,因此适合 system prompt),MessageRewriter 的变更在后续的 ReAct 循环也可见。
+
+### MaxStep
+
+指定 Agent 最大运行步长,每次从一个节点转移到下一个节点为一步,默认值为 node 个数 + 2。
+
+由于 Agent 中一次循环为 ChatModel + Tools,即为 2 步,因此默认值 12 最多可运行 6 个循环。但由于最后一步必须为 ChatModel 返回 (因为 ChatModel 结束后判断无须运行 tool 才能返回最终结果),因此最多运行 5 次 tool。
+
+同理,若希望最多可运行 10 个循环 (10 次 ChatModel + 9 次 Tools),则需要设置 MaxStep 为 20。若希望最多运行 20 个循环,则 MaxStep 需为 40。
+
+```go
+func main() {
+ agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ ToolCallingModel: toolableChatModel,
+ ToolsConfig: tools,
+ MaxStep: 20,
+ }
+}
+```
+
+### ToolReturnDirectly
+
+如果希望当 ChatModel 选择了特定的 Tool 并执行后,Agent 直接把 Tool 的 Response ToolMessage 返回去,则可以在 ToolReturnDirectly 中配置这个 Tool。
+
+```go
+a, err = NewAgent(ctx, &AgentConfig{
+ Model: cm,
+ ToolsConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{fakeTool, fakeStreamTool},
+ },
+
+ MaxStep: 40,
+ ToolReturnDirectly: map[string]struct{}{fakeToolName: {}}, // one of the two tools is return directly
+})
+```
+
+### StreamToolCallChecker
+
+不同的模型在流式模式下输出工具调用的方式可能不同: 某些模型(如 OpenAI) 会直接输出工具调用;某些模型 (如 Claude) 会先输出文本,然后再输出工具调用。因此需要使用不同的方法来判断,这个字段用来指定判断模型流式输出中是否包含工具调用的函数。
+
+可选填写,未填写时使用“非空包”是否包含工具调用判断:
+
+```go
+func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
+ defer sr.Close()
+
+ for {
+ msg, err := sr.Recv()
+ if err == io.EOF {
+ return false, nil
+ }
+ if err != nil {
+ return false, err
+ }
+
+ if len(msg.ToolCalls) > 0 {
+ return true, nil
+ }
+
+ if len(msg.Content) == 0 { // skip empty chunks at the front
+ continue
+ }
+
+ return false, nil
+ }
+}
+```
+
+上述默认实现适用于:模型输出的 Tool Call Message 中只有 Tool Call。
+
+默认实现不适用的情况:在输出 Tool Call 前,有非空的 content chunk。此时,需要自定义 tool Call checker 如下:
+
+```go
+toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
+ defer sr.Close()
+ for {
+ msg, err := sr.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ // finish
+ break
+ }
+
+ return false, err
+ }
+
+ if len(msg.ToolCalls) > 0 {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+```
+
+上面这个自定义 StreamToolCallChecker,在极端情况下可能需要判断**所有包**是否包含 ToolCall,从而导致“流式判断”的效果丢失。如果希望尽可能保留“流式判断”效果,解决这一问题的建议是:
+
+> 💡
+> 尝试添加 prompt 来约束模型在工具调用时不额外输出文本,例如:“如果需要调用 tool,直接输出 tool,不要输出文本”。
+>
+> 不同模型受 prompt 影响可能不同,实际使用时需要自行调整 prompt 并验证效果。
+
+## 调用
+
+### Generate
+
+```go
+agent, _ := react.NewAgent(...)
+
+var outMessage *schema.Message
+outMessage, err = agent.Generate(ctx, []*schema.Message{
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
+})
+```
+
+### Stream
+
+```go
+agent, _ := react.NewAgent(...)
+
+var msgReader *schema.StreamReader[*schema.Message]
+msgReader, err = agent.Stream(ctx, []*schema.Message{
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
+})
+
+for {
+ // msg type is *schema.Message
+ msg, err := msgReader.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ // finish
+ break
+ }
+ // error
+ log.Printf("failed to recv: %v\n", err)
+ return
+ }
+
+ fmt.Print(msg.Content)
+}
+```
+
+### WithCallbacks
+
+Callback 是在 Agent 运行时特定时机执行的回调,由于 Agent 这个 Graph 里面只有 ChatModel 和 ToolsNode,因此 Agent 的 Callback 就是 ChatModel 和 Tool 的 Callback。react 包中提供了一个 helper function 来帮助用户快速构建针对这两个组件类型的 Callback Handler。
+
+```go
+import (
+ template "github.com/cloudwego/eino/utils/callbacks"
+)
+// BuildAgentCallback builds a callback handler for agent.
+// e.g.
+//
+// callback := BuildAgentCallback(modelHandler, toolHandler)
+// agent, err := react.NewAgent(ctx, &AgentConfig{})
+// agent.Generate(ctx, input, agent.WithComposeOptions(compose.WithCallbacks(callback)))
+func BuildAgentCallback(modelHandler *template.ModelCallbackHandler, toolHandler *template.ToolCallbackHandler) callbacks.Handler {
+ return template.NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler()
+}
+```
+
+### Options
+
+React agent 支持通过运行时 Option 动态修改
+
+场景 1:运行时修改 Agent 中的 Model 配置,通过:
+
+```go
+// WithChatModelOptions returns an agent option that specifies model.Option for the chat model in agent.
+func WithChatModelOptions(opts ...model.Option) agent.AgentOption {
+ return agent.WithComposeOptions(compose.WithChatModelOption(opts...))
+}
+```
+
+场景 2:运行时修改 Tool 列表,通过:
+
+```go
+// WithToolList returns an agent option that specifies the list of tools can be called which are BaseTool but must implement InvokableTool or StreamableTool.
+func WithToolList(tools ...tool.BaseTool) agent.AgentOption {
+ return agent.WithComposeOptions(compose.WithToolsNodeOption(compose.WithToolList(tools...)))
+}
+```
+
+另外,也需要修改 ChatModel 中绑定的 tool: `WithChatModelOptions(model.WithTools(...))`
+
+场景 3:运行时修改某个 Tool 的 option,通过:
+
+```go
+// WithToolOptions returns an agent option that specifies tool.Option for the tools in agent.
+func WithToolOptions(opts ...tool.Option) agent.AgentOption {
+ return agent.WithComposeOptions(compose.WithToolsNodeOption(compose.WithToolOption(opts...)))
+}
+```
+
+### Prompt
+
+运行时修改 prompt,其实就是在 Generate 或者 Stream 的时候,传入不同的 Message 列表。
+
+### 获取中间结果
+
+如果希望实时拿到 React Agent 执行过程中产生的 *schema.Message,可以先通过 WithMessageFuture 获取一个运行时 Option 和一个 MessageFuture:
+
+```go
+// WithMessageFuture returns an agent option and a MessageFuture interface instance.
+// The option configures the agent to collect messages generated during execution,
+// while the MessageFuture interface allows users to asynchronously retrieve these messages.
+func WithMessageFuture() (agent.AgentOption, MessageFuture) {
+ h := &cbHandler{started: make(chan struct{})}
+
+ cmHandler := &ub.ModelCallbackHandler{
+ OnEnd: h.onChatModelEnd,
+ OnEndWithStreamOutput: h.onChatModelEndWithStreamOutput,
+ }
+ toolHandler := &ub.ToolCallbackHandler{
+ OnEnd: h.onToolEnd,
+ OnEndWithStreamOutput: h.onToolEndWithStreamOutput,
+ }
+ graphHandler := callbacks.NewHandlerBuilder().
+ OnStartFn(h.onGraphStart).
+ OnStartWithStreamInputFn(h.onGraphStartWithStreamInput).
+ OnEndFn(h.onGraphEnd).
+ OnEndWithStreamOutputFn(h.onGraphEndWithStreamOutput).
+ OnErrorFn(h.onGraphError).Build()
+ cb := ub.NewHandlerHelper().ChatModel(cmHandler).Tool(toolHandler).Graph(graphHandler).Handler()
+
+ option := agent.WithComposeOptions(compose.WithCallbacks(cb))
+
+ return option, h
+}
+```
+
+这个运行时 Option 就正常传递给 Generate 或者 Stream 方法。这个 MessageFuture 可以 GetMessages 或者 GetMessageStreams 来获取各中间状态的 Message。
+
+> 💡
+> 传入 MessageFuture 的 Option 后,Agent 仍然会阻塞运行,通过 MessageFuture 接收中间结果需要和 Agent 运行异步(在 goroutine 中读 MessageFuture 或在 goroutine 中运行 Agent)
+
+## Agent In Graph/Chain
+
+Agent 可作为 Lambda 嵌入到其他的 Graph 中:
+
+```go
+agent, _ := NewAgent(ctx, &AgentConfig{
+ ToolCallingModel: cm,
+ ToolsConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{fakeTool, &fakeStreamToolGreetForTest{}},
+ },
+
+ MaxStep: 40,
+})
+
+chain := compose.NewChain[[]*schema.Message, string]()
+agentLambda, _ := compose.AnyLambda(agent.Generate, agent.Stream, nil, nil)
+
+chain.
+ AppendLambda(agentLambda).
+ AppendLambda(compose.InvokableLambda(func(ctx context.Context, input *schema.Message) (string, error) {
+ t.Log("got agent response: ", input.Content)
+ return input.Content, nil
+ }))
+r, _ := chain.Compile(ctx)
+
+res, _ := r.Invoke(ctx, []*schema.Message{{Role: schema.User, Content: "hello"}},
+ compose.WithCallbacks(callbackForTest))
+```
+
+## Demo
+
+### 基本信息
+
+简介:这是一个拥有两个 tool (query_restaurants 和 query_dishes ) 的 `美食推荐官`
+
+地址:[eino-examples/flow/agent/react](https://github.com/cloudwego/eino-examples/tree/main/flow/agent/react)
+
+使用方式:
+
+1. clone eino-examples repo,并 cd 到根目录
+2. 提供一个 `OPENAI_API_KEY`: `export OPENAI_API_KEY=xxxxxxx`
+3. 运行 demo: `go run flow/agent/react/react.go`
+
+### 运行过程
+
+
+
+### 运行过程解释
+
+- 模拟用户输入了 `我在海淀区,给我推荐一些菜,需要有口味辣一点的菜,至少推荐有 2 家餐厅`
+- agent 运行第一个节点 `ChatModel`,大模型判断出需要做一次 ToolCall 调用来查询餐厅,并且给出的参数为:
+
+```json
+"function": {
+ "name": "query_restaurants",
+ "arguments": "{\"location\":\"海淀区\",\"topn\":2}"
+}
+```
+
+- 进入 `Tools` 节点,调用 查询餐厅 的 tool,并且得到结果,结果返回了 2 家海淀区的餐厅信息:
+
+```json
+[{"id":"1001","name":"老地方餐厅","place":"北京老胡同 5F, 左转进入","desc":"","score":3},{"id":"1002","name":"人间味道餐厅","place":"北京大世界商城-1F","desc":"","score":5}]
+```
+
+- 得到 tool 的结果后,此时对话的 history 中包含了 tool 的结果,再次运行 `ChatModel`,大模型判断出需要再次调用另一个 ToolCall,用来查询餐厅有哪些菜品,注意,由于有两家餐厅,因此大模型返回了 2 个 ToolCall,如下:
+
+```json
+"Message": {
+ "role": "ai",
+ "content": "",
+ "tool_calls": [ // <= 这里有 2 个 tool call
+ {
+ "index": 1,
+ "id": "call_wV7zA3vGGJBhuN7r9guhhAfF",
+ "function": {
+ "name": "query_dishes",
+ "arguments": "{\"restaurant_id\": \"1002\", \"topn\": 5}"
+ }
+ },
+ {
+ "index": 0,
+ "id": "call_UOsp0jRtzEbfxixNjP5501MF",
+ "function": {
+ "name": "query_dishes",
+ "arguments": "{\"restaurant_id\": \"1001\", \"topn\": 5}"
+ }
+ }
+ ]
+ }
+```
+
+- 再次进入到 `Tools` 节点,由于有 2 个 tool call,Tools 节点内部并发执行这两个调用,并且均加入到对话的 history 中,从 callback 的调试日志中可以看到结果如下:
+
+```json
+=========[OnToolStart]=========
+{"restaurant_id": "1001", "topn": 5}
+=========[OnToolEnd]=========
+[{"name":"红烧肉","desc":"一块红烧肉","price":20,"score":8},{"name":"清泉牛肉","desc":"很多的水煮牛肉","price":50,"score":8},{"name":"清炒小南瓜","desc":"炒的糊糊的南瓜","price":5,"score":5},{"name":"韩式辣白菜","desc":"这可是开过光的辣白菜,好吃得很","price":20,"score":9},{"name":"酸辣土豆丝","desc":"酸酸辣辣的土豆丝","price":10,"score":9}]
+=========[OnToolStart]=========
+{"restaurant_id": "1002", "topn": 5}
+=========[OnToolEnd]=========
+[{"name":"红烧排骨","desc":"一块一块的排骨","price":43,"score":7},{"name":"大刀回锅肉","desc":"经典的回锅肉, 肉很大","price":40,"score":8},{"name":"火辣辣的吻","desc":"凉拌猪嘴,口味辣而不腻","price":60,"score":9},{"name":"辣椒拌皮蛋","desc":"擂椒皮蛋,下饭的神器","price":15,"score":8}]
+```
+
+- 得到所有 tool call 返回的结果后,再次进入 `ChatModel` 节点,这次大模型发现已经拥有了回答用户提问的所有信息,因此整合信息后输出结论,由于调用时使用的 `Stream` 方法,因此流式返回的大模型结果。
+
+## 关联阅读
+
+- [Eino Tutorial: Host Multi-Agent ](/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
diff --git a/docs/Eino/docs/ecosystem_integration/_index.md b/docs/Eino/docs/ecosystem_integration/_index.md
new file mode 100644
index 0000000..dd106a5
--- /dev/null
+++ b/docs/Eino/docs/ecosystem_integration/_index.md
@@ -0,0 +1,67 @@
+---
+Description: ""
+date: "2026-01-20"
+lastmod: ""
+tags: []
+title: 组件集成
+weight: 6
+---
+
+## 组件集成
+
+### ChatModel
+
+- 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
+
+#### Loader
+
+- file: [Loader - local file](/zh/docs/eino/ecosystem_integration/document/loader_local_file)
+- s3: [Loader - amazon s3](/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3)
+- web url: [Loader - web url](/zh/docs/eino/ecosystem_integration/document/loader_web_url)
+
+#### Parser
+
+- html: [Parser - html](/zh/docs/eino/ecosystem_integration/document/parser_html)
+- pdf: [Parser - pdf](/zh/docs/eino/ecosystem_integration/document/parser_pdf)
+
+#### Transformer
+
+- markdown splitter: [Splitter - markdown](/zh/docs/eino/ecosystem_integration/document/splitter_markdown)
+- recursive splitter: [Splitter - recursive](/zh/docs/eino/ecosystem_integration/document/splitter_recursive)
+- semantic splitter: [Splitter - semantic](/zh/docs/eino/ecosystem_integration/document/splitter_semantic)
+
+### Embedding
+
+- ark: [Embedding - ARK](/zh/docs/eino/ecosystem_integration/embedding/embedding_ark)
+- openai: [Embedding - OpenAI](/zh/docs/eino/ecosystem_integration/embedding/embedding_openai)
+
+### Indexer
+
+- volc vikingdb: [Indexer - volc VikingDB](/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb)
+- Milvus 2.5+: [Indexer - Milvus 2 (v2.5+)](/zh/docs/eino/ecosystem_integration/indexer/indexer_milvusv2)
+- Milvus 2.4: [Indexer - Milvus](/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus)
+- OpenSearch 3: [Indexer - OpenSearch 3](/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch3)
+- OpenSearch 2: [Indexer - OpenSearch 2](/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch2)
+- ElasticSearch 9: [Indexer - Elasticsearch 9](/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9)
+- Elasticsearch 8: [Indexer - ES8](/zh/docs/eino/ecosystem_integration/indexer/indexer_es8)
+- ElasticSearch 7: [Indexer - Elasticsearch 7 ](/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7)
+
+### Retriever
+
+- volc vikingdb: [Retriever - volc VikingDB](/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb)
+- Milvus 2.5+: [Retriever - Milvus 2 (v2.5+) ](/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2)
+- Milvus 2.4: [Retriever - Milvus](/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus)
+- OpenSearch 3: [Retriever - OpenSearch 3](/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch3)
+- OpenSearch 2: [Retriever - OpenSearch 2](/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch2)
+- ElasticSearch 9: [Retriever - Elasticsearch 9](/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9)
+- ElasticSearch 8: [Retriever - ES8](/zh/docs/eino/ecosystem_integration/retriever/retriever_es8)
+- ElasticSearch 7: [Retriever - ES 7](/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7)
+
+### Tools
+
+- googlesearch: [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch)
+- duckduckgo search: [Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
diff --git a/docs/Eino/docs/ecosystem_integration/callbacks/_index.md b/docs/Eino/docs/ecosystem_integration/callbacks/_index.md
new file mode 100644
index 0000000..9ccb9ca
--- /dev/null
+++ b/docs/Eino/docs/ecosystem_integration/callbacks/_index.md
@@ -0,0 +1,26 @@
+---
+Description: ""
+date: "2026-03-02"
+lastmod: ""
+tags: []
+title: Callbacks
+weight: 5
+---
+
+# Callbacks 组件列表
+
+本分类的各组件详细文档请参考 GitHub README:
+
+| 组件名称 | 中文文档 | English Docs |
| APMPlus | README_zh.md | README.md |
| CozeLoop | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| ARK | README_zh.md | README.md |
| ARKBot | README_zh.md | README.md |
| Claude | README_zh.md | README.md |
| DeepSeek | README_zh.md | README.md |
| Gemini | README_zh.md | README.md |
| Ollama | README_zh.md | README.md |
| OpenAI | README_zh.md | README.md |
| Qianfan | README_zh.md | README.md |
| Qwen | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| MCP | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| amazon s3 | README_zh.md | README.md |
| local file | README_zh.md | README.md |
| web url | README_zh.md | README.md |
| html | README_zh.md | README.md |
| README_zh.md | README.md | |
| markdown | README_zh.md | README.md |
| recursive | README_zh.md | README.md |
| semantic | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| ARK | README_zh.md | README.md |
| Ollama | README_zh.md | README.md |
| OpenAI | README_zh.md | README.md |
| Qianfan | README_zh.md | README.md |
| TencentCloud | README_zh.md | README.md |
| dashscope | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| ElasticSearch 8 | README_zh.md | README.md |
| Elasticsearch 7 | README_zh.md | README.md |
| Elasticsearch 9 | README_zh.md | README.md |
| Milvus v1 (旧版) | README_zh.md | README.md |
| Milvus v2 (推荐) | README_zh.md | README.md |
| OpenSearch 2 | README_zh.md | README.md |
| OpenSearch 3 | README_zh.md | README.md |
| Redis | README_zh.md | README.md |
| volc VikingDB | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| Dify | README_zh.md | README.md |
| Elasticsearch 7 | README_zh.md | README.md |
| Elasticsearch 8 | README_zh.md | README.md |
| Elasticsearch 9 | README_zh.md | README.md |
| Milvus v1 (旧版) | README_zh.md | README.md |
| Milvus v2 (推荐) | README_zh.md | README.md |
| OpenSearch 2 | README_zh.md | README.md |
| OpenSearch 3 | README_zh.md | README.md |
| Redis | README_zh.md | README.md |
| volc VikingDB | README_zh.md | README.md |
| 组件名称 | 中文文档 | English Docs |
| MCP | README_zh.md | README.md |
| Bingsearch | README_zh.md | README.md |
| BrowserUse | README_zh.md | README.md |
| CommandLine | README_zh.md | README.md |
| DuckDuckGoSearch | README_zh.md | README.md |
| Googlesearch | README_zh.md | README.md |
| HTTPRequest | README_zh.md | README.md |
| SequentialThinking | README_zh.md | README.md |
| Wikipedia | README_zh.md | README.md |
+
+[Eino Github 仓库链接](https://github.com/cloudwego/eino)
+
+## 快速上手
+
+直接使用组件:
+
+```go
+model, _ := openai.NewChatModel(ctx, config) // create an invokable LLM instance
+message, _ := model.Generate(ctx, []*Message{
+ SystemMessage("you are a helpful assistant."),
+ UserMessage("what does the future AI App look like?")})
+```
+
+当然,你可以这样用,Eino 提供了许多开箱即用的有用组件。但通过使用编排功能,你能实现更多,原因有三:
+
+- 编排封装了大语言模型(LLM)应用的常见模式。
+- 编排解决了处理大语言模型流式响应这一难题。
+- 编排为你处理类型安全、并发管理、切面注入以及选项赋值等问题。
+
+Eino 提供了三组用于编排的 API:
+
+| API | 特性和使用场景 |
| Chain | 简单的链式有向图,只能向前推进。 |
| Graph | 有向有环或无环图。功能强大且灵活。 |
| Workflow | 有向无环图,支持在结构体字段级别进行数据映射。 |
+
+```go
+chain, _ := NewChain[map[string]any, *Message]().
+ AppendChatTemplate(prompt).
+ AppendChatModel(model).
+ Compile(ctx)
+chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
+```
+
+现在,我们来创建一个 Graph,一个 ChatModel,要么直接输出结果,要么最多调一次 Tool。
+
+
+
+```go
+graph := NewGraph[map[string]any, *schema.Message]()
+
+_ = graph.AddChatTemplateNode("node_template", chatTpl)
+_ = graph.AddChatModelNode("node_model", chatModel)
+_ = graph.AddToolsNode("node_tools", toolsNode)
+_ = graph.AddLambdaNode("node_converter", takeOne)
+
+_ = graph.AddEdge(START, "node_template")
+_ = graph.AddEdge("node_template", "node_model")
+_ = graph.AddBranch("node_model", branch)
+_ = graph.AddEdge("node_tools", "node_converter")
+_ = graph.AddEdge("node_converter", END)
+
+compiledGraph, err := graph.Compile(ctx)
+if err != nil {
+return err
+}
+out, err := compiledGraph.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
+```
+
+现在,我们来创建一个 Workflow,它能在字段级别灵活映射输入与输出:
+
+
+
+```go
+type Input1 struct {
+ Input string
+}
+
+type Output1 struct {
+ Output string
+}
+
+type Input2 struct {
+ Role schema.RoleType
+}
+
+type Output2 struct {
+ Output string
+}
+
+type Input3 struct {
+ Query string
+ MetaData string
+}
+
+var (
+ ctx context.Context
+ m model.BaseChatModel
+ lambda1 func(context.Context, Input1) (Output1, error)
+ lambda2 func(context.Context, Input2) (Output2, error)
+ lambda3 func(context.Context, Input3) (*schema.Message, error)
+)
+
+wf := NewWorkflow[[]*schema.Message, *schema.Message]()
+wf.AddChatModelNode("model", m).AddInput(START)
+wf.AddLambdaNode("lambda1", InvokableLambda(lambda1)).
+ AddInput("model", MapFields("Content", "Input"))
+wf.AddLambdaNode("lambda2", InvokableLambda(lambda2)).
+ AddInput("model", MapFields("Role", "Role"))
+wf.AddLambdaNode("lambda3", InvokableLambda(lambda3)).
+ AddInput("lambda1", MapFields("Output", "Query")).
+ AddInput("lambda2", MapFields("Output", "MetaData"))
+wf.End().AddInput("lambda3")
+runnable, err := wf.Compile(ctx)
+if err != nil {
+ return err
+}
+our, err := runnable.Invoke(ctx, []*schema.Message{
+ schema.UserMessage("kick start this workflow!"),
+})
+```
+
+Eino 的**图编排**开箱即用地提供以下能力:
+
+- **类型检查**:在编译时确保两个节点的输入和输出类型匹配。
+- **流处理**:如有需要,在将消息流传递给 ChatModel 和 ToolsNode 节点之前进行拼接,以及将该流复制到 callback handler 中。
+- **并发管理**:由于 StatePreHandler 是线程安全的,共享的 state 可以被安全地读写。
+- **切面注入**:如果指定的 ChatModel 实现未自行注入,会在 ChatModel 执行之前和之后注入回调切面。
+- **选项赋值**:调用 Option 可以全局设置,也可以针对特定组件类型或特定节点进行设置。
+
+例如,你可以轻松地通过回调扩展已编译的图:
+
+```go
+handler := NewHandlerBuilder().
+ OnStartFn(
+ func(ctx context.Context, info *RunInfo, input CallbackInput) context.Context) {
+ log.Infof("onStart, runInfo: %v, input: %v", info, input)
+ }).
+ OnEndFn(
+ func(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context) {
+ log.Infof("onEnd, runInfo: %v, out: %v", info, output)
+ }).
+ Build()
+
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
+```
+
+或者你可以轻松地为不同节点分配选项:
+
+```go
+// assign to All nodes
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
+
+// assign only to ChatModel nodes
+compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5))
+
+// assign only to node_1
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
+```
+
+现在,咱们来创建一个 “ReAct” 智能体:一个 ChatModel 绑定了一些 Tool。它接收输入的消息,自主判断是调用 Tool 还是输出最终结果。Tool 的执行结果会再次成为聊天模型的输入消息,并作为下一轮自主判断的上下文。
+
+
+
+Eino 的**智能体开发套件(ADK)**提供了开箱即用的 `ChatModelAgent` 来实现这一模式:
+
+```go
+agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "assistant",
+ Description: "A helpful assistant that can use tools",
+ Model: chatModel,
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{weatherTool, calculatorTool},
+ },
+ },
+})
+
+runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent})
+iter := runner.Query(ctx, "What's the weather in Beijing this weekend?")
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ // process agent events (model outputs, tool calls, etc.)
+}
+```
+
+ADK 在内部处理 ReAct 循环,为智能体推理过程的每个步骤发出事件。
+
+除了基本的 ReAct 模式,ADK 还提供了构建生产级智能体系统的强大能力:
+
+**多智能体与上下文管理**:智能体可以将控制权转移给子智能体,或被封装为工具。框架会自动管理跨智能体边界的对话上下文:
+
+```go
+// 设置智能体层级 - mainAgent 现在可以转移到子智能体
+mainAgentWithSubs, _ := adk.SetSubAgents(ctx, mainAgent, []adk.Agent{researchAgent, codeAgent})
+```
+
+当 `mainAgent` 转移到 `researchAgent` 时,对话历史会自动重写,为子智能体提供适当的上下文。
+
+智能体也可以被封装为工具,允许一个智能体在其工具调用工作流中调用另一个智能体:
+
+```go
+// 将智能体封装为可被其他智能体调用的工具
+researchTool := adk.NewAgentTool(ctx, researchAgent)
+```
+
+**随处中断,直接恢复**:任何智能体都可以暂停执行以等待人工审批或外部输入,并从中断处精确恢复:
+
+```go
+// 在工具或智能体内部,触发中断
+return adk.Interrupt(ctx, "Please confirm this action")
+
+// 稍后,从检查点恢复
+iter, _ := runner.Resume(ctx, checkpointID)
+```
+
+**预置智能体模式**:为常见架构提供开箱即用的实现:
+
+```go
+// Deep Agent:经过实战检验的复杂任务编排模式,
+// 内置任务管理、子智能体委派和进度跟踪
+deepAgent, _ := deep.New(ctx, &deep.Config{
+ Name: "deep_agent",
+ Description: "An agent that breaks down and executes complex tasks",
+ ChatModel: chatModel,
+ SubAgents: []adk.Agent{researchAgent, codeAgent},
+ ToolsConfig: adk.ToolsConfig{...},
+})
+
+// Supervisor 模式:一个智能体协调多个专家
+supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{
+ Supervisor: coordinatorAgent,
+ SubAgents: []adk.Agent{writerAgent, reviewerAgent},
+})
+
+// 顺序执行:智能体依次运行
+seqAgent, _ := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ SubAgents: []adk.Agent{plannerAgent, executorAgent, summarizerAgent},
+})
+```
+
+**可扩展的中间件系统**:在不修改核心逻辑的情况下为智能体添加能力:
+
+```go
+fsMiddleware, _ := filesystem.NewMiddleware(ctx, &filesystem.Config{
+ Backend: myFileSystem,
+})
+
+agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ // ...
+ Middlewares: []adk.AgentMiddleware{fsMiddleware},
+})
+```
+
+## 关键特性
+
+### 丰富的组件(Component)
+
+- 将常见的构建模块抽象为**组件**,每个组件抽象都有多个可开箱即用的**组件实现**。
+ - 诸如聊天模型(ChatModel)、工具(Tool)、提示模板(PromptTemplate)、检索器(Retriever)、文档加载器(Document Loader)、Lambda 等组件抽象。
+ - 每种组件类型都有其自身的接口:定义了输入和输出类型、定义了选项类型,以及合理的流处理范式。
+ - 实现细节是透明的。在编排组件时,你只需关注抽象层面。
+- 实现可以嵌套,并包含复杂的业务逻辑。
+ - ReAct 智能体(React Agent)、多查询检索器(MultiQueryRetriever)、主机多智能体(Host MultiAgent)等。它们由多个组件和复杂的业务逻辑构成。
+ - 从外部看,它们的实现细节依然透明。例如在任何接受 Retriever 的地方,都可以使用 MultiQueryRetriever。
+
+## **智能体开发套件(ADK)**
+
+**ADK** 包提供了针对构建 AI 智能体优化的高级抽象:
+
+- **ChatModelAgent**:ReAct 风格的智能体,自动处理工具调用、对话状态和推理循环。
+- **多智能体与上下文工程**:构建层级化智能体系统,对话历史在智能体转移和智能体作为工具调用时自动管理,实现专业智能体间的无缝上下文共享。
+- **工作流智能体**:使用 `SequentialAgent`、`ParallelAgent` 和 `LoopAgent` 组合智能体,实现复杂的执行流程。
+- **人机协作**:`Interrupt` 和 `Resume` 机制,支持检查点持久化,适用于需要人工审批或输入的工作流。
+- **预置模式**:开箱即用的实现,包括 Deep Agent(任务编排)、Supervisor(层级协调)和 Plan-Execute-Replan。
+- **智能体中间件**:可扩展的中间件系统,用于添加工具(文件系统操作)和管理上下文(token 缩减)。
+
+### 强大的编排 (Graph/Chain/Workflow)
+
+如需细粒度控制,Eino 提供**图编排**能力,数据从 Retriever / Document Loader / ChatTemplate 流向 ChatModel,接着流向 Tool ,并被解析为最终答案。
+
+- 组件实例是图的 **节点(Node)** ,而 **边(Edge)** 则是数据流通道。
+- 图编排功能强大且足够灵活,能够实现复杂的业务逻辑:
+ - **类型检查、流处理、并发管理、切面注入和选项分配**都由框架处理。
+ - 在运行时进行**分支(Branch)执行、读写全局状态(State)**,或者使用工作流进行字段级别的数据映射。
+
+## **切面(Callbacks)**
+
+**切面**处理日志记录、追踪、指标统计等横切关注点。切面可以直接应用于组件、编排图或 ADK 智能体。
+
+- 支持五种切面类型:OnStart、OnEnd、OnError、OnStartWithStreamInput、OnEndWithStreamOutput。
+- 可通过 Option 在运行时添加自定义回调处理程序。
+
+### 完善的流处理(Streaming)
+
+- 流数据处理(Stream Processing)很重要,因为 ChatModel 在生成消息时会实时输出完整消息的各个分片。在编排场景下会尤为重要,因为更多的组件需要处理分片的消息数据。
+- 对于只接受非流式输入的下游节点(如 ToolsNode),Eino 会自动将流 **拼接(Concatenate)** 起来。
+- 在图的执行过程中,当需要流时,Eino 会自动将非流式**转换**为流式。
+- 当多个流汇聚到一个下游节点时,Eino 会自动 **合并(Merge)** 这些流。
+- 当一个流传入到多个不同的下游节点或传递给回调处理器时,Eino 会自动 **复制(Copy)** 这些流。
+- 如 **分支(Branch)** 、或 **状态处理器(StateHandler)** 等编排元素,也能够感知和处理流。
+- 借助上述流数据处理能力,组件本身的“是否能处理流、是否会输出流”变的对用户透明。
+- 经过编译的 Graph 可以用 4 种不同的流输入输出范式来运行:
+
+| 流处理范式 | 解释 |
| Invoke | 接收非流类型 I ,返回非流类型 O |
| Stream | 接收非流类型 I , 返回流类型 StreamReader[O] |
| Collect | 接收流类型 StreamReader[I] , 返回非流类型 O |
| Transform | 接收流类型 StreamReader[I] , 返回流类型 StreamReader[O] |
+
+Eino 框架由几个部分组成:
+
+- [Eino](https://github.com/cloudwego/eino):包含类型定义、流数据处理机制、组件抽象定义、编排功能、切面机制等。
+- [EinoExt](https://github.com/cloudwego/eino-ext):组件实现、回调处理程序实现、组件使用示例,以及各种工具,如评估器、提示优化器等。
+
+> 💡
+> 针对字节内部使用的组件,有对应的内部代码仓库:
+
+- [Eino Devops](https://github.com/cloudwego/eino-ext/tree/main/devops):可视化开发、可视化调试等。
+- [EinoExamples](https://github.com/cloudwego/eino-examples):是包含示例应用程序和最佳实践的代码仓库。
+
+详见:[Eino 框架结构说明](/zh/docs/eino/overview/eino_框架结构说明)
+
+## 详细文档
+
+针对 Eino 的学习和使用,我们提供了完善的 Eino 用户手册,帮助大家快速理解 Eino 中的概念,掌握基于 Eino 开发设计 AI 应用的技能,赶快通过 [Eino 用户手册](https://www.cloudwego.io/zh/docs/eino/)尝试使用吧~。
+
+若想快速上手,了解 通过 Eino 构建 AI 应用的过程,推荐先阅读 [Eino: 快速开始](https://www.cloudwego.io/zh/docs/eino/quick_start/)
+
+完整 API Reference:[https://pkg.go.dev/github.com/cloudwego/eino](https://pkg.go.dev/github.com/cloudwego/eino)
+
+## 依赖说明
+
+- Go 1.18 及以上版本
+
+## **代码规范**
+
+本仓库开启了 `golangci-lint` 检查以约束基础代码规范,可通过以下命令在本地检查:
+
+```bash
+golangci-lint run ./...
+```
+
+主要规则包括:
+
+- 导出的函数、接口、package 等需要添加注释,且注释符合 GoDoc 规范。
+- 代码格式需符合 `gofmt -s` 规范。
+- import 顺序需符合 `goimports` 规范(std -> third party -> local)。
+
+## 安全
+
+如果你在该项目中发现潜在的安全问题,或你认为可能发现了安全问题,请通过我们的[安全中心](https://security.bytedance.com/src)或[漏洞报告邮箱](mailto:sec@bytedance.com)通知字节跳动安全团队。
+
+请**不要**创建公开的 GitHub Issue。
+
+## 联系我们
+
+- 如何成为 member: [COMMUNITY MEMBERSHIP](https://github.com/cloudwego/community/blob/main/COMMUNITY_MEMBERSHIP.md)
+- Issues: [Issues](https://github.com/cloudwego/eino/issues)
+- 飞书用户群([注册飞书](https://www.feishu.cn/)后扫码进群)
+
+
+
+- 字节内部 OnCall 群
+
+## 开源许可证
+
+本项目依据 [[Apache-2.0 许可证](https://www.apache.org/licenses/LICENSE-2.0.txt)]授权。
diff --git a/docs/Eino/docs/overview/bytedance_eino_practice.md b/docs/Eino/docs/overview/bytedance_eino_practice.md
new file mode 100644
index 0000000..de52085
--- /dev/null
+++ b/docs/Eino/docs/overview/bytedance_eino_practice.md
@@ -0,0 +1,488 @@
+---
+Description: ""
+date: "2026-03-03"
+lastmod: ""
+tags: []
+title: 字节跳动大模型应用 Go 开发框架 —— Eino 实践
+weight: 2
+---
+
+## 前言
+
+开发基于大模型的软件应用,就像指挥一支足球队:**组件**是能力各异的队员,**编排**是灵活多变的战术,**数据**是流转的足球。Eino 是字节跳动开源的大模型应用开发框架,拥有稳定的内核,灵活的扩展性,完善的工具生态,可靠且易维护,背靠豆包、抖音等应用的丰富实践经验。初次使用 Eino,就像接手一支实力雄厚的足球队,即使教练是初出茅庐的潜力新人,也可以踢出高质量、有内容的比赛。
+
+下面就让我们一起踏上新手上路之旅!
+
+## 认识队员
+
+Eino 应用的基本构成元素是功能各异的组件,就像足球队由不同位置角色的队员组成:
+
+| 组件名 | 组件功能 |
| ChatModel | 与大模型交互,输入 Message 上下文,得到模型的输出 Message |
| Tool | 与世界交互,根据模型的输出,执行对应的动作 |
| Retriever | 获取相关的上下文,让模型的输出基于高质量的事实 |
| ChatTemplate | 接收外界输入,转化成预设格式的 prompt 交给模型 |
| Document Loader | 加载指定的文本 |
| Document Transformer | 按照特定规则转化指定的文本 |
| Indexer | 存储文件并建立索引,供后续 Retriever 使用 |
| Embedding | Retriever 和 Indexer 的共同依赖,文本转向量,捕获文本语义 |
| Lambda | 用户定制 function |
| 组件名 | 官方组件实现 |
| ChatModel | OpenAI, Claude, Gemini, Ark, Ollama... |
| Tool | Google Search, Duck Duck Go... |
| Retriever | Elastic Search, Volc VikingDB... |
| ChatTemplate | DefaultChatTemplate... |
| Document Loader | WebURL, Amazon S3, File... |
| Document Transformer | HTMLSplitter, ScoreReranker... |
| Indexer | Elastic Search, Volc VikingDB... |
| Embedding | OpenAI, Ark... |
| Lambda | JSONMessageParser... |
| 编排方式 | 特点和场景 |
| Chain | 链式有向图,始终向前,简单。适合数据单向流动,没有复杂分支的场景。 |
| Graph | 有向图,有最大的灵活性;或有向无环图,不支持分支,但有清晰的祖先关系。 |
+
+```go
+chain, _ := NewChain[map[string]any, *Message]().
+ AppendChatTemplate(prompt).
+ AppendChatModel(model).
+ Compile(ctx)
+chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
+```
+
+Graph,如最多执行一次 ToolCall 的 Agent:
+
+
+
+```go
+graph := NewGraph[map[string]any, *schema.Message]()
+
+_ = graph.AddChatTemplateNode("node_template", chatTpl)
+_ = graph.AddChatModelNode("node_model", chatModel)
+_ = graph.AddToolsNode("node_tools", toolsNode)
+_ = graph.AddLambdaNode("node_converter", takeOne)
+
+_ = graph.AddEdge(START, "node_template")
+_ = graph.AddEdge("node_template", "node_model")
+_ = graph.AddBranch("node_model", branch)
+_ = graph.AddEdge("node_tools", "node_converter")
+_ = graph.AddEdge("node_converter", END)
+
+compiledGraph, err := graph.Compile(ctx)
+if err != nil {
+ return err
+}
+out, err := compiledGraph.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
+```
+
+## 了解工具
+
+现在想象下你接手的足球队用了一些黑科技,比如:在每个队员接球和出球的瞬间,身上的球衣可以自动的记录接球和出球的速度、角度并传递给场边的服务器,这样比赛结束后,就可以统计出每个队员触球的情况和处理球的时间。
+
+在 Eino 中,每个组件运行的开始和结束,也可以通过 Callbacks 机制拿到输入输出及一些额外信息,处理横切面需求。比如一个简单的打日志能力:
+
+```go
+handler := NewHandlerBuilder().
+ OnStartFn(
+ func(ctx context.Context, info *RunInfo, input CallbackInput) context.Context {
+ log.Printf("onStart, runInfo: %v, input: %v", info, input)
+ return ctx
+ }).
+ OnEndFn(
+ func(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context {
+ log.Printf("onEnd, runInfo: %v, out: %v", info, output)
+ return ctx
+ }).
+ Build()
+
+// 注入到 graph 运行中
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
+```
+
+再想象一下,这个足球队的黑科技不止一种,还可以让教练在比赛前制作“锦囊”并藏在球衣里,当队员接球时,这个锦囊就会播放教练事先录制好的妙计,比如“别犹豫,直接射门!”。听上去很有趣,但有一个难点:有的锦囊是给全队所有队员的,有的锦囊是只给一类队员(比如所有前锋)的,而有的锦囊甚至是只给单个队员的。如何有效的做到锦囊妙计的分发?
+
+在 Eino 中,类似的问题是 graph 运行过程中 call option 的分发:
+
+```go
+// 所有节点都生效的 call option
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
+
+// 只对特定类型节点生效的 call option
+compiledGraph.Invoke(ctx, input, WithChatModelOption(model.WithTemperature(0.5)))
+
+// 只对特定节点生效的 call option
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
+```
+
+## 发现独门秘笈
+
+现在,想象一下你的球队里有一些明星球员(中场大脑 ChatModel 和锋线尖刀 StreamableTool)身怀绝技,他们踢出的球速度如此之快,甚至出现了残影,看上去就像是把一个完整的足球切成了很多片!面对这样的“流式”足球,对手球员手足无措,不知道该如何接球,但是你的球队的所有队员,都能够完美的接球,要么直接一个片一个片的接收“流式”足球并第一时间处理,要么自动的把所有片拼接成完整的足球后再处理。身怀这样的独门秘笈,你的球队具备了面对其他球队的降维打击能力!
+
+在 Eino 中,开发者只需要关注一个组件在“真实业务场景”中,是否可以处理流式的输入,以及是否可以生成流式的输出。根据这个真实的场景,具体的组件实现(包括 Lambda Function)就去实现符合这个流式范式的方法:
+
+```go
+// ChatModel 实现了 Invoke(输入输出均非流)和 Stream(输入非流,输出流)两个范式
+type ChatModel interface {
+ Generate(ctx context.Context, input []*Message, opts ...Option) (*Message, error)
+ Stream(ctx context.Context, input []*Message, opts ...Option) (
+ *schema.StreamReader[*Message], error)
+}
+
+// Lambda 可以实现任意四种流式范式
+
+// Invoke is the type of the invokable lambda function.
+type Invoke[I, O, TOption any] func(ctx context.Context, input I, opts ...TOption) (
+ output O, err error)
+
+// Stream is the type of the streamable lambda function.
+type Stream[I, O, TOption any] func(ctx context.Context,
+ input I, opts ...TOption) (output *schema.StreamReader[O], err error)
+
+// Collect is the type of the collectable lambda function.
+type Collect[I, O, TOption any] func(ctx context.Context,
+ input *schema.StreamReader[I], opts ...TOption) (output O, err error)
+
+// Transform is the type of the transformable lambda function.
+type Transform[I, O, TOption any] func(ctx context.Context,
+ input *schema.StreamReader[I], opts ...TOption) (output *schema.StreamReader[O], err error)
+```
+
+Eino 编排能力会自动做两个重要的事情:
+
+1. 上游是流,但是下游只能接收非流时,自动拼接(Concat)。
+2. 上游是非流,但是下游只能接收流时,自动流化(T -> StreamReader[T])。
+
+除此之外,Eino 编排能力还会自动处理流的合并、复制等各种细节,把大模型应用的核心——流处理做到了极致。
+
+## 一场训练赛 -- Eino 智能助手
+
+好了,现在你已经初步了解了 Eino 这支明星球队的主要能力,是时候通过队员(组件)、战术(编排)、工具(切面、可视化)来一场训练赛,去亲自体验一下它的强大。
+
+### 场景设定
+
+Eino 智能助手:根据用户请求,从知识库检索必要的信息并按需调用多种工具,以完成对用户的请求的处理。工具列表如下:
+
+- DuckDuckGo:从 DuckDuckGo 搜索互联网信息
+- EinoTool:获取 Eino 的工程信息,比如仓库链接、文档链接等
+- GitClone:克隆指定仓库到本地
+- 任务管理(TaskManager):添加、查看、删除 任务
+- OpenURL:使用系统的默认应用打开文件、Web 等类型的链接
+
+本文主要呈现一个 Demo 样例,用户可根据自己的场景,更换自己的知识库和工具,以搭建自己所需的智能助手。
+
+先来一起看看「基于 Eino 搭建」起来的 Agent 助手能实现什么效果
+
+
+
+我们分两步来构建这个 Eino 智能助手:
+
+- Knowledge Indexing(索引知识库):将我们在特定领域沉淀的知识,以分词、向量化等多种手段,构建成索引,以便在接收用户请求时,索引出合适的上下文。 本文采用向量化索引来构建知识库。
+- Eino Agent(Eino 智能助手):根据用户的请求信息以及我们预先构建好的可调用的工具,让 ChatModel 帮我们决策下一步应该执行什么动作或输出最终结果。Tool 的执行结果会再次输入给 ChatModel,让 ChatModel 再一次判断下一步的动作,直至完成用户的请求。
+
+### 任务工作流
+
+#### **索引知识库(Knowledge Indexing)**
+
+将 Markdown 格式的 Eino 用户手册,以合适的策略进行拆分和向量化,存入到 RedisSearch 的 VectorStore 中,作为 Eino 知识库。
+
+
+
+#### **Eino 智能体(Eino Agent)**
+
+根据用户请求,从 Eino 知识库召回信息,采用 ChatTemplate 构建消息,请求 React Agent,视需求循环调用对应工具,直至完成处理用户的请求。
+
+
+
+### 所需工具
+
+在从零开始构建「Eino 智能助手」这个实践场景中,需要下列工具:
+
+| 工具集 | 是否必须 | 功能与作用 | 资源列表 |
| Eino 框架 | 必须 | ||
| EinoDev 插件(Goland、VSCode) | 非必须 | ||
| 火山云豆包模型/向量化 | 必须 | 「火山引擎豆包模型」:需要实名认证后购买使用,每人有 50万免费Tokens额度![]() | |
| Docker | 非必须 | ||
| Eino 智能助手代码示例 | 必须 |
+
+#### 启动 Redis Stack
+
+本文将使用 Redis 作为 Vector Database,为方便用户构建环境,提供 Docker 的快捷指令
+
+- 在 eino-examples/quickstart/eino_assistant 提供 docker-compose.yml
+- 在 eino-examples/quickstart/eino_assistant/data 目录下提供了 Redis 的初始知识库
+
+直接用 redis 官方的 redis stack 镜像启动即可
+
+```bash
+# 切换到 eino_assistant 目录
+cd xxx/eino-examples/quickstart/eino_assistant
+
+docker-compose up -d
+```
+
+
+
+- 完成启动后,打开本地的 8001 可进入 redis stack 的 web 界面
+
+> 在浏览器打开链接: [http://127.0.0.1:8001](http://127.0.0.1:8001)
+
+#### 可视化开发
+
+> 「Eino 可视化开发」是为了降低 Eino AI 应用开发的学习曲线,提升开发效率。对于熟悉 Eino 的开发者,也可选择跳过「Eino 可视化开发」阶段,直接基于 Eino 的 API 进行全码开发。
+
+1. [安装 EinoDev 插件](/zh/docs/eino/core_modules/devops/ide_plugin_guide),并打开 Eino Workflow 功能
+
+ - Graph name: KnowledgeIndexing
+ - Node trigger mode: Triggered after all predecessor nodes are executed
+ - Input type: document.Source
+ - Import path of input type: github.com/cloudwego/eino/components/document
+ - Output type: []string
+ - 其他置空
+
+
+2. 按照上文「**索引知识库**」中的流程说明,从 Eino Workflow 中选择需要使用的组件库,本文需要用到如下组件:
+
+ - document/loader/file
+ - 从指定 URI 加载文件,解析成文本内容,以 schema.Document 列表形式返回。
+ - document/transformer/splitter/markdown
+ - 将从 FileLoader 中加载到的文本内容,进一步拆分成合适的大小,以平衡向量化计算/存储的尺寸限制和召回的效果。
+ - indexer/redis
+ - 将 schema.Document 的原文、索引字段 存储在 Redis Vector Database 中
+ - embedding/ark
+ - 采用 Ark 平台的向量化模型,对 schema.Document 中的 Content 等内容进行向量化计算
+3. 将选中的组件按照预期的拓扑结构进行编排,完成编排后,点击“生成代码”到指定目录。
+
+ - 「**索引知识库**」的代码生成到:eino_assistant/eino/knowledgeindexing
+ - 本示例可直接复制 eino/knowledge_indexing.json 中的 Graph Schema,来快速构建示例中的图
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+#### 运行
+
+> PS: 示例项目中,已经内置了 eino 的一部分文档向量化到 redis 中
+
+1. 在 .env 文件中按照注释说明,获取并填写 ARK_EMBEDDING_MODEL 和 ARK_API_KEY 的值,按如下指令,运行 KnowledgeIndexing 指令
+
+ ```bash
+ cd xxx/eino-examples/quickstart/eino_assistant # 进入 eino assistant 的 example 中
+
+ # 修改 .env 中所需的环境变量 (大模型信息、trace 平台信息)
+ source .env
+
+ # 因示例的Markdown文件存放在 cmd/knowledgeindexing/eino-docs 目录,代码中指定了相对路径 eino-docs,所以需在 cmd/knowledgeindexing 运行指令
+ cd cmd/knowledgeindexing
+ go run main.go
+ ```
+
+
+2. 执行运行成功后,即完成 Eino 知识库的构建,可在 Redis Web UI 中看到向量化之后的内容
+
+ > 在浏览器打开链接: [http://127.0.0.1:8001](http://127.0.0.1:8001)
+ >
+
+
+
+### Eino 智能体
+
+> 示例的仓库路径:[https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant](https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant)
+>
+> 下文中,采用相对于此目录的相对路径来标识资源位置
+
+构建一个基于从 Redis VectorStore 中召回的 Eino 知识回答用户问题,帮用户执行某些操作的 ReAct Agent,即典型的 RAG ReAct Agent。可根据对话上下文,自动帮用户记录任务、Clone 仓库,打开链接 等。
+
+#### 大模型资源创建
+
+继续使用「索引知识库」章节中创建的 doubao-embedding-large 和 doubao-pro-4k
+
+#### 启动 RedisSearch
+
+继续使用「索引知识库」章节中启动的 Redis Stack
+
+#### 可视化开发
+
+
+
+1. 打开 EinoDev 插件,进入到 Eino Workflow 页面,新建一张画布
+
+ - Graph Name: EinoAgent
+ - Node Trigger Mode: 任意前驱节点结束后触发
+ - Input Type Name: *UserMessage
+ - Input Package Path: ""
+ - Output Type Name: *schema.Message
+ - Output Import Path: github.com/cloudwego/eino/schema
+ - 其他置空
+2. 按照上文「**Eino 智能体**」中的流程说明,从 Eino Workflow 中选择需要使用的组件库,本文需要用到如下组件:
+
+ - lambda: 将开发者任意的函数 func(ctx context.Context, input I) (output O, err error),转换成可被编排的节点,在 EinoAgent 中,有两个转换场景
+ - 将 *UserMessage 消息转换成 ChatTemplate 节点的 map[string]any
+ - 将 *UserMessage 转换成 RedisRetriever 的输入 query
+ - retriever/redis
+ - 根据用户 Query 从 Redis Vector Database 根据语义相关性,召回和 Query 相关的上下文,以 schema.Document List 的形式返回。
+ - prompt/chatTemplate
+ - 通过字符串字面量构建 Prompt 模板,支持 文本替换符 和 消息替换符,将输入的任意 map[string]any,转换成可直接输入给模型的 Message List。
+ - flow/agent/react
+ - 基于开发者提供的 ChatModel 和 可调用的工具集,针对用户的问题,自动决策下一步的 Action,直至能够产生最终的回答。
+ - model/ark
+ - Ark 平台提供的能够进行对话文本补全的大模型,例如豆包模型。作为 ReAct Agent 的依赖注入。
+ - 可调用的工具列表
+ - 互联网搜索工具(DuckDuckGo)、EinoTool、GitClone、任务管理(TaskManager)、 OpenURL
+3. 将选中的组件按照预期的拓扑结构进行编排,完成编排后,点击“生成代码”到指定目录。
+
+ - 本示例中,「**Eino 智能体**」的代码生成到:eino/einoagent
+ - 本示例可直接复制 eino/eino_agent.json 中的 Graph Schema,来快速构建示例中的图
+
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+#### 运行
+
+1. 在 .env 文件中按照注释说明,获取并填写对应各变量的值,按如下指令,启动 Eino Agent Server
+
+ ```bash
+ cd eino-examples/eino_assistant # 进入 eino assistant 的 example 中
+
+ # 修改 .env 中所需的环境变量 (大模型信息、trace 平台信息)
+ source .env
+
+ # 为了使用 data 目录,需要在 eino_assistant 目录下执行指令
+ go run cmd/einoagent/*.go
+ ```
+
+
+2. 启动后可访问如下链接,打开 Eino Agent Web
+
+> Eino Agent Web:[http://127.0.0.1:8080/agent/](http://127.0.0.1:8080/agent/)
+
+#### 观测(可选)
+
+##### APMPlus
+
+如果在运行时,在 .env 文件中指定了 `APMPLUS_APP_KEY`,便可在 [火山引擎 APMPlus](https://console.volcengine.com/apmplus-server) 平台中,登录对应的账号,查看 Trace 以及 Metrics 详情。
+
+
+
+##### Langfuse
+
+如果在运行时,在 .env 文件中指定了 `LANGFUSE_PUBLIC_KEY` 和 `LANGFUSE_SECRET_KEY`,便可在 Langfuse 平台中,登录对应的账号,查看请求的 Trace 详情。
+
+
+
+## 相关链接
+
+项目地址:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino),[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+
+Eino 用户手册:[https://www.cloudwego.io/zh/docs/eino/](https://www.cloudwego.io/zh/docs/eino/)
+
+项目官网:__[https://www.cloudwego.io](https://www.cloudwego.io)__
+
+扫描二维码加入飞书社群:
+
+
diff --git a/docs/Eino/docs/overview/eino_adk0_1.md b/docs/Eino/docs/overview/eino_adk0_1.md
new file mode 100644
index 0000000..af28e17
--- /dev/null
+++ b/docs/Eino/docs/overview/eino_adk0_1.md
@@ -0,0 +1,571 @@
+---
+Description: ""
+date: "2026-03-24"
+lastmod: ""
+tags: []
+title: Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统
+weight: 6
+---
+
+# 前言
+
+当大语言模型突破了 “理解与生成” 的瓶颈,Agent 迅速成为 AI 落地的主流形态。从智能客服到自动化办公,几乎所有场景都需要 Agent 来承接 LLM 能力、执行具体任务。
+
+但技术演进中痛点也随之凸显,有的团队因不懂如何衔接 LLM 与业务系统,导致 Agent 只能 “空谈”;有的因状态管理缺失,让 Agent 执行任务时频频 “失忆”,复杂的交互流程也进一步增加了开发难度。
+
+为此,**Eino ADK(Agent Development Kit)应运而生,为 Go 开发者提供了一套完整、灵活且强大的智能体开发框架**,直接解决传统开发中的核心难题。
+
+## 🙋 什么是 Agent?
+
+Agent 代表一个独立的、可执行的智能任务单元,能够自主学习,适应与作出决策,主要功能包含:
+
+- **推理**:Agent 可以分析数据、识别模式、使用逻辑和可用信息来得出结论、进行推断及解决问题。
+- **行动**:Agent 根据决策、计划或外部输入采取行动或执行任务来实现目标。
+- **观察**:Agent 自主收集相关的信息(例如计算机视觉、自然语言处理或传感器数据分析)来了解上下文,为做出明智的决策打下基础。
+- **规划**:Agent 可以确定必要的步骤、评估潜在行动,并根据可用信息和预期结果选择最佳行动方案。
+- **协作**:Agent 能够在复杂且动态的环境中,与他人(无论是人类还是其他 AI 智能体)进行有效协作。
+
+你可以把它想象成一个能够理解指令、执行任务并给出回应的“智能体”。任何需要与大语言模型(LLM)交互的场景都可以抽象为一个 Agent。例如:
+
+- 一个用于查询天气信息的 Agent。
+- 一个用于预定会议的 Agent。
+- 一个能够回答特定领域知识的 Agent。
+
+## 🙋♂️ 什么是 Eino ADK?
+
+[Eino ADK](https://github.com/cloudwego/eino) 是一个专为 Go 语言设计的 Agent 和 Multi-Agent 开发框架,设计上参考了 [Google-ADK](https://google.github.io/adk-docs/agents/) 中对 Agent 与协作机制的定义。
+
+它不仅是一个工具库,更是一套完整的智能体开发体系:通过统一的抽象接口、灵活的组合模式和强大的协作机制,将复杂的 AI 应用拆解为独立、可组合的智能体单元,让开发者能够像搭建乐高积木一样构建复杂的智能体系统:
+
+- **少写胶水**:统一接口与事件流,复杂任务拆解更自然。
+- **快速编排**:预设范式 + 工作流,分分钟搭好管线。
+- **更可控**:可中断、可恢复、可审计,Agent 协作过程“看得见”。
+
+无论你是 AI 应用的新手,还是经验丰富的开发者,ADK 都能为你提供合适的工具和模式。它的设计哲学是"简单的事情简单做,复杂的事情也能做"——让开发者能够专注于业务逻辑的实现,而不必担心底层的技术复杂性。
+
+# 核心构建
+
+## 🧠 ChatModelAgent:智能决策的大脑
+
+`ChatModelAgent` 是 ADK 中最重要的预构建组件,它封装了与大语言模型的交互逻辑,实现了经典的 [ReAct](https://react-lm.github.io/)(Reason-Act-Observe)模式,运行过程为:
+
+1. 调用 LLM(Reason)
+2. LLM 返回工具调用请求(Action)
+3. ChatModelAgent 执行工具(Act)
+4. 将工具结果返回给 LLM(Observation),结合之前的上下文继续生成,直到模型判断不需要调用 Tool 后结束。
+
+
+
+ReAct 模式的核心是“**思考 → 行动 → 观察 → 再思考**”的闭环,解决传统 Agent “盲目行动”或“推理与行动脱节”的痛点,以下是几种可能的实践场景:
+
+- **行业赛道分析**:使用 ReAct 模式避免了一次性搜集全部信息导致的信息过载,通过逐步推理聚焦核心问题;同时使用数据验证思考,而非凭空靠直觉决策,过程可解释,提升了生成报告的准确性。
+ - **Think-1**:判断赛道潜力,需要 “政策支持力度、行业增速、龙头公司盈利能力、产业链瓶颈”4 类信息。
+ - **Act-1**:调用 API 获取行业财报整体数据
+ - **Think-2**:分析数据,判断行业高增长 + 政策背书,但上游价格上涨可能挤压中下游利润,需要进一步验证是否有影响
+ - **Act-2**: 调用 API 获取供需、行业研报等详细数据
+ - **Think-3**: 整合结论生成分析报告,附关键数据来源
+- **IT 故障运维**:使用 ReAct 模式逐步缩小问题范围,避免盲目操作;每一步操作有理有据,方便运维工程师实施解决方案前的二次验证,为后续复盘与制定预防措施提供基础。
+ - **Think-1**:理清故障的常见原因,例如宕机的常见原因是 “CPU 过载、内存不足、磁盘满、服务崩溃”,需要先查基础监控数据
+ - **Act-1**:调用「监控系统 API」查询服务器打点数据
+ - **Think-2**:判断主因,例如 CPU 利用率异常则进一步排查哪些进程 CPU 占用高
+ - **Act-2**:用「进程管理工具」查 TOP 进程,看是否有异常服务
+ - **Think-3**:发现日志服务异常,可能是 “日志文件过大” 或 “配置错误”,需要进一步查看日志服务的配置和日志文件大小
+ - **Act-3**:bash 执行命令,发现日志文件过大,同时配置未开启滚动,也未设置最大日志大小
+ - **Think-4**:向运维工程师提供可行的解决方案:清理日志,修改配置并开启滚动,重启日志服务与应用
+
+`ChatModelAgent` 利用 LLM 强大的功能进行推理、理解自然语言、作出决策、生成响应、进行工具交互,**充当智能体应用程序 "思考" 的部分**。您可以使用 ADK 快速构建具有 `ReAct` 能力的 `ChatModelAgent`:
+
+```go
+import github.com/cloudwego/eino/adk
+
+// 创建一个包含多个工具的 ReAct ChatModelAgent
+chatAgent := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "intelligent_assistant",
+ Description: "An intelligent assistant capable of using multiple tools to solve complex problems",
+ Instruction: "You are a professional assistant who can use the provided tools to help users solve problems",
+ Model: openaiModel,
+ ToolsConfig: adk.ToolsConfig{
+ Tools: []tool.BaseTool{
+ searchTool,
+ calculatorTool,
+ weatherTool,
+ },
+ }
+})
+```
+
+## 🎭 WorkflowAgents:精密的流水线
+
+Eino ADK 提供了专用于协调子 Agent 执行流程的 WorkflowAgents 模式,用于通过预定义逻辑管理 Agent 的运行方式,产生确定的执行过程,协助实现**可预测可控制的多 Agent 协作方式**。您可以按需对下列模式进行排列组合,结合 `ChatModelAgent` 构造出符合自身需求的完整工作流水线:
+
+- **Sequential Agent**: 将配置中注册的 Agents 按顺序依次执行一次后结束,运行遵循以下原则:
+ - **线性执行**:严格按照 SubAgents 数组的顺序执行。
+ - **运行结果传递**:配置中的每个 Agent 都能够获取 Sequential Agent 的完整输入以及前序 Agent 的输出。
+ - **支持提前退出**:如果任何一个子 Agent 产生退出 / 中断动作,整个 Sequential 流程会立即终止。
+- 可能的实践场景有:
+ - **数据 ETL**:`ExtractAgent`(从 MySQL 抽取订单数据)→ `TransformAgent`(清洗空值、格式化日期)→ `LoadAgent`(加载到数据仓库)
+ - **CI / CD 流水线**:`CodeCloneAgent`(从代码仓库拉取代码)→`UnitTestAgent`(运行单元测试,用例失败时返回错误与分析报告)→`CompileAgent`(编译代码)→`DeployAgent`(部署到目标环境)
+
+```go
+import github.com/cloudwego/eino/adk
+
+// 依次执行 制定研究计划 -> 搜索资料 -> 撰写报告
+sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ Name: "research_pipeline",
+ SubAgents: []adk.Agent{
+ planAgent, // 制定研究计划
+ searchAgent, // 搜索资料
+ writeAgent, // 撰写报告
+ },
+})
+```
+
+
+
+- **Parallel Agent**: 将配置中注册的 Agents 并发执行,所有 Agent 执行完毕后结束,运行遵循以下原则:
+ - **并发执行**:所有子 Agent 同时启动,在独立的 goroutine 中并行执行。
+ - **共享输入**:所有子 Agent 接收调用 Pararllel Agent 时相同的初始输入。
+ - **等待与结果聚合**:内部使用 sync.WaitGroup 等待所有子 Agent 执行完成,收集所有子 Agent 的执行结果并按接收顺序输出到 `AsyncIterator` 中。
+- 可能的实践场景有:
+ - **多源数据采集**:`MySQLCollector`(采集用户表)+ `PostgreSQLCollector`(采集订单表)+ `MongoDBCollector`(采集商品评论)
+ - **多渠道推送**:`WeChatPushAgent`(推送到微信公众号)+ `SMSPushAgent`(发送短信)+ `AppPushAgent`(推送到 APP)
+
+```go
+import github.com/cloudwego/eino/adk
+
+// 并发执行 情感分析 + 关键词提取 + 内容摘要
+parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
+ Name: "multi_analysis",
+ SubAgents: []adk.Agent{
+ sentimentAgent, // 情感分析
+ keywordAgent, // 关键词提取
+ summaryAgent, // 内容摘要
+ },
+})
+```
+
+
+
+- **Loop Agent**:将配置中注册的 Agents 按顺序依次执行并循环多次,运行遵循以下原则:
+ - **循环执行**:重复执行 SubAgents 序列,每次循环都是一个完整的 Sequential 执行过程。
+ - **运行结果累积**:每次迭代的结果都会累积,后续迭代的输入可以访问所有历史信息。
+ - **条件退出**:支持通过输出包含 `ExitAction` 的事件或达到最大迭代次数来终止循环,配置 `MaxIterations=0` 时表示无限循环。
+- 可能的实践场景有:
+ - **数据同步**:`CheckUpdateAgent`(检查源库增量)→ `IncrementalSyncAgent`(同步增量数据)→ `VerifySyncAgent`(验证一致性)
+ - **压力测试**:`StartClientAgent`(启动测试客户端)→ `SendRequestsAgent`(发送请求)→ `CollectMetricsAgent`(收集性能指标)
+
+```go
+import github.com/cloudwego/eino/adk
+
+// 循环执行 5 次,每次顺序为:分析当前状态 -> 提出改进方案 -> 验证改进效果
+loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
+ Name: "iterative_optimization",
+ SubAgents: []adk.Agent{
+ analyzeAgent, // 分析当前状态
+ improveAgent, // 提出改进方案
+ validateAgent, // 验证改进效果
+ },
+ MaxIterations: 5,
+})
+```
+
+
+
+## 🛠️ 预构建的 Multi-Agent 范式
+
+Eino ADK 基于日常 Multi-Agent 协作实践中沉淀的最佳工程经验,为用户提供**两种预构建的 Multi-Agent 范式**,无需从头设计协作逻辑即可开箱即用,覆盖「集中式协调」与「结构化问题解决」两大核心场景,高效支撑复杂任务的智能协作。
+
+#### 🎯 Supervisor 模式:集中式协调
+
+Supervisor Agent 是 ADK 提供的一种中心化 Multi-Agent 协作模式,旨在为集中决策与分发执行的通用场景提供解决方案,由一个 Supervisor Agent(监督者) 和多个 SubAgent (子 Agent)组成,其中:
+
+- Supervisor Agent 负责任务的分配、子 Agent 完成后的结果汇总与下一步决策。
+- 子 Agents 专注于执行具体任务,并在完成后自动将任务控制权交回 Supervisor。
+
+
+
+Supervisor 模式有如下特点:
+
+- **中心化控制**:Supervisor 统一管理子 Agent,可根据输入与子 Agent 执行结果动态调整任务分配。
+- **确定性回调**:子 Agent 执行完毕后会将运行结果返回到 Supervisor Agent,避免协作流程中断。
+- **松耦合扩展**:子 Agent 可独立开发、测试和替换,方便拓展与维护。
+
+Supervisor 模式的这种层级化的结构非常适合于**动态协调多个专业 Agent 完成复杂任务**的场景,例如:
+
+- **科研项目管理**:Supervisor 分配调研、实验、报告撰写任务给不同子 Agent。
+- **客户服务流程**:Supervisor 根据用户问题类型,分配给技术支持、售后、销售等子 Agent。
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/supervisor
+
+// 科研项目管理:创建一个监督者模式的 multi-agent
+// 包含 research(调研),experimentation(实验),report(报告)三个子 Agent
+supervisor, err := supervisor.New(ctx, &supervisor.Config{
+ SupervisorAgent: supervisorAgent,
+ SubAgents: []adk.Agent{
+ researchAgent,
+ experimentationAgent,
+ reportAgent,
+ },
+})
+```
+
+#### 🎯 Plan-Execute 模式:结构化问题解决
+
+Plan-Execute Agent 是 ADK 提供的基于「规划-执行-反思」范式的 Multi-Agent 协作模式(参考论文 **Plan-and-Solve Prompting**),旨在解决复杂任务的分步拆解、执行与动态调整问题,通过 Planner(规划器)、Executor(执行器)和 Replanner(重规划器) 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态重规划,最终达成用户目标,其中:
+
+- **Planner**:根据用户目标,生成一个包含详细步骤且结构化的初始任务计划
+- **Executor**:执行当前计划中的首个步骤
+- **Replanner**:评估执行进度,决定是修正计划继续交由 Executor 运行,或是结束任务
+
+
+
+Plan-Execute 模式有如下特点:
+
+- **明确的分层架构**:通过将任务拆解为规划、执行和反思重规划三个阶段,形成层次分明的认知流程,体现了 “先思考再行动,再根据反馈调整” 的闭环认知策略,在各类场景中都能达到较好的效果。
+- **动态迭代优化**:Replanner 根据执行结果和当前进度,实时判断任务是否完成或需调整计划,支持动态重规划。该机制有效解决了传统单次规划难以应对环境变化和任务不确定性的瓶颈,提升了系统的鲁棒性和灵活性。
+- **职责分明且松耦合**:Plan-Execute 模式由多个智能体协同工作,支持独立开发、测试和替换。模块化设计方便扩展和维护,符合工程最佳实践。
+- **具备良好扩展性**:不依赖特定的语言模型、工具或 Agent,方便集成多样化外部资源,满足不同应用场景需求。
+
+Plan-Execute 模式的「规划 → 执行 → 重规划」闭环结构非常适合**需要多步骤推理、动态调整和工具集成的复杂任务场景**,例如:
+
+- **复杂研究分析**:通过规划分解研究问题,执行多轮数据检索与计算,动态调整研究方向和假设,提升分析深度和准确性。
+- **自动化工作流管理**:将复杂业务流程拆解为结构化步骤,结合多种工具(如数据库查询、API 调用、计算引擎)逐步执行,并根据执行结果动态优化流程。
+- **多步骤问题解决**:适用于需要分步推理和多工具协作的场景,如法律咨询、技术诊断、策略制定等,确保每一步执行都有反馈和调整。
+- **智能助理任务执行**:支持智能助理根据用户目标规划任务步骤,调用外部工具完成具体操作,并根据重规划思考结合用户反馈调整后续计划,提升任务完成的完整性和准确性。
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/planexecute
+
+// Plan-Execute 模式的科研助手
+researchAssistant := planexecute.New(ctx, &planexecute.Config{
+ Planner: adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "research_planner",
+ Instruction: "制定详细的研究计划,包括文献调研、数据收集、分析方法等",
+ Model: gpt4Model,
+ }),
+ Executor: adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "research_executor",
+ ToolsConfig: adk.ToolsConfig{
+ Tools: []tool.BaseTool{
+ scholarSearchTool,
+ dataAnalysisTool,
+ citationTool,
+ },
+ },
+ }),
+ Replanner: replannerAgent,
+})
+```
+
+#### 🎯 DeepAgents 模式:规划驱动的集中式协作
+
+DeepAgents 是一种在 Main Agent 统一协调下的 Multi-Agent 模式。Main Agent 借助具备工具调用能力的 ChatModel 以 ReAct 流程运行:
+
+- 通过 WriteTodos 将用户目标拆解为结构化待办并记录进度
+- 通过统一入口 TaskTool 选择并调用对应的 SubAgent 执行子任务;主/子代理上下文隔离,避免中间步骤污染主流程。
+- 汇总各子代理返回的结果;必要时再次调用 WriteTodos 更新进度或进行重规划,直至完成。
+
+
+
+DeepAgents 模式的特点为:
+
+- **强化任务拆解与进度管理**:通过 WriteTodos 形成明确的子任务与里程碑,使复杂目标可分解、可跟踪。
+- **上下文隔离更稳健**:子代理在“干净”上下文中执行,主代理仅汇总结果,减少冗余思维链和工具调用痕迹对主流程的干扰。
+- **统一委派入口、易扩展**:TaskTool 将所有子代理与工具能力抽象为统一调用面,便于新增或替换专业子代理。
+- **计划与执行的灵活闭环**:规划作为工具可按需调用;对简单任务可跳过不必要规划,从而降低 LLM 调用成本与耗时。
+- **边界与权衡**:过度拆解会增加调用次数与成本;对子任务划分与提示词调优提出更高要求,模型需具备稳定的工具调用与规划能力。
+
+DeepAgent 的核心价值在于自动化处理需要多步骤、多角色协作的复杂工作流。它不仅仅是单一功能的执行者,更是一个具备深度思考、规划和动态调整能力的“项目经理”,适配场景有:
+
+- **多角色协作的复杂业务流程**:围绕研发、测试、发布、法务、运营多角色协作,集中委派子任务并统一汇总;每个阶段设定关口与回退策略,进度可视且可重试。
+- **长流程的阶段性管理**:规划拆解清洗、校验、血缘分析、质检等步骤,子代理在隔离上下文中运行;出现异常时仅重跑相关阶段,产物统一对账与汇总。
+- **需要严格上下文隔离的执行环境**:统一入口收集材料与请求,TaskTool 将法务、风控、财务等子任务分别路由;子任务之间边界清晰互不可见,进度与留痕可审计,失败可重试而不影响其他环节。
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/deep
+
+agent, err := deep.New(ctx, &deep.Config{
+ Name: "deep-agent",
+ ChatModel: gpt4Model,
+ SubAgents: []adk.Agent{
+ LegalAgent,
+ RiskControlAgent,
+ FinanceAgent,
+ },
+ MaxIteration: 100,
+})
+```
+
+# 基础设计
+
+## 🎯 统一的 Agent 抽象
+
+ADK 的核心是一个简洁而强大的 `Agent` 接口:
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+每个 Agent 都有明确的身份(Name)、清晰的职责(Description)和标准化的执行方式(Run),为 Agent 之间的发现与调用提供了基础。无论是简单的问答机器人,还是复杂的多步骤任务处理系统,都可以通过这个统一的接口加以实现。
+
+## ⚡ 异步事件驱动架构
+
+ADK 采用了异步事件流设计,通过 `AsyncIterator[*AgentEvent]` 实现非阻塞的事件处理,并通过 `Runner` 框架运行 Agent:
+
+- **实时响应**:`AgentEvent` 包含 Agent 执行过程中特定节点输出(Agent 回复、工具处理结果等等),用户可以立即看到 Agent 的思考过程和中间结果。
+- **追踪执行过程**:`AgentEvent` 额外携带状态修改动作与运行轨迹,便于开发调试和理解 Agent 行为。
+- **自动流程控制**:框架通过 `Runner` 自动处理中断、跳转、退出行为,无需用户额外干预。
+
+## 🤝 灵活的协作机制
+
+Eino ADK 支持处于同一个系统内的 Agent 之间以多种方式进行协作(交换数据或触发运行):
+
+- **共享 Session**:单次运行过程中持续存在的 KV 存储,用于支持跨 Agent 的状态管理和数据共享。
+
+```go
+// 获取全部 SessionValues
+func GetSessionValues(ctx context.Context) map[string]any
+
+// 指定 key 获取 SessionValues 中的一个值,key 不存在时第二个返回值为 false,否则为 true
+func GetSessionValue(ctx context.Context, key string) (any, bool)
+
+// 添加 SessionValues
+func AddSessionValue(ctx context.Context, key string, value any)
+
+// 批量添加 SessionValues
+func AddSessionValues(ctx context.Context, kvs map[string]any)
+```
+
+- **移交运行(Transfer)**:携带本 Agent 输出结果上下文,将任务移交至子 Agent 继续处理。适用于智能体功能可以清晰的划分边界与层级的场景,常结合 ChatModelAgent 使用,通过 LLM 的生成结果进行动态路由。结构上,以此方式进行协作的两个 Agent 称为父子 Agent:
+
+
+
+```go
+// 设置父子 Agent 关系
+func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error)
+
+// 指定目标 Agent 名称,构造 Transfer Event
+func NewTransferToAgentAction(destAgentName string) *AgentAction
+```
+
+- **显式调用(ToolCall)**:将 Agent 视为工具进行调用。适用于 Agent 运行仅需要明确清晰的参数而非完整运行上下文的场景,常结合 ChatModelAgent,作为工具运行后将结果返回给 ChatModel 继续处理。除此之外,ToolCall 同样支持调用符合工具接口构造的、不含 Agent 的普通工具。
+
+
+
+```go
+// 将 Agent 转换为 Tool
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+## 🔄 **中断与恢复机制**
+
+Eino ADK 提供运行时中断与恢复的功能,允许正在运行中的 Agent 主动中断并保存其当前状态,并在未来从中断点恢复执行。该功能为长时间等待、可暂停或需要外部输入(Human in the loop)等场景下的开发提供协助。
+
+- Agent 内部运行过程中,通过抛出含 `Interrupt Action` 的 `Event` 主动通知 `Runner` 中断运行,并允许携带额外信息供调用方阅读与使用。
+- `Runner` 通过初始化时注册的 `CheckPointStore` 记录当前运行状态
+- 重新准备好运行后,通过 `Resume` 方法携带恢复运行所需要的新信息,从断点处重新启动该 Agent 运行
+
+```go
+// 1. 创建支持断点恢复的 Runner
+runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ Agent: complexAgent,
+ CheckPointStore: memoryStore, // 内存状态存储
+})
+
+// 2. 开始执行
+iter := runner.Query(ctx, "recommend a book to me", adk.WithCheckPointID("1"))
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+ if event.Action != nil {
+ // 3. 由 Agent 内部抛出 Interrupt 事件
+ if event.Action.Interrupted != nil {
+ ii, _ := json.MarshalIndent(event.Action.Interrupted.Data, "", "\t")
+ fmt.Printf("action: interrupted\n")
+ fmt.Printf("interrupt snapshot: %v", string(ii))
+ }
+ }
+}
+
+// 4. 从 stdin 接收用户输入
+scanner := bufio.NewScanner(os.Stdin)
+fmt.Print("\nyour input here: ")
+scanner.Scan()
+fmt.Println()
+nInput := scanner.Text()
+
+// 5. 携带用户输入信息,从断点恢复执行
+iter, err := runner.Resume(ctx, "1", adk.WithToolOptions([]tool.Option{subagents.WithNewInput(nInput)}))
+```
+
+# 快速开始
+
+## 安装
+
+```go
+go get github.com/cloudwego/eino@latest
+```
+
+## 项目开发经理智能体
+
+下面的示例使用 Eino ADK 构建了一个项目开发经理智能体,面向多方面管理协同的场景:
+
+- Project Manager Agent:项目经理智能体,整体使用 Supervisor 模式,各 Agent 的功能如下:
+ - `ResearchAgent`:调研 Agent,负责调研并生成可行方案,支持中断后从用户处接收额外的上下文信息来提高调研方案生成的准确性。
+ - `CodeAgent`:编码 Agent,使用知识库工具,召回相关知识作为参考,生成高质量的代码。
+ - `ReviewAgent`:评论 Agent,使用顺序工作流编排问题分析、评价生成、评价验证三个步骤,对调研结果 / 编码结果进行评审,给出合理的评价,供项目经理进行决策。
+ - `ProjectManagerAgent`:项目经理 Agent,根据动态的用户输入,路由并协调多个负责不同维度工作的子智能体开展工作。
+- 该 Agent 可能的工作场景为:
+ - **从零开始实现项目**:项目经理从需求入手,经由调研、编码、评论三个 Agent 工作,最终完成项目交付。
+ - **对已有项目的完善**:项目经理从评论 Agent 获得项目仍旧需要完善的功能点,交由编码 Agent 进行实现,再交由评论 Agent 对修改后的代码进行评审。
+ - **开展技术调研**:项目经理要求调研 Agent 生成技术调研报告,然后由评论 Agent 给出评审意见。调用方结合返回的技术调研报告和评审意见,决定后续动作。
+
+
+
+该示例的设计涵盖了文中介绍的大部分概念,您可以基于示例回顾之前的提到的种种设计理念。另外,请试想普通开发模式下如何完成该示例的编写,ADK 的优势便立刻凸显了出来:
+
+| 设计点 | 传统开发模式 | 基于 Eino ADK 开发 |
| Agent 抽象 | 没有统一定义,团队协作开发效率差,后期维护成本高 | 统一定义,职责独立,代码整洁,便于各 Agent 分头开发 |
| 输入输出 | 没有统一定义,输入输出混乱运行过程只能手动加日志,不利于调试 | 有统一定义,全部基于事件驱动运行过程通过 iterator 透出,所见即所得 |
| Agent 协作 | 通过代码手动传递上下文 | 框架自动传递上下文 |
| 中断恢复能力 | 需要从零开始实现,解决序列化与反序列化、状态存储与恢复等问题 | 仅需在 Runner 中注册 CheckPointStore 提供断点数据存储介质 |
| Agent 模式 | 需要从零开始实现 | 多种成熟模式开箱即用 |
diff --git a/docs/Eino/docs/overview/eino_adk_excel_agent.md b/docs/Eino/docs/overview/eino_adk_excel_agent.md
new file mode 100644
index 0000000..7da9a7e
--- /dev/null
+++ b/docs/Eino/docs/overview/eino_adk_excel_agent.md
@@ -0,0 +1,541 @@
+---
+Description: ""
+date: "2025-12-02"
+lastmod: ""
+tags: []
+title: 用 Eino ADK 构建你的第一个 AI 智能体:从 Excel Agent 实战开始
+weight: 7
+---
+
+## 从 Excel Agent 详解 Eino ADK
+
+本文将会向您介绍如何利用 **Eino ADK** (**Agent Development Kit**) 构建一个强大的多智能体系统,往期 Eino ADK 介绍链接:[Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统](https://mp.weixin.qq.com/s/ffGjlDEzEzroo8w6knlLqw)
+
+示例以 Excel Agent 这个实际业务场景为基础,Excel Agent 是一个能够“听懂你的话、看懂你的表格、写出并执行代码”的智能助手。它把复杂的 Excel 处理工作拆解为清晰的步骤,通过自动规划、工具调用与结果校验,稳定完成各项 Excel 数据处理任务。
+
+接下来我们将从 Excel Agent 的完整架构与功能出发,向您展示该 Agent 是如何通过 Eino ADK 逐步搭建的,进而深入浅出的理解 Eino ADK 的核心设计特点,助您快速上手 Eino ADK,向构建自定义智能体与 AI 应用系统更进一步。
+
+本示例完整代码位于 [Github](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent),您可以随时浏览与下载。
+
+### Excel Agent 是什么?
+
+Excel Agent 是一个“看得懂 Excel 的智能助手”,它先把问题拆解成步骤,再一步步执行并校验结果。它能理解用户问题与上传的文件内容,提出可行的解决方案,并选择合适的工具(系统命令、生成并运行 Python 代码、网络查询等等)完成任务。
+
+Excel Agent 整体是基于 Eino ADK 实现的 Multi-Agent 系统,完整架构如下图所示:
+
+
+
+Excel Agent 内部包含的几个 Agent 功能分别为:
+
+- **Planner**:分析用户输入,拆解用户问题为可执行的计划
+- **Executor**:正确执行当前计划中的首个步骤
+- **CodeAgent**:接收来自 Executor 的指令,调用多种工具(例如读写文件,运行 python 代码等)完成任务
+- **WebSearchAgent**:接收来自 Executor 的指令,进行网络搜索
+- **Replanner**:根据 Executor 执行的结果和现有规划,决定继续执行、调整规划或完成执行
+- **ReportAgent**:根据运行过程与结果,生成总结性质的报告
+
+### Excel Agent 的典型使用场景
+
+在真实业务里,你可以把 Excel Agent 当成一位“Excel 专家 + 自动化工程师”。当你交付一个原始表格和目标描述,它会给出方案并完成执行:
+
+- **数据清理与格式化**:从一个包含大量数据的 Excel 文件中完成去重、空值处理、日期格式标准化操作。
+- **数据分析与报告生成**:从销售数据中提取每月的销售总额,聚合统计、透视,最终生成并导出图表报告。
+- **自动化预算计算**:根据不同部门的预算申请,自动计算总预算并生成部门预算分配表。
+- **数据匹配与合并**:将多个不同来源的客户信息表进行匹配合并,生成完整的客户信息数据库。
+
+Excel Agent 的完整运行动线为:
+
+
+
+> 💡
+> **核心收益**:
+>
+> - **更少的人工操作**,把复杂繁琐的 Excel 处理工作交给 Agent 自动完成。
+> - **更稳定的产出质量**,通过“规划—执行—反思”闭环减少漏项与错误。
+> - **更强的可扩展性**,各 Agent 独立构建,低耦合利于迭代更新。
+
+Excel Agent 既可以单独使用,也可以作为子 Agent,集成在一个复合的多专家系统中,由外部路由到此 Agent 上,解决 excel 领域相关的问题。
+
+下面我们将逐步拆解 Excel Agent,深入了解 Eino ADK 的核心设计特点,以及如何利用这些特点构建高效、灵活的 AI 应用系统。
+
+### ChatModelAgent:与 LLM 交互的基石
+
+`ChatModelAgent` 是 Eino ADK 中的一个核心预构建的 Agent,内部使用了 [ReAct](https://react-lm.github.io/) 模式(一种让模型‘思考-行动-观察’的链式推理模式):
+
+
+
+`ChatModelAgent` 旨在让 ChatModel 进行显式的、一步一步的“思考”,结合思考过程驱动行动,观测历史思考过程与行动结果继续进行下一步的思考与行动,最终解决复杂问题:
+
+- 调用 ChatModel(Reason)
+- LLM 返回工具调用请求(Action)
+- ChatModelAgent 执行工具(Act)
+- 将工具结果返回给 LLM(Observation),结合之前的上下文继续生成,直到模型判断不需要调用工具后结束
+
+
+
+在 Excel Agent 中,每个 Agent 的核心都是这样一个 `ChatModelAgent`,以 Executor 运行【读取用户输入表格的头信息】这个步骤为例 ,我们可以通过观察完整的运行过程来理解 ReAct 模式在 `ChatModelAgent` 中的表现:
+
+1. Executor:经过判断,将任务转交给 CodeAgent 运行
+2. CodeAgent:接收到任务【读取用户输入表格的头信息】
+ 1. **Think-1**:上下文未提供工作目录下的所有文件,需要查看
+ 2. **Act-1**: 调用 Bash 工具,ls 查看工作目录下的所有文件
+ 3. **Think-2**: 找到了用户输入的文件,判断需要编写 Python 代码读取 xlsx 表格的首行
+ 4. **Act-2**: 调用 PythonRunner 工具,书写代码并运行,获取运行结果
+ 5. **Think-3**: 获取到了 xlsx 首行,判断任务完成
+3. 运行完成,将表格头信息返回给 Executor
+
+### Plan-Execute Agent:基于「规划-执行-反思」的多智能体协作框架
+
+Plan-Execute Agent 是 Eino ADK 中一种基于「规划-执行-反思」范式的多智能体协作框架,旨在解决复杂任务的分步拆解、执行与动态调整问题。它通过 **Planner(规划器)**、**Executor(执行器)**和 **Replanner(重规划器)** 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态 replanning,最终达成用户目标:
+
+```go
+// 完整代码: https://github.com/cloudwego/eino/blob/main/adk/prebuilt/planexecute/plan_execute.go
+
+// NewPlanner creates a new planner agent based on the provided configuration.
+func NewPlanner(_ context.Context, cfg *PlannerConfig) (adk.Agent, error)
+
+// NewExecutor creates a new executor agent.
+func NewExecutor(ctx context.Context, cfg *ExecutorConfig) (adk.Agent, error)
+
+// NewReplanner creates a new replanner agent.
+func NewReplanner(_ context.Context, cfg *ReplannerConfig) (adk.Agent, error)
+
+// New creates a new plan-execute-replan agent with the given configuration.
+func New(ctx context.Context, cfg *Config) (adk.Agent, error)
+```
+
+
+
+而 Excel Agent 的核心能力恰好为【解决用户在 excel 领域的问题】,与该智能体协作框架定位一致:
+
+- **规划者**(**Planner**):明确目标,自动拆解可执行步骤
+- **执行者(Executor)**:调用工具(Excel 读取、系统命令、Python 代码)完成规划中的每一个详细步骤
+- **反思者(Replanner)**:根据执行进度决定继续、调整规划或结束
+
+Planner 和 Replanner 会将用户模糊的指令拆解为清晰的、可执行的步骤清单,即包含多个步骤(Step)的计划(Plan),Eino ADK 为此提供了灵活的 Plan 接口定义,支持用户自定义 Plan 结构与细节:
+
+```go
+type Plan interface {
+ // FirstStep returns the first step to be executed in the plan.
+ FirstStep() string
+ // Marshaler serializes the Plan into JSON.
+ // The resulting JSON can be used in prompt templates.
+ json.Marshaler
+ // Unmarshaler deserializes JSON content into the Plan.
+ // This processes output from structured chat models or tool calls into the Plan structure.
+ json.Unmarshaler
+}
+```
+
+默认情况下,框架会使用内置的 Plan 结构作为兜底配置,例如下面就是 Excel Agent 产生的一个完整运行计划:
+
+```sql
+### 任务计划
+- [x] 1. Read the contents of '模拟出题.csv' from the working directory into a pandas DataFrame.
+- [x] 2. Identify the question type (e.g., multiple-choice, short-answer) for each row in the DataFrame.
+- [x] 3. For non-short-answer questions, restructure the data to place question, answer, explanation, and options in the same row.
+- [x] 4. For short-answer questions, merge the answer content into the explanation column and ensure question and merged explanation are in the same row.
+- [x] 5. Verify that all processed rows have question, answer (where applicable), explanation, and options (where applicable) in a single row with consistent formatting.
+- [x] 6. Generate a cleaned report presenting the formatted questions with all relevant components (question, answer, explanation, options) in unified rows.
+```
+
+### Workflow Agents:可控的多 Agent 运行流水线
+
+Excel Agent 中,存在一些需要按照特定顺序运行 agent 的情况:
+
+1. **顺序运行**:先运行 Planner,再运行 Executor 和 Replanner;Planner 只运行一次。
+2. **循环运行**:Executor 和 Replanner 需要按需循环运行多次,每次循环运行都是先运行 Executor 后运行 Replanner
+3. **顺序运行**:Plan-Executor 整体运行完后,固定运行一次 ReportAgent 进行总结。
+
+对于这些拥有固定执行流程的场景,Eino ADK 提供了三种流程编排方式,协助用户快速搭建可控的工作流:
+
+- **SequentialAgent**:按照配置中提供的顺序,依次执行一系列子 Agent。每个子 Agent 执行完成后,其输出会通过 History 机制传递给下一个子 Agent,形成一个线性的执行链。
+
+ ```go
+ import github.com/cloudwego/eino/adk
+
+ // 依次执行 制定研究计划 -> 搜索资料 -> 撰写报告
+ sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ Name: "research_pipeline",
+ SubAgents: []adk.Agent{
+ planAgent, // 制定研究计划
+ searchAgent, // 搜索资料
+ writeAgent, // 撰写报告
+ },
+ })
+ ```
+
+
+
+- **LoopAgent**:重复执行配置的子 Agent 序列,直到达到最大迭代次数或某个子 Agent 产生 ExitAction,每次迭代的结果都会累积,后续迭代的输入可以访问所有历史信息。LoopAgent 基于 SequentialAgent 实现。
+
+ ```go
+ import github.com/cloudwego/eino/adk
+
+ // 循环执行 5 次,每次顺序为:分析当前状态 -> 提出改进方案 -> 验证改进效果
+ loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
+ Name: "iterative_optimization",
+ SubAgents: []adk.Agent{
+ analyzeAgent, // 分析当前状态
+ improveAgent, // 提出改进方案
+ validateAgent, // 验证改进效果
+ },
+ MaxIterations: 5,
+ })
+ ```
+
+
+
+- **ParallelAgent**:允许多个子 Agent 基于相同的输入上下文并发执行。所有子 Agent 接收相同的初始输入,各自在独立的 goroutine(Go 语言中一种轻量级的并发执行单元) 运行,最终收集所有子 Agent 的执行结果并按顺序输出到 `AsyncIterator` 中。
+
+ ```go
+ import github.com/cloudwego/eino/adk
+
+ // 并发执行 情感分析 + 关键词提取 + 内容摘要
+ parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
+ Name: "multi_analysis",
+ SubAgents: []adk.Agent{
+ sentimentAgent, // 情感分析
+ keywordAgent, // 关键词提取
+ summaryAgent, // 内容摘要
+ },
+ })
+ ```
+
+
+
+### Agent 抽象:灵活定义 Agent 的基础
+
+Eino ADK 的核心是一个简洁而强大的 Agent 接口,每个 Agent 都有明确的身份(Name)、清晰的职责(Description)和标准化的执行方式(Run),为 Agent 之间的发现与调用提供了基础。无论是简单的问答机器人,还是复杂的多步骤任务处理系统,都可以通过这个统一的接口加以实现。
+
+- **统一的 Agent 抽象**:ADK 提供的预构建 Agent(ChatModelAgent,Plan-Execute Agent,Workflow Agents)都遵循该接口定义。您也可以基于该接口,书写自定义 Agent,完成定制化需求。
+
+ ```go
+ type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+ }
+ ```
+- **标准化输入**:Agent 通常以 LLM 为核心,因此 Eino ADK 定义的 Agent 的输入与 LLM 接收的输入一致:
+
+ ```go
+ type AgentInput struct {
+ Messages []Message
+ EnableStreaming bool
+ }
+
+ type Message = *schema.Message // *schema.Message 是模型输入输出的结构定义
+ ```
+- **异步事件驱动输出**:Agent 的输出是一个 AgentEvent 的异步迭代器,其中的 AgentEvent 表示 Agent 在其运行过程中产生的核心事件数据。其中包含了 Agent 的元信息、输出、行为和报错信息:
+
+ ```go
+ type AgentEvent struct {
+ AgentName string // 产生 Event 的 Agent 名称(框架自动填充)
+
+ RunPath []RunStep // 到达当前 Agent 的完整运行轨迹(框架自动填充)
+
+ Output *AgentOutput // Agent 输出消息内容
+
+ Action *AgentAction // Agent 动作事件内容
+
+ Err error // Agent 报错
+ }
+
+ type AgentOutput struct {
+ MessageOutput *MessageVariant // 模型消息输出内容
+
+ CustomizedOutput any // 自定义输出内容
+ }
+
+ type MessageVariant struct {
+ IsStreaming bool // 是否为流式输出
+
+ Message Message // 非流式消息输出
+ MessageStream MessageStream // 流式消息输出
+
+ Role schema.RoleType // 消息角色
+ ToolName string // 工具名称
+ }
+
+ type AgentAction struct {
+ Exit bool // Agent 退出
+
+ Interrupted *InterruptInfo // Agent 中断
+
+ TransferToAgent *TransferToAgentAction // Agent 跳转
+
+ CustomizedAction any // 自定义 Agent 动作
+ }
+ ```
+
+异步迭代器允许 Agent 在运行过程中的任意时刻向迭代器发送消息(Agent 调用模型结果、工具运行结果、中间状态等等),同时调用方以一种有序、阻塞的方式消费这一系列事件:
+
+```go
+iter := myAgent.Run(ctx, "hello") // get AsyncIterator
+
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ // handle event
+}
+```
+
+### Agent 协作:隐藏在 Agent 后的数据传递
+
+Excel Agent 架构图中的节点代表每个具体的 Agent,边代表了数据流通与任务转移。在构建多 Agent 系统时,让不同 Agent 之间高效、准确地共享信息至关重要。
+
+这些信息不仅包含 Agent 的输入输出,还有全局的、部分可见的种种额外信息,例如:
+
+- Executor 执行需要从 Planner / Replanner 拿到一个结构化的、可被拆分为详细步骤(Step)的计划(Plan),而非一段非结构化的 LLM 原始输出消息。
+- ReportAgent 需要拿到完整的运行计划、运行过程与运行产物才能正确产生报告。
+
+Eino ADK 包含两种基础的数据传递机制:
+
+- **History**:每一个 Agent 产生的 AgentEvent 都会被保存到这个隐藏的 History 中,调用一个新 Agent 时 History 中的 AgentEvent 会被转换并拼接到 AgentInput 中。默认情况下,其他 Agent 的 Assistant 或 Tool Message,被转换为 User Message,这相当于在告诉当前的 LLM:“刚才, Agent_A 调用了 some_tool ,返回了 some_result 。现在,轮到你来决策了。”。 通过这种方式,其他 Agent 的行为被当作了提供给当前 Agent 的“外部信息”或“事实陈述”,而不是它自己的行为,从而避免了 LLM 的上下文混乱。
+
+
+
+- **共享 Session**:单次运行过程中持续存在的 KV 存储,用于支持跨 Agent 的状态管理和数据共享,一次运行中的任何 Agent 可以在任何时间读写 SessionValues。以 Plan-Execute Agent 模式为例,Planner 生成首个计划并写入 Session;Executor 从 Session 读取计划并执行;Replanner 从 Session 读取当前计划后,结合运行结果,将更新后的计划写回 Session 覆盖当前的计划。
+
+ ```go
+ // Agent 内获取全部 SessionValues
+ func GetSessionValues(ctx context.Context) map[string]any
+
+ // Agent 内指定 key 获取 SessionValues 中的值
+ func GetSessionValue(ctx context.Context, key string) (any, bool)
+
+ // Agent 内添加 SessionValues
+ func AddSessionValue(ctx context.Context, key string, value any)
+
+ // Agent 内批量添加 SessionValues
+ func AddSessionValues(ctx context.Context, kvs map[string]any)
+
+ // WithSessionValues 在 Agent 运行前由外部注入 SessionValues
+ func WithSessionValues(v map[string]any) AgentRunOption
+ ```
+
+
+
+除了完善的 Agent 间数据传递机制,Eino ADK 从实践出发,提供了多种 Agent 协作模式:
+
+- **预设 Agent 运行顺序(Workflow)**:以代码中预设好的流程运行, Agent 的执行顺序是事先确定、可预测的。对应 Workflow Agents 章节提到的三种范式。
+- **移交运行(Transfer)**:携带本 Agent 输出结果上下文,将任务移交至子 Agent 继续处理。适用于智能体功能可以清晰的划分边界与层级的场景,常结合 ChatModelAgent 使用,通过 LLM 的生成结果进行动态路由。结构上,以此方式进行协作的两个 Agent 称为父子 Agent:
+
+
+
+```go
+// 设置父子 Agent 关系
+func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error)
+
+// 指定目标 Agent 名称,构造 Transfer Event
+func NewTransferToAgentAction(destAgentName string) *AgentAction
+```
+
+- **显式调用(ToolCall)**:将 Agent 视为工具进行调用,适用于 Agent 运行仅需要明确清晰的参数而非完整运行上下文的场景。常结合 ChatModelAgent,将 Agent 作为工具运行后将结果返回给 ChatModel 继续处理。除此之外,ToolCall 同样支持调用符合工具接口构造的、不含 Agent 的普通工具。
+
+
+
+```go
+// 将 Agent 转换为 Tool
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+## Excel Agent 示例运行
+
+### 配置环境与输入输出路径
+
+- 环境变量:Excel Agent 运行依赖的完整环境变量可参考项目 README。
+- 运行输入:包括一段用户需求描述和待处理的一系列文件,其中:
+
+ - `main.go` 中首行表示用户输入的需求描述,可自行修改:
+
+ ```go
+ func main() {
+ // query := schema.UserMessage("统计附件文件中推荐的小说名称及推荐次数,并将结果写到文件中。凡是带有《》内容都是小说名称,形成表格,表头为小说名称和推荐次数,同名小说只列一行,推荐次数相加")
+ // query := schema.UserMessage("读取模拟出题.csv 中的内容,规范格式将题目、答案、解析、选项放在同一行,简答题只把答案写入解析即可")
+ query := schema.UserMessage("请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中")
+ }
+ ```
+ - `adk/multiagent/integration-excel-agent/playground/input` 为默认的附件输入路径,附件输入路径支持配置,参考 README。
+ - `adk/multiagent/integration-excel-agent/playground/test_data` 路径下提供了几个示例文件,您可以将文件复制到附件输入路径下来进行测试运行:
+
+ ```go
+ % tree adk/multiagent/integration-excel-agent/playground/test_data
+ adk/multiagent/integration-excel-agent/playground/test_data
+ ├── questions.csv
+ ├── 推荐小说.txt
+ └── 模拟出题.csv
+
+ 1 directory, 3 files
+ ```
+- 运行输出:Excel Agent 输入的附件、运行的中间产物与最终结果都会放置在工作路径下:`adk/multiagent/integration-excel-agent/playground/${uuid}`,输出路径支持配置,参考 README。
+
+### 查看运行结果
+
+Excel Agent 单次运行会在输出路径下创建一个新的工作目录,并在该目录下完成任务,运行时产生的中间产物与最终结果都会写到该目录下。
+
+以 `请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中` 这个任务为例,运行完成后在工作目录下的文件包含:
+
+
+
+1. 原始输入:从输入路径获取到的 `question.csv`
+2. Planner / Replanner 给出的运行计划:`plan.md`
+
+ ```go
+ ### 任务计划
+ - [x] 1. {"desc":"Read the 'questions.csv' file into a pandas DataFrame."}
+ - [x] 2. Save the extracted first column to a new CSV file.
+ ```
+3. Executor 中的 CodeAgent 书写的代码:`$uuid.py`
+
+ ```go
+ import pandas as pd
+
+ df = pd.read_csv('questions.csv')
+ first_column = df.iloc[:, _0_]
+ first_column.to_csv('extracted_first_column.csv', index=_False_)
+ ```
+4. 运行中间产物:`extracted_first_column.csv` 和 `first_column.csv`
+
+ ```go
+ type
+ multiple-choice
+ ...
+ short-answer
+ ```
+5. 最终报告:`final_report.json`
+
+ ```json
+ {
+ "is_success": true,
+ "result": "Successfully extracted the first column from questions.csv and saved it to first_column.csv.",
+ "files": [
+ {
+ "path": "/User/user/go/src/github.com/cloudwego/eino-examples/adk/multiagent/integration-excel-agent/playground/00f118af-4bd8-42f7-8d11-71f2801218bd/first_column.csv",
+ "desc": "A CSV file containing only the first column data from the original questions.csv."
+ }
+ ]
+ }
+ ```
+
+### 运行过程输出
+
+Excel Agent 会将每个步骤的运行结果输出到日志中。下面仍以 `请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中` 这个任务为例,向您展示 Excel Agent 在运行过程中的几个关键步骤及其输出,并通过对步骤的解释,直观地呈现 Agent 的运行流程及其强大能力。:
+
+- Planner 生成 JSON 格式的初始计划
+
+ ```yaml
+ name: Planner
+ answer: {
+ **"steps"**: [
+ {
+ **"index"**: **1**,
+ **"desc"**: **"Read the 'questions.csv' file into a pandas DataFrame."**
+ },
+ {
+ **"index"**: **2**,
+ **"desc"**: **"Extract the first column from the DataFrame."**
+ },
+ {
+ **"index"**: **3**,
+ **"desc"**: **"Save the extracted first column to a new CSV file."**
+ }
+ ]
+ }
+ ```
+- Executor 将 CodeAgent 作为工具进行调用,执行计划中的首个步骤
+
+ ```yaml
+ name: Executor
+ tool name: CodeAgent
+ arguments: {"request":"Read the 'questions.csv' file into a pandas DataFrame using pandas. Use the pandas.read_csv function and store the result in a variable named df."}
+ ```
+- CodeAgent 使用 PythonRunner 工具运行代码,并使用 ReAct 模式自动纠错,修正代码中的错误
+
+ ```yaml
+ # CodeAgent 使用 PythonRunner 工具运行代码
+ name: Executor
+ tool name: PythonRunner
+ arguments: {"code":"```python\nfirst_column = df.iloc[:, 0]\n```"}
+
+ # PythonRunner 代码运行报错
+ name: Executor
+ tool response: Traceback (most recent call last):
+ File "/User/user/go/src/github.com/cloudwego/eino-examples/adk/multiagent/integration-excel-agent/playground/00f118af-4bd8-42f7-8d11-71f2801218bd/00f118af-4bd8-42f7-8d11-71f2801218bd.py", line 1, in
diff --git a/docs/Eino/docs/overview/eino_open_source.md b/docs/Eino/docs/overview/eino_open_source.md
new file mode 100644
index 0000000..62d6c25
--- /dev/null
+++ b/docs/Eino/docs/overview/eino_open_source.md
@@ -0,0 +1,187 @@
+---
+Description: ""
+date: "2025-12-01"
+lastmod: ""
+tags: []
+title: 大语言模型应用开发框架 —— Eino 正式开源!
+weight: 3
+---
+
+今天,经过字节跳动内部半年多的使用和迭代,基于 Golang 的大模型应用综合开发框架 —— Eino,已在 CloudWeGo 正式开源啦!
+
+Eino 基于明确的“组件”定义,提供强大的流程“编排”,覆盖开发全流程,旨在帮助开发者以最快的速度实现最有深度的大模型应用。
+
+你是否曾有这种感受:想要为自己的应用添加大模型的能力,但面对这个较新的领域,不知如何入手;想持续的站在研究的最前沿,应用最新的业界成果,但使用的应用开发框架却已经数月没有更新;想看懂项目里的用 Python 写的代码,想确定一个变量或者参数的类型,需要反复查看上下文确认;不确定模型生成的效果是否足够好,想用又不太敢用;在调试、追踪、评测等开发之外的必要环节,还需要额外探索学习其他配套的工具。如果是,欢迎了解和尝试 Eino,因为 Eino 作为旨在覆盖 devops 全流程的大模型应用开发框架,具有如下特点:
+
+- 内核稳定,API 简单易懂,有明确的上手路径,平滑的学习曲线。
+- 极致的扩展性,研发工作高度活跃,长期可持续。
+- 基于强类型语言 Golang,代码能看懂,易维护,高可靠。
+- 背靠字节跳动核心业务线的充分实践经验。
+- 提供开箱即用的配套工具。
+
+Eino 已成为字节跳动内部大模型应用的首选全代码开发框架,已有包括豆包、抖音、扣子等多条业务线、数百个服务接入使用。
+
+项目地址:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino),[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+
+未来,我们将以 Eino 开源库为核心代码仓库,坚持**内外用一套代码**,与社区共建最优秀的大模型应用开发框架。
+
+## 快速认识 Eino
+
+Eino 是覆盖 devops 全流程的大模型应用开发框架,从最佳实践样例的 Eino Examples,到各环节的工具链,都是 Eino 的领域:
+
+
+
+那么 Eino 具体能做什么?首先,Eino 由一个个大模型领域的“**组件**”组成,比如最核心的是与大模型交互的 Chat Model:
+
+```go
+model, _ := ark.NewChatModel(ctx, config) // 创建一个豆包大模型
+message, _ := model.Generate(ctx, []*Message{
+ SystemMessage("you are a helpful assistant."),
+ UserMessage("what does the future AI App look like?")}
+```
+
+像上面这样一个个的直接使用组件,当然没问题,Eino 提供了大量有用的组件实现供选择。但是,大模型应用有它们自身的特点和规律,比如:
+
+- 核心是大模型,业务逻辑围绕“如何给大模型充分、有效的上下文”以及“如何让大模型的输出可靠的影响环境”,核心的组件类型、数据类型和交互模式是可以枚举的,整体可以由有向图来描述。
+- 大模型输出的特点是流式输出,意味着模型的下游都需要有效的处理流式数据,包括流的实时处理、流的复制、多个流的合并、单个流的拼接等。
+- 以有向图为基础,衍生出并发处理、扇入扇出、通用横切面、option 分配等一系列子问题。
+
+Eino 的编排能力,是上述通用问题的充分解决方案。
+
+以 ReAct Agent 为例:一个 ChatModel(大模型),“绑定”了 Tool(工具),接收输入的 Message,由 ChatModel 自主判断是否调用 Tool 或输出最终结果。Tool 执行结果会再次成为给到 ChatModel 的 Message,并作为下一轮自主判断的上下文。
+
+
+
+上述基于 ChatModel 进行自主决策和选路的 ReAct Agent,便是基于 Eino 的 组件 和 Graph 编排 来实现, 代码清晰简洁,可与流程图清晰对应。
+
+- 代码实现详见:[flow/agent/react](https://github.com/cloudwego/eino/blob/main/flow/agent/react/react.go) 的实现
+- ReAct Agent 用户手册详见:[react_agent_manual](https://www.cloudwego.io/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual/)
+
+在 Eino 中,这是几十行代码的图编排:
+
+```go
+// 构建一个 ReAct Agent,编译为一个输入为 []*Message,输出为 *Message 的 Runnable
+
+// 创建包含 state 的 Graph,用户存储请求维度的 Message 上下文
+graph = NewGraph[[]*Message, *Message](
+ WithGenLocalState(func(ctx context.Context) *state {
+ return &state{Messages: make([]*Message, 0, config.MaxStep+1)}
+ }))
+
+// 将一个轮次中的上下文和响应,存储到 Graph 的临时状态中
+modelPreHandle = func(ctx context.Context, input []*Message, state *state) ([]*Message, error) {
+ state.Messages = append(state.Messages, input...)
+ return state.Messages, nil
+}
+
+_ = graph.AddChatModelNode(nodeKeyModel, chatModel, WithStatePreHandler(modelPreHandle))
+
+_ = graph.AddEdge(START, nodeKeyModel)
+
+_ = graph.AddToolsNode(nodeKeyTools, toolsNode)
+
+// chatModel 的输出可能是多个 Message 的流
+// 这个 StreamGraphBranch 根据流的首个包即可完成判断,降低延迟
+modelPostBranch = NewStreamGraphBranch(
+ func(_ context.Context, sr *schema.StreamReader[*Message]) (endNode string, err error) {
+ defer sr.Close()
+
+ if msg, err := sr.Recv(); err != nil {
+ return "", err
+ } else if len(msg.ToolCalls) == 0 {
+ return END, nil
+ }
+
+ return nodeKeyTools, nil
+ }, map[string]bool{nodeKeyTools: true, END: true})
+
+_ = graph.AddBranch(nodeKeyModel, modelPostBranch)
+
+// toolsNode 执行结果反馈给 chatModel
+_ = graph.AddEdge(nodeKeyTools, nodeKeyModel)
+
+// 编译 Graph:类型检查、callback 注入、自动流式转换、生成执行器
+agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))
+```
+
+在上面这几十行代码的背后,Eino 自动做了一些事情:
+
+- 类型检查,在 compile 时确保相邻的节点的类型对齐。
+- 流式封装,编译出的 Runnable 既可以 Invoke 调用,也可以 Stream 调用,无论内部的 Tool 是否支持流。
+- 并发管理,对 state 这个公共状态的读写是并发安全的。
+- 横切面注入,如果某个组件(比如一个 tool)没有实现 callbacks 注入,则 Eino 自动注入。
+- Option 分配,编译出的 Runnable 可以灵活接收并把 option 分配给指定的节点。
+
+## Eino 的独特优势
+
+基于大语言模型的软件应用正处于快速发展阶段,新技术、新思路、新实践不断涌现,我们作为应用开发者,一方面需要高效、可靠的把业界共识的最佳实践应用起来,另一方面需要不断学习和提升认知,从而能够整体理解这个新领域的可能性。因此,一个优秀的大模型应用开发框架,既需要**封装领域内“不变”的通用核心要素**,又需要基于最新进展**敏捷的横向和纵向扩展**。
+
+另一方面,目前较为主流的框架如 LangChain,LlamaIndex 等,都基于 Python,虽然能借助 Python 较为丰富的生态快速实现多样的功能,但是同时也继承了 Python 作为动态语言所带来的“弱类型检验”和“长期维护成本高”等问题。在大模型应用快速进入大规模线上运行阶段的当下,基于 Golang 这一强类型语言而实现的**高可靠性**和**高可维护性**,逐渐具有更大的价值。
+
+基于大模型的应用开发是相对较新的领域,有时需要摸着石头过河,靠实践来检验认知。依托字节跳动高频应用豆包、抖音等的多样场景、快速迭代和海量反馈,Eino 在**实践驱动设计**方面有独特的优势。
+
+最后,生产级的框架需要面对真实、复杂的业务场景,因此,除了直观易用的 API 设计之外,提供有针对性设计的开发**工具**可以有效的帮助开发者理解和应对复杂性、加速开发过程。
+
+### 内核稳定
+
+我们认为,存在一个常见的组件列表,共同构成了大模型应用的常见组成部分。每类组件作为一个 interface,有完善、稳定的定义:具体的输入输出类型,明确的运行时 option,以及明确的流处理范式。
+
+在明确的组件定义基础之上,我们认为,大模型应用开发存在通用的基座性质的能力,包括但不限于:处理模型输出的流式编程能力;支持横切面功能以及透出组件内部状态的 Callback 能力;组件具体实现超出组件 interface 定义范围的 option 扩展能力。
+
+在组件定义和通用基座能力的基础上,我们认为,大模型应用开发存在相对固定的数据流转和流程编排范式:以 ChatModel(大模型)为核心,通过 ChatTemplate 注入用户输入和系统 prompt,通过 Retriever、Document Loader & Transformer 等注入上下文,经过 ChatModel 生成,输出 Tool Call 并执行,或输出最终结果。基于此,Eino 提供了上述组件的不同编排范式:Chain,链式有向无环图;Graph,有向图或有向无环图;Workflow,有字段映射能力的有向无环图。
+
+上述设计和功能共同构成了 Eino 的稳定内核:
+
+
+
+### 敏捷扩展
+
+每类组件都可以横向扩展出不同的实现,比如 ChatModel 组件可以有 OpenAI、Gemini、Claude 等不同的实现等。这些具体的实现,在实现组件 interface 从而可作为组件参与编排的基础上,可以实现和持续扩展自身的特殊功能。
+
+当实际业务场景中,出现需要进入编排但是不对应任何组件定义的功能时,Eino 支持将自定义 function 声明为 Lambda 类型。Lambda 有用户声明的输入输出以及 option 类型,可支持全部的流处理范式,具备完整的 Callback 能力,在编排视角等价于官方组件。
+
+在大模型应用开发领域,存在并且持续会涌现多个组件的特定编排范式,这些范式封装了验证有效的研究成果或实践经验,比如 ReAct Agent,Host Multi-Agent 等。这些开箱即用的封装,浓缩了大模型应用开发领域的最佳实践,会随着我们认知的提升持续纵向扩展。
+
+在组件和图执行过程中,开发者可以在固定的时机嵌入自定义的回调逻辑,用于注入横切面功能。
+
+综上所述,Eino 框架具备充分的可扩展性:
+
+
+
+### 高可靠易维护
+
+基于 Golang 写 Eino 代码时,开发者可以充分利用 Golang 的强类型特性,为所有的组件、Lambda、编排产物等声明具体类型。这像是为代码绘制了一幅精确的地图,开发者可以沿着清晰的路径进行维护和扩展,即使在项目规模不断扩大、功能持续迭代的情况下,依然能够保有较高的可维护性。
+
+同时,Eino 编排能力也充分利用了强类型系统的编译时校验能力,尽可能将类型匹配问题暴露的时机提前到 graph 的编译时,而不是 graph 的运行时。尽早并明确的暴露类型匹配问题,有助于开发者迅速定位和修复,减少因类型错误在运行时引发的难以排查的故障和性能问题。
+
+另一方面,Eino 遵循模块化设计,核心库以及各组件实现是单独的 go module,每个 go module 做到依赖最小化。同时,API 设计以“精简”、"直观"和“同构性”为原则,辅以由浅入深的全面文档,尽可能让学习曲线更平滑。最重要的是,Eino 采用清晰的分层设计,每层职责明确、功能内聚,在提升维护性的同时能更好的保证稳定性。
+
+Eino 框架结构图:
+
+
+
+### 实践驱动
+
+Eino 框架的设计开发过程,扎根于 “满足真实需求” 与 “实践驱动设计” 这两大基石之上。功能的演进过程与字节跳动各业务线的接入过程紧密结合,始终倾听开发者的声音,并通过实际使用效果来检验设计的合理性。比如我们收到来自抖音的“希望能够以字段为粒度在图中映射和传递数据”的需求,以此为基础设计了 Workflow;倾听来自豆包的使用痛点,增强作为模型输入输出类型的 Message 结构体。在未来的开源生态共建过程中,我们会继续坚持上述原则,满足更广大的用户和开发者的真实需求,并在更大的范围内认真实践和精进。
+
+
+
+### 工具生态
+
+链路追踪、调试、可视化,是编排引擎的三个重要辅助工具。Eino 内置了 tracing callback,并与 APMPlus 和 Langfuse 平台做了集成。同时提供了 IDE 插件,可以在写代码的过程中随时可视化查看编排出的 graph,并进行调试运行,甚至可以通过 UI 拖拽的方式快速构建 graph 并导出为 Eino 代码。
+
+## 快速上手
+
+针对 Eino 的学习和使用,我们提供了完善的 Eino 用户手册,帮助大家快速理解 Eino 中的概念,掌握基于 Eino 开发设计 AI 应用的技能,赶快通过「[Eino: 快速开始](https://www.cloudwego.io/zh/docs/eino/quick_start/)」尝试使用吧~。
+
+如有任何问题,可通过下方的飞书群或者 [Eino Issues](https://github.com/cloudwego/eino/issues) 和我们沟通、反馈~
+
+## 相关链接
+
+项目地址:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino),[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+
+项目官网:__[https://www.cloudwego.io](https://www.cloudwego.io)__
+
+扫描二维码加入飞书社群:
+
+
diff --git a/docs/Eino/docs/overview/graph_or_agent.md b/docs/Eino/docs/overview/graph_or_agent.md
new file mode 100644
index 0000000..96d9c21
--- /dev/null
+++ b/docs/Eino/docs/overview/graph_or_agent.md
@@ -0,0 +1,348 @@
+---
+Description: ""
+date: "2026-03-24"
+lastmod: ""
+tags: []
+title: Agent 还是 Graph?AI 应用路线辨析
+weight: 8
+---
+
+## 引言:两种并存的 AI 交互范式
+
+许多应用程序的界面都集成了不同形态的 AI 功能,如下图所示:
+
+
+
+这张看似简单的截图,代表了“AI 应用”的两种形态:
+
+- 以“聊天框”为代表性标志的“Agent(智能体)”。**Agent 以 LLM(大语言模型)为决策中心,自主规划并能进行多轮交互**,天然适合处理开放式、持续性的任务,表现为一种“对话”形态。
+- 以“按钮”或者“API”为代表性标志的“Graph(流程图)”。比如上面的“录音纪要”这个“按钮”,其背后的 Graph 大概是“录音”-》“LLM 理解并总结” -》“保存录音”这种固定流程。**Graph 的核心在于其流程的确定性与任务的封闭性**,通过预定义的节点和边来完成特定目标,表现为一种“功能”形态。举个例子,视频生成是 “API”形态 AI 应用:
+
+
+
+```mermaid
+flowchart TD
+ linkStyle default stroke-width:2px,stroke:#000000
+
+ classDef startend_style fill:#EAE2FE,stroke:#000000,stroke-width:2px,color:#1f2329
+ classDef process_style fill:#F0F4FC,stroke:#000000,stroke-width:2px,color:#1f2329
+ classDef decision_style fill:#FEF1CE,stroke:#000000,stroke-width:2px,color:#1f2329
+ classDef subgraph_style fill:#f5f5f5,stroke:#bbbfc4,stroke-width:1px,color:#000000
+
+ S(["AI 应用形态"])
+ D{"任务特征"}
+ A("Agent")
+ G("Graph")
+ A1("LLM 决策中心")
+ A2("多轮交互")
+ G1("预设拓扑结构")
+ G2("确定性输出")
+
+ S --> D
+ D -->|"开放式或不确定"| A
+ D -->|"封闭且确定"| G
+ A --> A1
+ A --> A2
+ G --> G1
+ G --> G2
+
+ class S startend_style
+ class D decision_style
+ class A,G,A1,A2,G1,G2 process_style
+```
+
+本文详细探讨了 Agent 和 Graph 两种 AI 应用形态的区别和联系,提出“两者的最佳结合点,在于将 Graph 封装为 Agent 的 Tool(工具)”,并为 [Eino](https://github.com/cloudwego/eino) 开发者给出建议的使用姿势。
+
+## 核心概念辨析
+
+### 基础定义
+
+- **Graph**: 一个由开发者**预先定义**的、具有明确拓扑结构的流程图。它的节点可以是代码函数、API 调用或 LLM,输入和输出通常是结构化的。**核心特征是“确定性”**,即给定相同输入,其执行路径和最终产出是可预测的。
+- **Agent**: 一个以 LLM 为核心,能够**自主规划、决策和执行**任务的实体。它通过与环境(Tool、用户、其他 Agent)的**动态交互**来完成目标,其行为具有不确定性。**核心特征是“自主性”**。
+- **Tool**: Agent 可以调用的任何外部能力,通常是一个**封装了特定功能的函数或 API**。Tool 本身可以是同步或异步的、有状态或无状态的。它只负责执行,不具备自主决策能力。
+- **编排**: **组织和协调多个计算单元(节点、Agent)协同工作**的过程。在本文中,特指通过 Graph 的方式来预定义静态流程。
+
+### 深度对比
+
+| 特征维度 | Agent | Graph |
| 核心驱动力 | LLM 自主决策 | 开发者预设流程 |
| 输入 | 非结构化的自然语言、图像等 | 结构化的数据 |
| 交付物 | 过程与结果并重 | 聚焦最终结果 |
| 状态管理 | 长时程、跨执行 | 单次执行、stateless |
| 运行模式 | 偏向异步 | 偏向同步 |
| 特征维度 | Graph | Tool |
| 输入 | 结构化的数据 | 结构化的数据 |
| 交付物 | 聚焦最终结果 | 聚焦最终结果 |
| 状态管理 | 单次执行、stateless | 单次执行、stateless |
| 运行模式 | 整体是同步 | LLM 的视角 Tool 是同步的 |
| +🔧 灵活的中间件架构 +全新 ChatModelAgentMiddleware 接口 | +📊 增强的可观测性 +Agent 级别 Callback 支持 |
| 特性 | AgentMiddleware | ChatModelAgentMiddleware |
| 扩展性 | 封闭 | 开放,可实现自定义 handler |
| Context 传播 | 回调只返回 error | 所有方法返回 (ctx, ..., error) |
| 配置管理 | 分散在闭包中 | 集中在结构体字段中 |
| 阶段 | 触发时机 | 作用 |
| 截断 (Truncation) | 工具返回后 | 截断超长输出,保存到文件 |
| 清理 (Clear) | 模型调用前 | 清理历史工具结果,释放 Token |
| 类型 | 变更项 |
| API 变更 | ShellBackend→ Shell接口重命名 |
| 行为变更 | AgentEvent发送机制改为 Middleware |
| 行为变更 | ReadRequest.Offset从 0-based 改为 1-based |
| 行为变更 | FileInfo.Path不再保证为绝对路径 |
| 行为变更 | WriteRequest文件存在时从报错改为覆盖 |
| 行为变更 | GrepRequest.Pattern从字面量改为正则表达式 |
| 版本类型 | 版本号格式 | 版本说明 | 备注 |
| 稳定版(Stable Release) | 格式: | ||
| 预发版(Pre-release) | 格式: | ||
| 尝鲜版(Canary/Experimental/Dev) | 格式: | 一般来说,在字节内部用不到此种版本类型,可能在开源社区中使用 |
| 标题 | 说明 | 备注 |
| V0大版本内部的不稳定性 | ||
| V1、Vn(n>1)大版本内部的稳定性 | v1.x.x版本会保证不引入不兼容的更改。 v2.0.0。 v1.1.0。 v1.0.1。 |
| 章节 | 主题 | 入口 |
| 第一章 | ChatModel 与 Message(Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch01_chatmodel_agent_console.md |
| 第二章 | Agent 与 Runner(Console 多轮) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch02_chatmodel_agent_runner_console.md |
| 第三章 | Memory 与 Session(持久化对话) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch03_memory_session_jsonl.md |
| 第四章 | Tool 与文件系统访问 | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch04_tool_backend_filesystem.md |
| 第五章 | Middleware(中间件模式) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch05_middleware.md |
| 第六章 | Callback 与 Trace(可观测性) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch06_callback.md |
| 第七章 | Interrupt/Resume(中断与恢复) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch07_interrupt_resume.md |
| 第八章 | Graph Tool(复杂工作流) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch08_graph_tool.md |
| 第九章 | Skill(Console) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch09_skill.md |
| 最终章 | A2UI(Web) | https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/docs/ch10_a2ui.md |
| 章节 | 主题 | 核心内容 | 能力提升 |
| 第一章 | ChatModel 与 Message | 理解 Component 抽象,实现单次对话 | 基础对话能力 |
| 第二章 | Agent 与 Runner | 引入执行抽象,实现多轮对话 | 会话管理能力 |
| 第三章 | Memory 与 Session | 持久化对话历史,支持会话恢复 | 持久化能力 |
| 第四章 | Tool 与文件系统 | 添加文件访问能力,读取源码 | 工具调用能力 |
| 第五章 | Middleware | 中间件机制,统一处理横切关注点 | 扩展性增强 |
| 第六章 | Callback | 回调机制,监控 Agent 执行过程 | 可观测性 |
| 第七章 | Interrupt 与 Resume | 中断与恢复,支持长时间任务 | 可靠性增强 |
| 第八章 | Graph 与 Tool | 使用 Graph 编排复杂工作流 | 复杂编排能力 |
| 第九章 | A2UI | Agent 到 UI 的集成方案 | 生产级应用 |
| 维度 | ChatModel | ChatModelAgent |
| 定位 | Component(组件) | Agent(智能体) |
| 接口 | Generate() / Stream() | Run() -> AsyncIterator[*AgentEvent] |
| 输出 | 直接返回消息内容 | 返回事件流(含消息、控制动作等) |
| 能力 | 单纯的模型调用 | 可扩展 tools、middleware、interrupt 等 |
| 适用场景 | 简单的对话场景 | 复杂的智能体应用 |
| 存储方案 | 适用场景 | 优势 | 劣势 |
| JSONL 文件 | 单机应用、开发调试 | 零依赖,简单直观 | 不支持并发、分布式 |
| SQLite / LevelDB | 桌面端应用 | 轻量级嵌入式数据库 | 不适合高并发写入 |
| MySQL / PostgreSQL | 服务端部署 | 成熟稳定,功能丰富 | 运维成本较高 |
| Redis | 分布式、高频访问 | 性能极高,支持过期策略 | 数据需额外持久化 |
| S3 / OSS | 海量冷数据归档 | 成本极低,无限扩展 | 不适合频繁查询 |
| 能力 | ChatModelAgent | DeepAgent |
|---|---|---|
| 多轮对话 | ✅ | ✅ |
| 添加自定义 Tool | ✅ 手动注册每个 Tool | ✅ 手动注册或自动注册 |
| 文件系统访问(Backend) | ❌ 需手动创建并注册所有文件工具 | ✅ 一级配置,自动注册 |
| 命令执行(StreamingShell) | ❌ 需手动创建 | ✅ 一级配置,自动注册 |
| 内置任务管理 | ❌ | ✅ `write_todos` 工具 |
| 支持子 Agent | ❌ | ✅ |
| Middleware | 功能说明 |
|---|---|
| reduction | 工具输出缩减——当工具返回过长时自动截断并存入文件系统,防止上下文溢出 |
| summarization | 对话历史摘要——Token 超阈值时自动生成摘要压缩历史,节省上下文空间 |
| skill | 技能加载——让 Agent 按需动态加载预定义的 SKILL.md 知识包 |
| 时机常量 | 对应 Handler 方法 | 触发点 | 输入/输出 |
| TimingOnStart | OnStart | 组件开始处理前 | CallbackInput |
| TimingOnEnd | OnEnd | 组件成功返回后 | CallbackOutput |
| TimingOnError | OnError | 组件返回错误时 | error |
| TimingOnStartWithStreamInput | OnStartWithStreamInput | 组件接收流式输入时 | StreamReader[CallbackInput] |
| TimingOnEndWithStreamOutput | OnEndWithStreamOutput | 组件返回流式输出时 | StreamReader[CallbackOutput] |