diff --git a/assets/js/mermaid.js b/assets/js/mermaid.js
index 501d0ffa8e2..416000ddcb4 100644
--- a/assets/js/mermaid.js
+++ b/assets/js/mermaid.js
@@ -2,9 +2,80 @@
{{ if .enable }}
(function($) {
var needMermaid = false;
- $('.language-mermaid').parent().replaceWith(function() {
+ function preprocessMermaid(text) {
+ var fm = text.match(/^\s*---\s*\n([\s\S]*?)\n---\s*\n?/);
+ var directive = '';
+ var body = text;
+ if (fm) {
+ var yaml = fm[1];
+ var obj = {};
+ var current = null;
+ yaml.split('\n').forEach(function(line) {
+ var l = line.trim();
+ if (!l) return;
+ if (/^[\w-]+\s*:\s*$/.test(l)) {
+ current = l.replace(/:\s*$/, '').trim();
+ obj[current] = obj[current] || {};
+ return;
+ }
+ var m = l.match(/^(\w[\w-]*)\s*:\s*(.*)$/);
+ if (m) {
+ var k = m[1];
+ var v = m[2].trim();
+ v = v.replace(/^['"]|['"]$/g, '');
+ if (current) {
+ obj[current][k] = v;
+ } else {
+ obj[k] = v;
+ }
+ }
+ });
+ var cfg = obj.config || obj;
+ directive = '%%{init: ' + JSON.stringify(cfg) + '}%%\n';
+ body = text.replace(fm[0], '');
+ }
+ var isV10 = typeof mermaid !== 'undefined' && mermaid.version && parseInt(mermaid.version.split('.')[0], 10) >= 10;
+ body = body.replace(/
(?![^`]*`)/gi, '\\n');
+ if (!isV10) {
+ body = body.split('\n').map(function(line) {
+ var m = line.match(/^\s*([A-Za-z0-9_]+)\s*@\{\s*([^}]*)\s*\}\s*$/);
+ if (m) {
+ var id = m[1];
+ var props = m[2];
+ var lm = props.match(/label\s*:\s*("[^"]*"|'[^']*'|[^,]+)/);
+ var label = lm ? lm[1].replace(/^['"]|['"]$/g, '') : id;
+ return id + '["' + label + '"]';
+ }
+ return line;
+ }).join('\n');
+ }
+ return directive + body;
+ }
+
+ function isMermaidLike(text) {
+ var t = text.trim();
+ if (/^%%\{init:/.test(t) || /^---\s*/.test(t)) return true;
+ var firstLine = t.split('\n')[0].trim();
+ if (/^(flowchart|sequenceDiagram|classDiagram|stateDiagram|gantt|pie)\b/.test(firstLine)) return true;
+ // Only treat as mermaid when 'graph' starts with a valid direction token
+ if (/^graph\s+(TD|TB|LR|RL)\b/.test(firstLine)) return true;
+ return false;
+ }
+
+ var toReplace = [];
+ $('pre > code.language-mermaid').each(function() { toReplace.push($(this)); });
+
+ toReplace.forEach(function($code) {
needMermaid = true;
- return $('
').text($(this).text());
+ var raw = $code.text();
+ var processed = preprocessMermaid(raw);
+ var $wrapper = $code.closest('div[class*=language-]');
+ var $new = $('').text(processed);
+ if ($wrapper.length) {
+ $wrapper.replaceWith($new);
+ } else {
+ $code.parent().replaceWith($new);
+ }
});
if (!needMermaid) {
diff --git a/config.toml b/config.toml
index 35257deb1b0..51dfac79886 100644
--- a/config.toml
+++ b/config.toml
@@ -165,7 +165,7 @@ prism_syntax_highlighting = true
[params.mermaid]
enable = true
theme = "default"
-securitylevel = "strict"
+securitylevel = "loose"
# User interface configuration
[params.ui]
diff --git a/content/en/docs/eino/FAQ.md b/content/en/docs/eino/FAQ.md
new file mode 100644
index 00000000000..dea1f7a2c78
--- /dev/null
+++ b/content/en/docs/eino/FAQ.md
@@ -0,0 +1,153 @@
+---
+Description: ""
+date: "2025-12-11"
+lastmod: ""
+tags: []
+title: FAQ
+weight: 6
+---
+
+# Q: cannot use openapi3.TypeObject (untyped string constant "object") as *openapi3.Types value in struct literal; cannot use types (variable of type string) as *openapi3.Types value in struct literal
+
+Ensure the `github.com/getkin/kin-openapi` dependency version does not exceed `v0.118.0`. Starting from Eino `v0.6.0`, Eino no longer depends on the `kin-openapi` library.
+
+# Q: During agent streaming, it never reaches ToolsNode, or streaming is lost and appears non-streaming.
+
+- First, update Eino to the latest version.
+
+Different models output tool calls differently in streaming mode. Some models (e.g., OpenAI) emit tool calls directly; others (e.g., Claude) might emit text first and then the tool call. You therefore need different logic to determine whether a tool call is present in a streamed message.
+
+The ReAct Agent `Config` has a `StreamToolCallChecker`. If omitted, the agent uses a default checker that determines a tool call by inspecting whether any non-empty early chunk contains tool calls:
+
+```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
+ }
+}
+```
+
+This default works when the Tool Call message contains only a tool call.
+
+It does not fit cases where a non-empty content chunk appears before the tool call. In such cases, provide a custom checker like this:
+
+```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
+}
+```
+
+Note: this custom `StreamToolCallChecker` checks all chunks for tool calls. When the model is outputting a normal answer, this may reduce “early streaming detection”, because it waits until all chunks are inspected. To preserve streaming responsiveness, try guiding the model with prompts:
+
+> 💡
+> Add prompt constraints such as: “If a tool is required, output only the tool call; do not output text.”
+>
+> Models vary in how much they adhere to such prompts. Tune and validate for your chosen model.
+
+# Q: [github.com/bytedance/sonic/loader](http://github.com/bytedance/sonic/loader): invalid reference to runtime.lastmoduledatap
+
+Older versions of `sonic` are incompatible with `go1.24`. Upgrade to `v1.13.2` or higher.
+
+# Q: Tool input deserialization failed: `failed to invoke tool call {tool_call_id}: unmarshal input fail`
+
+Models typically do not produce invalid JSON. Investigate the specific reason for deserialization failure; in most cases this is due to output truncation when the model’s response exceeds limits.
+
+# Q: How can I implement batch processing nodes in Eino (like Coze’s batch nodes)?
+
+Eino currently does not support batch processing. Two options:
+
+1. Dynamically build the graph per request — the overhead is low. Note that `Chain Parallel` requires the number of parallel nodes to be greater than one.
+2. Implement a custom batch node and handle batching inside the node.
+
+# Q: Panic occurs in Fornax SDK or panic stack mentions Fornax SDK
+
+Upgrade both the Fornax SDK and Eino to the latest versions and retry.
+
+# Q: Does Eino support structured model outputs?
+
+Yes, in two steps:
+
+1. Ensure the model outputs structured data. Options:
+ - Some models support configuration for structured output (e.g., OpenAI response format).
+ - Use tool calls to obtain structured results.
+ - Prompt the model explicitly to output structured data.
+2. After obtaining a structured message, use `schema.NewMessageJSONParser` to parse the message into your target struct.
+
+# Q: In image recognition scenarios, error: `One or more parameters specified in the request are not valid`
+
+Check whether the model supports image input (for Doubao models, only variants with `vision` support it).
+
+# Q: How to access Reasoning Content / “thinking” output from a chat model?
+
+If the model implementation supports Reasoning Content, it is stored in the `ReasoningContent` field of the output `Message`.
+
+# Q: Errors include `context deadline exceeded`, `timeout`, or `context canceled`
+
+Cases:
+
+1. `context.canceled`: While executing a graph or agent, the user code passed a cancelable context and triggered cancellation. Investigate your application’s context-cancel logic. This is unrelated to the Eino framework.
+2. `context deadline exceeded`: Two common possibilities:
+ 1. During graph or agent execution, the user code passed a context with a timeout, which was reached.
+ 2. A `ChatModel` or other external resource has its own timeout configured (or its HTTP client does), which was reached.
+
+Inspect the thrown error for `node path: [node name x]`. If the node name is not a `ChatModel` or any node that performs external calls, it is likely case 2-a; otherwise, it is likely case 2-b.
+
+If you suspect 2-a, trace upstream to find where a timeout was set on the context (common sources include FaaS platforms, gateways, etc.).
+
+If you suspect 2-b, check whether the node itself configures a timeout (e.g., Ark ChatModel `Timeout`, or OpenAI ChatModel via an `HttpClient` with `Timeout`). If none are configured but timeouts still occur, it may be the SDK’s default timeout. Known defaults: Ark SDK 10 minutes; Deepseek SDK 5 minutes.
+
+# Q: How to access parent graph `State` within a subgraph?
+
+If the subgraph and parent graph have different `State` types, use `ProcessState[ParentStateType]()` to process the parent’s state. If they share the same `State` type, make the types distinct (for example, with a type alias: `type NewParentStateType StateType`).
+
+# Q: How does `eino-ext` adapt multimodal input/output for supported models?
+
+For multimodal support, see [https://www.cloudwego.io/docs/eino/ecosystem_integration/chat_model](https://www.cloudwego.io/docs/eino/ecosystem_integration/chat_model) and the corresponding examples for each model.
+
+# Q: Using `UserInputMultiContent` for multimodal input, but the model side seems to miss the data or cannot read `multicontent`
+
+Recent versions of Eino introduce `UserInputMultiContent` and `AssistantGenMultiContent` for multimodal user input and model output respectively. All `eino-ext` chat model implementations have been adapted. If the model does not receive the multimodal payload, upgrade the provider package to the latest version and try again.
+
+# Q: After upgrading to `0.6.x`, there are breaking changes
+
+Per the migration plan [Migration from OpenAPI 3.0 Schema Object to JSONSchema in Eino · Discussion #397](https://github.com/cloudwego/eino/discussions/397), Eino `v0.6.1` removed the dependency on `getkin/kin-openapi` and all OpenAPI 3.0-related code.
+
+If `eino-ext` modules error with `undefined: schema.NewParamsOneOfByOpenAPIV3`, upgrade those modules to the latest versions.
+
+If schema migration is complex, use the helper tooling in the [JSONSchema conversion guide](https://bytedance.larkoffice.com/wiki/ZMaawoQC4iIjNykzahwc6YOknXf).
+
+# Q: Which ChatModels in `eino-ext` support the Responses API form?
+
+By default, ChatModels generated by `eino-ext` do not support the Responses API; they support only the Chat Completions API. A special case is the Ark Chat Model, which implicitly supports the Responses API when you set `Cache.APIType = ResponsesAPI`.
diff --git a/content/en/docs/eino/_index.md b/content/en/docs/eino/_index.md
index 9901e0c3461..20fe88b28a7 100644
--- a/content/en/docs/eino/_index.md
+++ b/content/en/docs/eino/_index.md
@@ -1,6 +1,6 @@
---
-Description: Eino is an AI application development framework based on Go
-date: "2025-02-21"
+Description: Eino is a Golang-based AI application development framework
+date: "2025-12-09"
lastmod: ""
linktitle: Eino
menu:
@@ -8,65 +8,7 @@ menu:
parent: Documentation
weight: 6
tags: []
-title: 'Eino: User Manual'
+title: Eino User Manual
weight: 6
---
-> Eino pronunciation: US / 'aino /, approximate sound: i know, with the hope that the application can achieve the vision of "i know"
-
-## What is Eino
-
-> 💡
-> Eino:An AI Application Development Framework Built with Go
-
-Eino aims to provide an AI application development framework built with Go. Eino refers to many excellent AI application development frameworks in the open-source community, such as LangChain, LangGraph, LlamaIndex, etc., and provides an AI application development framework that is more in line with the programming habits of Go.
-
-Eino provides rich capabilities such as **atomic components**, **integrated components**, **component orchestration**, and **aspect extension** that assist in AI application development, which can help developers more simply and conveniently develop AI applications with a clear architecture, easy maintenance, and high availability.
-
-Take ReactAgent as an example:
-
-- It provides common components such as ChatModel, ToolNode, and PromptTemplate, and the business can be customized and extended.
-- Flexible orchestration can be performed based on existing components to generate integrated components for use in business services.
-
-
-
-## Eino Components
-
-> One of Eino's goals is: to collect and improve the component system in the context of AI applications, so that the business can easily find some common AI components, facilitating the iteration of the business.
->
-> Eino will provide components with a relatively good abstraction around the scenarios of AI applications, and provide some common implementations around this abstraction.
-
-- The abstract definition of Eino components is in: [eino/components](https://github.com/cloudwego/eino/tree/main/components)
-- The implementation of Eino components is in: [eino-ext/components](https://github.com/cloudwego/eino-ext/tree/main/components)
-
-## Eino application scenarios
-
-Thanks to the lightweight and in-field affinity properties of Eino, users can introduce powerful large model capabilities to their existing microservices with just a few lines of code, allowing traditional microservices to evolve with AI genes.
-
-When people hear the term "Graph Orchestration", their first reaction might be to segment and layer the implementation logic of the entire application interface, and convert it into an orchestratable Node. The biggest problem encountered in this process is the issue of **long-distance context passing (variable passing across Node nodes)**, whether using the State of Graph/Chain or using Options for transparent passing, the entire orchestration process is extremely complex, far from being as simple as directly making function calls.
-
-Based on the current Graph orchestration capabilities, the scenarios suitable for orchestration have the following characteristics:
-
-- The overall focus is on the semantic processing-related capabilities of the model. Here, semantics are not limited to modalities.
-- In orchestration output, there are very few nodes related to Session. Overall, the vast majority of nodes do not have processing logic at the granularity of unenumerable business entities such as users/devices.
- - Whether through the State of Graph/Chain or through CallOptions, the methods for reading, writing, or transparently passing user/device granularity information are not convenient.
-- Require common aspect capabilities, and build horizontal governance capabilities such as observation, rate limiting, and evaluation based on this.
-
-What is the significance of orchestration: To aggregate, control, and present the context of long-distance orchestration elements in a fixed paradigm.
-
-- Context of long-distance orchestration elements: Generally, orchestration elements are scattered throughout the system of the entire orchestration product, making it difficult for people to have a macroscopic and overall cognition. The role of orchestration is to gather and present this macroscopic information.
-- Aggregation control and presentation: It is to easily organize and control the relationship between orchestration elements, facilitating adjustment and display.
-
-Overall, the scenarios where "Graph Orchestration" is applicable are: business-customized AI integration components. That is, to flexibly orchestrate AI-related atomic capabilities and provide simple and easy-to-use scenario-based AI components. Moreover, in this AI component, there is a unified and complete horizontal governance capability.
-
-- When generating the corresponding AI component, the core is to provide common cross-cutting capabilities.
-- The logic inside a service interface: Generally speaking, the responsibilities are relatively single, the distribution is relatively concentrated, and the context is relatively cohesive. It does not match the applicable scenarios of "orchestration". The AI integration component can be used as an SDK in the business interface.
-- Recommended usage methods
-
-
-
-- A more challenging approach -- 【Node orchestration of the entire business process】
-- Biz Handler typically focuses on business logic and has a relatively light focus on data flow, and is more suitable for development in a function stack call manner.
- - If the logical division and combination are carried out in a graph orchestration manner, it will increase the difficulty of business logic development.
-
-
diff --git a/content/en/docs/eino/core_modules/_index.md b/content/en/docs/eino/core_modules/_index.md
index e4318cc4864..5237e4720a0 100644
--- a/content/en/docs/eino/core_modules/_index.md
+++ b/content/en/docs/eino/core_modules/_index.md
@@ -1,26 +1,22 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Core Modules'
weight: 3
---
-The core modules in Eino include the following parts:
+Eino’s core modules include the following parts:
- **Components**: [Eino: Components](/docs/eino/core_modules/components)
-Eino abstracts commonly used components in LLM applications, such as `ChatModel`, `Embedding`, `Retriever`, etc. These are the building blocks for constructing an LLM application, forming the foundation of application capabilities and serving as atomic objects for complex logic orchestration.
+ Eino abstracts commonly used components in LLM applications, such as `ChatModel`, `Embedding`, `Retriever`. These are the building blocks for application capabilities and the atomic objects in complex orchestration.
-- **Chain/Graph Orchestration**: [Eino: Chain & Graph Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration)
+- **Chain/Graph Orchestration**: [Eino: Chain/Graph Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction)
-Using multiple components in combination to implement the chain of business logic, Eino provides orchestration methods through Chains/Graphs, encapsulating the complexity of linking business logic within Eino itself. It offers easy-to-understand business logic orchestration interfaces and provides a unified cross-sectional governance capability.
+ Multiple components are combined to implement business logic. Eino provides Chain/Graph orchestration that encapsulates the complexity inside Eino, exposing easy-to-understand interfaces for logic composition and unified cross-cutting governance.
-- **Flow Integration Tools (Agents)**: [Eino: Flow integration components](/docs/eino/core_modules/flow_integration_components)
+- **Flow Integration (agents)**: [Eino: Flow Integration Components](/docs/eino/core_modules/flow_integration_components)
-Eino packages the most commonly used LLM application modes into simple and easy-to-use tools, ultra-simplifying the development of LLM applications for generic scenarios. Currently, it provides `ReAct Agent` and `Host Multi Agent`.
-
-- **EinoDev Development Assistant Tool**: [EinoDev: Devops tools](/docs/eino/core_modules/devops)
-
-Eino is dedicated to making the development of large-scale model applications with full-code very simple, and EinoDev provides a `visual` and `interactive` development and debugging solution for Eino orchestration, which allows developers to see the results immediately, releasing their energy from the `debugging hell` and focusing on the scene logic.
+ Eino wraps common LLM application patterns into simple and easy-to-use tools to greatly simplify development for general scenarios. Currently available: `ReAct Agent` and `Host Multi Agent`.
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/_index.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
index 82433d7f4f1..2507ac1e711 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
@@ -1,57 +1,54 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Chain & Graph & Workflow Orchestration'
-weight: 0
+weight: 2
---
-In LLM applications, `Components` are the smallest units that provide "atomic capabilities," such as:
+In LLM applications, `Components` provide atomic capabilities such as:
-- `ChatModel` provides the conversation capability of LLM
-- `Embedding` provides semantic-based text vectorization capabilities
-- `Retriever` provides relevant content retrieval capabilities
-- `ToolsNode` provides the capability to execute external tools
+- `ChatModel`: chat-oriented LLM interaction
+- `Embedding`: semantic vectorization for text
+- `Retriever`: retrieving relevant content
+- `ToolsNode`: invoking external tools
-> For detailed information on components, refer to: [Eino: Components](/docs/eino/core_modules/components)
+> For component details: [Eino: Components](/docs/eino/core_modules/components)
-An LLM application, in addition to needing these atomic capabilities, also needs to **combine and sequence** these atomic capabilities based on contextual business logic. This is called **Orchestration**.
+Beyond atomic capabilities, applications must combine and chain them according to business scenarios. This is **orchestration**.
-Developing LLM applications has its own typical characteristics: The custom business logic itself is usually not very complex, primarily involving the combination and sequencing of "atomic capabilities."
+LLM application development has a typical character: custom business logic is rarely complex; most of the work is composing and chaining atomic capabilities.
-In traditional code development, business logic is expressed through "code execution logic." When transitioning to LLM application development, the most straightforward approach is "to call components manually and use the results as inputs for subsequent component calls." Such an approach results in `messy code`, `difficulty in reuse`, and `lack of aspect-oriented capabilities`...
+If you simply call components manually and pass outputs downstream by hand, you end up with code that is messy, hard to reuse, and lacks cross-cutting aspects.
-When developers pursue code that is '**elegant**' and follows the '**clean code principles**,' they find a significant gap when applying traditional code organization methods to LLM applications.
+To keep code elegant and clean, Eino makes LLM app development simple, intuitive, and robust.
-Eino's initial goal was to make LLM application development extremely simple, ensuring that the application code logic is "simple," "intuitive," "elegant," and "robust."
+Eino’s perspective on orchestration:
-Eino has the following insights into "Orchestration":
+- Orchestration should be a clear layer above business logic — **do not blend business logic into orchestration**.
+- LLM applications center on composing components; **components are first-class citizens of orchestration**.
+- Abstractly, orchestration builds a network through which data flows. Each node imposes requirements on the data’s format/content. A smooth network hinges on **type alignment between upstream and downstream nodes**.
+- Real-world complexity appears in orchestration artifacts; only **horizontal governance** keeps complexity controlled.
+- LLMs and applications evolve quickly; only **extensible applications** remain viable.
-- Orchestration should become a clear layer on top of business logic, **without embedding business logic into orchestration**.
-- The core of the LLM application is "sequencing and combining components that provide atomic capabilities," **with components being the "first citizens" of orchestration**.
-- From an abstract perspective, orchestration builds a network where data flows through. Each node within the network has specific format/content requirements for the flowing data. The key to a seamlessly flowing data network is "**whether the data formats between upstream and downstream nodes are aligned**?".
-- The complexity of business scenarios will be reflected in the complexity of orchestration artifacts. Only **horizontal governance capabilities** can keep complex scenarios under control.
-- LLMs will continue to develop rapidly, and so will LLM applications. Only applications with **expansion capabilities** will have vitality.
+Therefore, Eino offers a graph-based model (`node + edge`) where **components** are atomic nodes and **type alignment** underpins orchestration.
-Therefore, Eino provides a solution for "orchestration based on the Graph model (node + edge), with **components** as atomic nodes and **upstream-downstream type alignment** as the foundation."
+Specifically:
-Specifically, the following features are implemented:
+- Everything centers on components. Clear encapsulation yields clear responsibilities and natural reuse.
+ - See: [Eino: Components](/docs/eino/core_modules/components)
+- Push business complexity into component implementations; the orchestration layer maintains global clarity.
+- Provide aspect capabilities via callbacks, enabling **unified governance** at the node level.
+ - See: [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)
+- Provide call options for **extensibility** during rapid iteration.
+ - See: [Eino: Call Option Capabilities](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities)
+- Reinforce **type alignment** to reduce cognitive load and leverage Go’s type safety.
+ - See: [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+- Provide **automatic stream conversion**, removing “stream handling” from the list of orchestration complexity sources.
+ - See: [Eino: Streaming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
-- Everything is centered around "components," standardizing the encapsulation of business functionalities, making **division of responsibilities clear** and **reuse** natural.
- - For more details, refer to: [Eino: Components](/docs/eino/core_modules/components)
-- The complexity of business logic is encapsulated within the components, giving the orchestration layer a more global perspective, making **logic layers very clear**.
-- Provides aspect capabilities and a callback mechanism that supports node-based **unified governance capabilities**.
- - For more details, refer to: [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)
-- Provides a call option mechanism, **extensibility** is the most fundamental requirement of the system in rapid iterations.
- - For more details, refer to: [Eino: CallOption capabilities and specification](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities)
-- Provides an enhanced "type alignment" development method, reducing the mental burden on developers and leveraging Go's **type safety** features.
- - For more details, refer to: [Eino: The design concept of orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
-- Provides an **"automated stream conversion"** capability, removing "stream" from the "source of complexity ranking" in the orchestration system.
- - For more details, refer to: [Eino Points of Streaming Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+Graphs are powerful and semantically complete; you can express branches, parallelism, and loops. The tradeoff is using `graph.AddXXXNode()` and `graph.AddEdge()` — powerful, but a bit verbose.
-Graph itself is powerful and semantically complete, capable of rendering almost any "data flow network," such as "branching," "parallel," and "loop."
+Most real scenarios only need sequential chaining. Eino exposes `Chain`, a simpler interface that wraps `Graph`. Except for cycles, `Chain` surfaces nearly all `Graph` capabilities.
-However, Graph is not without its drawbacks. Based on the "node" and "edge" model, Graph requires developers to use the `graph.AddXXXNode()` and `graph.AddEdge()` interfaces to create a data channel, which is powerful but somewhat complex.
-
-In most real-world business scenarios, simply "connecting in sequence" is often sufficient. Therefore, Eino encapsulates an easier-to-use interface called `Chain`. Chain is a wrapper around Graph, exposing almost all of Graph's capabilities except for "loops."
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
index 97c8a5b4153..b128cbd1af1 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
@@ -1,305 +1,178 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: 'Eino: CallOption capabilities and specification'
-weight: 5
+title: 'Eino: CallOption Capabilities and Conventions'
+weight: 6
---
-**CallOption**: A channel for directly passing data to a specific set of nodes (Component, Implementation, Node) when invoking Graph compilation products.
+**CallOption**: a request-scoped way to pass configuration directly to specific nodes (Component, Implementation, Node) when invoking a compiled Graph.
-- Difference from Node Config: Node Config is an instance-level configuration, meaning the values in Config are set from instance creation to instance destruction and do not need to change once determined.
-- CallOption: This is request-level configuration, where values differ for each request. It is more like node parameters, but these parameters are directly passed from the Graph entrance rather than from upstream nodes.
-- Example: Passing a Temperature configuration to a ChatModel node; passing a custom option to a Lambda node.
+- Difference from node Config: node Config is instance-scoped (set at construction and stable across the instance’s lifetime).
+- CallOption is request-scoped; values differ per request. Think of it as per-invocation arguments injected from the Graph entry rather than passed from an upstream node.
+ - Example: set `Temperature` for a `ChatModel` node; pass custom options to a `Lambda` node.
-## **Component CallOption Form**
+## Component CallOption Shapes
-Component CallOption configuration has two levels:
+Two granularities:
-- CallOption configuration uniformly defined by the abstract (Abstract/Interface) of the component [Component Abstract CallOption]
-- CallOption configuration defined by the implementation (Type/Implementation) of the component for that specific type [Component Implementation CallOption]
+- Component-level common options defined by the abstract interface [Component-abstract CallOptions]
+- Implementation-specific options defined by a concrete implementation [Component-impl CallOptions]
-Taking the ChatModel component as an example, the form of CallOption is introduced
+Using `ChatModel` to illustrate.
-### **Directory of Model Abstract and Implementation**
+### Directory Layout
```
-// Location of the abstract in the code
+// Abstract
eino/components/model
├── interface.go
-├── option.go // CallOption parameter at the component abstract level
+├── option.go // Component-abstract CallOptions
-// Location of the abstract implementation in the code
+// Implementations
eino-ext/components/model
├── claude
-│ ├── option.go // CallOption parameter for one implementation of the component
+│ ├── option.go // impl-specific CallOptions
│ └── chatmodel.go
├── ollama
-│ ├── call_option.go // CallOption parameter for one implementation of the component
+│ ├── call_option.go // impl-specific CallOptions
│ ├── chatmodel.go
```
-### **Model Abstraction**
+### Model Abstraction
-As mentioned above, when defining the CallOption for a component, it is necessary to distinguish between the [Component Abstract CallOption] and the [Component Implementation CallOption] scenarios. Whether to provide the [Component Implementation CallOption] is determined by the Component Abstraction.
-
-The CallOption extension capabilities provided by the Component Abstraction are as follows (taking Model as an example, other components are similar):
+When designing CallOptions, distinguish abstract vs implementation-specific options. The abstract component decides whether to expose impl-specific CallOptions.
```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.
+ Stream(ctx context.Context, input []*schema.Message, opts ...Option) (*schema.StreamReader[*schema.Message], error)
BindTools(tools []*schema.ToolInfo) error
}
-// This structure is the unified definition of [Component Abstract CallOption]. The component implementation can extract information from [Component Abstract CallOption] according to its own needs.
-// Options is the common options for the model.
+// Abstract/common options
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 a 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 the ChatModel component.
type Option struct {
- // This field serves the apply method for [Component Abstract CallOption], such as WithTemperature
- // If the component abstraction does not want to provide the [Component Abstract CallOption], this field can be omitted, along with the GetCommonOptions() method.
- apply func(opts *Options)
-
- // This field serves the apply method for [Component Implementation CallOption], and it is assumed the apply method is: func(*T)
- // If the component abstraction does not want to provide [Component Implementation CallOption], this field can be omitted along with the GetImplSpecificOptions() method.
- 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
- },
- }
+ apply func(opts *Options) // for abstract/common options
+ implSpecificOptFn any // for impl-specific options (func(*T))
}
-// WithModel is the option to set the model name.
-func WithModel(name string) Option {
- return Option{
- apply: func(opts *Options) {
- opts.Model = &name
- },
- }
-}
+func WithTemperature(temperature float32) Option { return Option{ apply: func(o *Options){ o.Temperature = &temperature } } }
+func WithMaxTokens(maxTokens int) Option { return Option{ apply: func(o *Options){ o.MaxTokens = &maxTokens } } }
+func WithModel(name string) Option { return Option{ apply: func(o *Options){ o.Model = &name } } }
+func WithTopP(topP float32) Option { return Option{ apply: func(o *Options){ o.TopP = &topP } } }
+func WithStop(stop []string) Option { return Option{ apply: func(o *Options){ o.Stop = stop } } }
-// 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)
- }
- }
-
+ if base == nil { base = &Options{} }
+ for i := range opts { if f := opts[i].apply; f != nil { f(base) } }
return base
}
-// Component implementers can use this method to encapsulate their own Option functions: func WithXXX(xxx string) Option{}
-func WrapImplSpecificOptFn[T any](optFn func(*T)) Option {
- return Option{
- implSpecificOptFn: optFn,
- }
-}
+func WrapImplSpecificOptFn[T any](optFn func(*T)) Option { return Option{ implSpecificOptFn: optFn } }
-// GetImplSpecificOptions provides tool authors 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)
- }
-
+ 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)
- }
- }
+ if optFn, ok := opts[i].implSpecificOptFn.(func(*T)); ok { optFn(base) }
}
-
return base
}
```
-### **Claude Implementation**
-
-> [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)
+### Example: Claude Implementation
```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
- })
-}
+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)
+Usage inside the provider:
```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
-}
+common := model.GetCommonOptions(&model.Options{ Model:&c.model, Temperature:c.temperature, MaxTokens:&c.maxTokens, TopP:c.topP, Stop:c.stopSequences }, opts...)
+claude := model.GetImplSpecificOptions(&options{ TopK:c.topK }, opts...)
```
-## **CallOption in Composition**
+## CallOptions in Orchestration
-> [https://github.com/cloudwego/eino/blob/main/compose/runnable.go](https://github.com/cloudwego/eino/blob/main/compose/runnable.go)
-
-Graph compilation result is Runnable
+Compiled graphs implement `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)
+ Invoke(ctx context.Context, input I, opts ...Option) (O, error)
+ Stream(ctx context.Context, input I, opts ...Option) (*schema.StreamReader[O], error)
+ Collect(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (O, error)
+ Transform(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (*schema.StreamReader[O], error)
}
```
-Each method of Runnable accepts a list of compose.Option.
+Each method accepts `compose.Option`, which can include:
-> [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 global options (e.g., callbacks)
+- Component-specific options (abstract or impl-specific)
+- Node-targeted options
-Including overall configuration for graph run, configuration of various components, and specific Lambda configurations, etc.
+These options are propagated to matching nodes during execution — globally, by component type, or by designated node — giving fine-grained per-request control without mutating instance configs.
```go
// Option is a functional option type for calling a graph.
type Option struct {
options []any
handler []callbacks.Handler
-
- paths []*NodePath
-
+ 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.
+// DesignateNode sets the key of the node(s) to which the option will be applied.
+// 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)
- }
+ 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"})
+// DesignateNodeWithPath sets the path of the node(s) to which the option will be applied.
+// You can make the option take effect in a 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.
+// WithEmbeddingOption is a functional option 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...)
-}
+func WithEmbeddingOption(opts ...embedding.Option) Option { return withComponentOption(opts...) }
```
-compose.Option can be assigned to different nodes in the Graph as needed.
-
-
+compose.Option can be targeted to different nodes in the Graph as needed:
```go
-// Call option effective for all nodes
+// call option effective for all nodes
compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
-// Call option effective for specific types of nodes
-compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5))
+// call option effective for specific component types
+compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5)))
+compiledGraph.Invoke(ctx, input, WithToolOption(WithXXX("xxx")))
-// Call option effective only for specific nodes
+// call option effective for a specific node
compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
-// Call option effective only for specific nested graphs or nodes within them
-compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNodeWithPath(NewNodePath("1", "2"))
+// call option effective for specific nested subgraph or its node(s)
+compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNodeWithPath(NewNodePath("1", "2")))
```
+
+
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
index 87ce2c74347..f9966668c26 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
@@ -1,319 +1,214 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino: Callback Manual'
-weight: 4
+weight: 5
---
-## **Problem Solved**
+## Problem Statement
-Components (including Lambda) and Graph orchestration together solve the problem of "defining business logic." However, cross-cutting features such as logging, tracing, metrics, and on-screen display need mechanisms to inject functionality into Components (including Lambda) and Graphs.
+Components (including Lambdas) and Graph orchestration define business logic. Cross-cutting concerns like logging, tracing, metrics, and UI surfacing need an injection mechanism into Components (including Lambdas) and Graphs. Users may also need internal state exposure (e.g., DB name in `VikingDBRetriever`, temperature in `ArkChatModel`).
-On the other hand, users might want to obtain intermediate information during the execution process of a specific Component implementation. For instance, VikingDBRetriever might provide additional query DB Names, and ArkChatModel might provide additional parameters like the requested temperature. There needs to be a mechanism to expose intermediate states.
+Callbacks enable both cross-cutting injection and mid-execution state exposure. Users provide and register callback handlers; Components/Graphs call them at defined timings with relevant information.
-Callbacks support both "**cross-cutting feature injection**" and "**intermediate state exposure**". Specifically, users provide and register "functions" (Callback Handlers). Components and Graphs call back these functions at designated "times" (or cut points, locations) and provide the corresponding information.
+## Core Concepts
-## **Core Concepts**
+Entities in Eino (Components, Graph Nodes/Chain Nodes, Graph/Chain itself) trigger callbacks at defined timings (Callback Timing) by invoking user-provided handlers (Callback Handlers). They pass who is running (RunInfo) and what is happening (Callback Input/Output or streams).
-The core concepts interconnected within Eino include entities such as Components and Graphs, which at specific **timings** (Callback Timing), callback user-provided **functions** (Callback Handlers), conveying **what they are** (RunInfo), and **what happened** at that moment (Callback Input & Output).
+### Triggering Entities
-### **Trigger Entities**
+- Components (official types and user Lambdas)
+- Graph Nodes (and Chain Nodes)
+- Graph itself (and Chain)
-Component (including types defined officially and Lambda), Graph Node (as well as Chain Node), and the Graph itself (including Chain). These three types of entities require cross-cutting functionality injection and intermediate state exposure, thus they all trigger callbacks. See the section on “[Trigger Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)” below for details.
+All can inject cross-cutting concerns and expose intermediate state.
-### **Trigger Timings**
+### Timings
```go
-// CallbackTiming enumerates all the timing of callback aspects.
type CallbackTiming = callbacks.CallbackTiming
const (
- TimingOnStart CallbackTiming = iota // Enter and begin execution
- TimingOnEnd // Successfully complete and about to return
- TimingOnError // Fail and about to return an error
- TimingOnStartWithStreamInput // OnStart, but the input is StreamReader
- TimingOnEndWithStreamOutput // OnEnd, but the output is StreamReader
+ TimingOnStart
+ TimingOnEnd
+ TimingOnError
+ TimingOnStartWithStreamInput
+ TimingOnEndWithStreamOutput
)
```
-Different trigger entities, in various scenarios, determine whether to trigger OnStart or OnStartWithStreamInput (similarly, OnEnd/OnEndWithStreamOutput). Specific rules are detailed in the section on “[Trigger Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)” below.
+Entity type and execution mode determine whether OnStart vs OnStartWithStreamInput (and similarly for end). See triggering rules below.
-### **Callback Handler**
+### Handler Interface
```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
+ OnStartWithStreamInput(ctx context.Context, info *RunInfo, input *schema.StreamReader[CallbackInput]) context.Context
+ OnEndWithStreamOutput(ctx context.Context, info *RunInfo, output *schema.StreamReader[CallbackOutput]) context.Context
}
```
-A Handler is a struct that implements the 5 methods above (corresponding to the 5 trigger timings). Each method receives three pieces of information:
+Each method receives:
-- Context: Used for **receiving custom information** that might be set by previous trigger timings of the same Handler.
-- RunInfo: Metadata of the entity triggering the callback.
-- Input/Output/InputStream/OutputStream: Business information at the time of triggering the callback.
+- `context.Context`: carries data across timings within the same handler
+- `RunInfo`: metadata of the running entity
+- `Input/Output` or `InputStream/OutputStream`: business information at the timing
-Each method also returns a new context: used for **passing information between different trigger timings** of the same Handler.
+All return a context for passing info across timings for the same handler.
-If a Handler does not want to focus on all 5 trigger timings but only a subset, for example, just OnStart, it is recommended to use `NewHandlerBuilder().OnStartFn(...).Build()`. If it only wants to focus on specific components, such as ChatModel, it is recommended to use `NewHandlerHelper().ChatModel(...).Handler()`, which receives callbacks from only ChatModel and obtains a specific type of CallbackInput/CallbackOutput. See the section on “[Handler Implementation Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)” for details.
+Use builders/helpers to focus on subsets:
-The order of triggering between different Handlers is **not** guaranteed.
+- `NewHandlerBuilder().OnStartFn(...).Build()` to handle selected timings only
+- `NewHandlerHelper().ChatModel(...).Handler()` to target specific component types and get typed inputs/outputs
-### **RunInfo**
+Handlers have no guaranteed ordering.
-Describes the metadata of the entity that triggers the Callback.
+### RunInfo
```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 string // user-specified name with business meaning
+ Type string // specific implementation type (e.g., OpenAI)
+ Component components.Component // abstract component type (e.g., ChatModel)
}
```
-- Name: Meaningful business name, to be specified by the user; if not specified, it defaults to an empty string. For different triggering entities:
- - Component: When in Graph, use Node Name. When used standalone outside Graph, user sets it manually. See "Injecting RunInfo" and "Using Component Standalone" for details.
- - Graph Node: Use Node Name `func WithNodeName(n string) GraphAddNodeOpt`
- - Graph itself:
- - For the top-level graph, use Graph Name `func WithGraphName(graphName string) GraphCompileOption`
- - For nested graphs, use the Node Name added when joining the upper-level graph.
-- Type: Specified by the component's specific implementation:
- - Component with interface: If it implements Typer interface, use the result of the GetType() method. Otherwise, use reflection to get the Struct/Func name.
- - Lambda: If Type is specified using `func WithLambdaType(t string) LambdaOpt`, use it, otherwise it defaults to an empty string.
- - Graph Node: Use the value of the internal Component/Lambda/Graph.
- - Graph itself: Defaults to an empty string.
-- Component:
- - Component with interface: Corresponds to its interface.
- - Lambda: Fixed value Lambda.
- - Graph Node: Use the value of the internal Component/Lambda/Graph.
- - Graph itself: Fixed values Graph / Chain. (Previously had StateGraph / StateChain, now consolidated into Graph / Chain.)
-
-### **Callback Input & Output**
-
-Essentially of any type, as the input, output, and internal state of different Components can vary greatly.
+- Name: user-specified
+ - Component: Node Name in Graph; manual when used standalone
+ - Graph Node: Node Name (set via `WithNodeName(n string)`)
+ - Graph: Graph Name for top-level (set via `WithGraphName(name string)`); node name when nested
+- Type: provider decides
+ - Interface components: `GetType()` if `Typer` implemented; fallback to reflection
+ - Lambda: `WithLambdaType` or empty
+ - Graph Node: type of internal component/lambda/graph
+ - Graph itself: empty
+- Component: abstract type (ChatModel/Lambda/Graph; Graph/Chain/Workflow for graph itself)
+
+### Callback Input & Output
+
+Types vary per component.
```go
type CallbackInput any
type CallbackOutput any
```
-For a specific component, there are more specific types, such as Chat Model.
+Example for ChatModel:
```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
+ Tools []*schema.ToolInfo
+ Config *Config
+ 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.
+ Message *schema.Message
+ Config *Config
TokenUsage *TokenUsage
- // Extra is the extra information for the callback.
- Extra map[string]any
+ Extra map[string]any
}
```
-In the specific implementation of Chat Model, such as OpenAI Chat Model, it is recommended that component authors pass specific Input/Output types to the Callback Handler instead of Any. This allows for more specific, customized intermediate state information to be exposed.
-
-If a Graph Node triggers the Callback, since the Node cannot obtain the internal intermediate state information of the component, it can only get the input and output specified by the component interface. For Chat Model, this would be []*schema.Message and *schema.Message.
+Providers should pass typed inputs/outputs to expose richer state. Graph Nodes only have component interface-level inputs/outputs; they cannot access internal provider state.
-When the Graph itself triggers the Callback, the input and output are the overall input and output of the Graph.
+Graph itself uses graph-level input/output.
## Injecting Handlers
-Handlers need to be injected into the Context to be triggered.
-
-### **Globally Injecting Handlers**
-
-Use `callbacks.AppendGlobalHandlers` to inject global Handlers. Once injected, all triggered callback actions will automatically trigger these global Handlers. Typical scenarios include tracing, logging, and other globally consistent, business-agnostic functions.
-
-It is not thread-safe. It is recommended to inject it once during service initialization.
+### Global Handlers
-### **Injecting Handlers into the Graph**
+Use `callbacks.AppendGlobalHandlers` to register. Suitable for universal concerns (tracing, logging). Not concurrency-safe; register at service init.
-Use `compose.WithCallbacks` to inject Handlers at runtime within the graph. These Handlers will be effective throughout the current execution of the graph, including each Node within the Graph and the Graph itself (as well as any nested graphs).
+### Handlers in Graph Execution
-Use `compose.WithCallbacks(...).DesignateNode(...)` to inject a Handler into a specific Node of the top-level Graph. If this Node itself is a nested Graph, it will be injected into the nested Graph itself and each Node within it.
+- `compose.WithCallbacks` injects handlers for the current graph run (includes nested graphs and nodes)
+- `compose.WithCallbacks(...).DesignateNode(...)` targets a specific top-level node (injects into a nested graph and its nodes when the node is a graph)
+- `compose.WithCallbacks(...).DesignateNodeForPath(...)` targets a nested node by path
-Use `compose.WithCallbacks(...).DesignateNodeForPath(...)` to inject a Handler into a specific Node of a nested Graph.
+### Outside Graph
-### **Injecting Handlers Outside the Graph**
+Use `InitCallbacks(ctx, info, handlers...)` to obtain a new context with handlers and RunInfo.
-If you do not want to use the Graph but still want to use Callbacks:
+### Handler Inheritance
-Use `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` to get a new Context and inject Handlers and RunInfo.
+Child contexts inherit parent handlers. A graph run inherits handlers present in the incoming context.
-### **Handler Inheritance**
+## Injecting RunInfo
-Similar to how a child Context inherits all Values from its parent Context, a child Context will also inherit all Handlers from its parent Context. For example, if Handlers are already present in the Context passed into the Graph at runtime, these Handlers will be inherited and effective throughout the entire execution of the Graph.
+### Graph-Managed RunInfo
-## **Injecting RunInfo**
+Graph injects RunInfo for all internal nodes automatically using child contexts.
-RunInfo also needs to be injected into the Context to be available to Handlers when callbacks are triggered.
+### Outside Graph
-### **RunInfo Managed by the Graph**
+Use `InitCallbacks(ctx, info, handlers...)` or `ReuseHandlers(ctx, info)` to set RunInfo with existing handlers.
-The Graph will automatically inject RunInfo for all internal Nodes. The mechanism is that each Node's execution is a new child Context, and the Graph injects the corresponding Node's RunInfo into this new Context.
+## Triggering
-### **Injecting RunInfo Outside the Graph**
+### Component-level Callbacks
-If you do not want to use the Graph but still want to use Callbacks:
+Providers should trigger `callbacks.OnStart/OnEnd/OnError/OnStartWithStreamInput/OnEndWithStreamOutput` inside component implementations. Example (Ark ChatModel):
-Use `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` to get a new Context and inject Handlers and RunInfo.
-
-Use `ReuseHandlers(ctx context.Context, info *RunInfo)` to get a new Context, reuse the Handlers from the previous Context, and set the new RunInfo.
-
-## **Trigger Methods**
-
-
+```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) } }()
-### **Component Callback**
+ // assemble request config
+ ctx = callbacks.OnStart(ctx, &fmodel.CallbackInput{ Messages: in, Tools: append(cm.rawTools), ToolChoice: nil, Config: reqConf })
-In the implementation code of the component, call `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), OnEndWithStreamOutput()` from the callbacks package. Taking Ark's ChatModel implementation as an example, in the Generate method:
-
-```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),
- })
+ // invoke provider API and read response
+ _ = callbacks.OnEnd(ctx, &fmodel.CallbackOutput{ Message: outMsg, Config: reqConf, TokenUsage: toModelCallbackUsage(outMsg.ResponseMeta) })
return outMsg, nil
}
-```
-In the Stream method:
+func (cm *ChatModel) Stream(ctx context.Context, in []*schema.Message, opts ...fmodel.Option) (outStream *schema.StreamReader[*schema.Message], err error) {
+ defer func() { if err != nil { _ = callbacks.OnError(ctx, err) } }()
-```go
-func (cm *ChatModel) Stream(ctx context.Context, in []*schema.Message, opts ...fmodel.Option) (
- 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]
+ // assemble request config
+ ctx = callbacks.OnStart(ctx, &fmodel.CallbackInput{ Messages: in, Tools: append(cm.rawTools), ToolChoice: nil, Config: reqConf })
+ // invoke provider API and convert response 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
-}
-```
-
-As can be seen, when the Generate method is called, OnEnd is triggered, whereas in the Stream method, OnEndWithStreamOutput is triggered:
-
-When triggering Callbacks within the component implementation:
-
-- **When the component input is StreamReader, trigger OnStartWithStreamInput, otherwise trigger OnStart**
-- **When the component output is StreamReader, trigger OnEndWithStreamOutput, otherwise trigger OnEnd**
-
-Components that internally implement callback triggers should implement the Checker interface, returning true from IsCallbacksEnabled, to convey to the outside that "I have internally implemented callback triggers":
-
-```go
-// Checker tells the callback aspect status of the 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
+ return schema.StreamReaderWithConvert(sr, func(src *fmodel.CallbackOutput) (*schema.Message, error) {
+ if src.Message == nil { return nil, schema.ErrNoValue }
+ return src.Message, nil
+ }), nil
}
```
-If a component implementation does not implement the Checker interface, or IsCallbacksEnabled returns false, it can be assumed that the component does not trigger callbacks internally, and Graph Node is needed to handle the injection and triggering (when used within the Graph).
-
-### Graph Node Trigger (Node Callback)
-
-When a Component is organized into a Graph, it becomes a Node. At this time, if the Component itself triggers a callback, the Node will reuse the Component's callback handling. Otherwise, the Node will place a callback handler at the points outside the Component. These points correspond to the streaming paradigm of the Component itself. For example, a ChatModelNode will have OnStart/OnEnd/OnError outside the Generate method, and OnStart/OnEndWithStreamOutput/OnError outside the Stream method.
-
-When the Graph is running, each component will operate in either Invoke or Transform paradigm, and will call the corresponding component methods based on the specific implementation of the component's business streaming paradigm. For example, if the Graph runs in Invoke, the Chat Model Node will run in Invoke, calling the Generate method. However, if the Graph runs in Stream, the Chat Model Node will run in Transform, but since the Chat Model's business streaming paradigm does not include Transform, it will automatically downgrade to calling the Stream method. Therefore:
-
-**Which specific trigger points a Graph Node executes (OnStart or OnStartWithStreamInput) depends on the component's business streaming paradigm and the Graph running mode.**
-
-For a detailed introduction to Eino's streaming programming, refer to [Eino Points of Streaming Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
+### Graph/Node-level Callbacks
-### **Graph Self Trigger (Graph Callback)**
+When a Component is orchestrated into a Graph Node, and the Component does not implement callbacks, the Node injects callback trigger points matching the Component’s streaming paradigm. For example, a ChatModelNode triggers OnStart/OnEnd around `Generate`, and OnStart/OnEndWithStreamOutput around `Stream`. Which timing is triggered depends on both Graph’s execution mode (Invoke/Stream/Collect/Transform) and the Component’s streaming support.
-The Graph triggers the Callback Handler at its own start, end, and error moments. If the Graph is called in Invoke form, it triggers OnStart/OnEnd/OnError. If called in Stream/Collect/Transform form, it triggers OnStartWithStreamInput/OnEndWithStreamOutput/OnError. This is because **the Graph will always execute internally in Invoke or Transform**. See [Eino Points of Streaming Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials)
+See [Streaming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
-It is worth noting that a graph is also a kind of component, so a graph callback is also a special form of component callback. According to the definition of Node Callback, when the component inside the Node implements the perception and processing of the trigger moments, the Node will directly reuse the Component's implementation and will not implement the Node Callback again. This means when a graph is added to another Graph as a Node through the AddGraphNode method, this Node will reuse the internal graph's graph callback.
+### Graph-level Callbacks
-## **Parsing Callback Input & Output**
+Graph triggers callbacks at its own start/end/error timings. If Graph is called via `Invoke`, it triggers `OnStart/OnEnd/OnError`. If called via `Stream/Collect/Transform`, it triggers `OnStartWithStreamInput/OnEndWithStreamOutput/OnError` because Graph internally always executes as `Invoke` or `Transform`. See [Streaming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
-As mentioned earlier, the underlying type of Callback Input & Output is Any, and different component types might pass in their specific types when triggering callbacks. Also, in the interface definition of the Callback Handler, the input parameters of various methods are also Callback Input & Output of type Any.
+Note: Graph is also a component. Therefore, a graph callback is a special form of component callback. Per Node Callback semantics, when a node’s internal component (including a nested graph added via `AddGraphNode`) implements callback timings itself, the node reuses the component’s behavior and does not add duplicate node-level callbacks.
-Therefore, in the specific Handler implementation, two things need to be done:
+## Parsing Callback Input & Output
-1. Determine which component type triggered the callback using RunInfo. For example, RunInfo.Component == "ChatModel" or RunInfo.Type == "xxx Chat Model".
-2. Convert the Callback Input & Output of type any to the corresponding specific type. For instance, if RunInfo.Component == "ChatModel":
+Underlying types are `any`, while specific components may pass their own typed inputs/outputs. Handler method parameters are `any` as well, so convert as needed.
```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
+ case *CallbackInput: // component implementation already passed 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,
- }
+ case []*schema.Message: // graph node injected callback passes ChatModel interface input: []*schema.Message
+ return &CallbackInput{ Messages: t }
default:
return nil
}
@@ -322,33 +217,29 @@ func ConvCallbackInput(src callbacks.CallbackInput) *CallbackInput {
// 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
+ case *CallbackOutput: // component implementation already passed 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,
- }
+ case *schema.Message: // graph node injected callback passes ChatModel interface output: *schema.Message
+ return &CallbackOutput{ Message: t }
default:
return nil
}
}
```
-If the Handler needs to add switch cases to determine RunInfo.Component and, for each case, call the corresponding conversion function to convert Any to the specific type, it does get somewhat complex. To reduce the repetitive labor of writing glue code, we provide two convenient utility functions for implementing Handlers.
+To reduce boilerplate, prefer helpers/builders when focusing on specific components or timings.
## Handler Implementation
-In addition to implementing the Handler interface directly, Eino provides two convenient tools for implementing Handlers.
+### HandlerHelper
-### **HandlerHelper**
-
-If the user's Handler only focuses on specific types of components, such as in the scenario of ReactAgent, which only focuses on ChatModel and Tool, it is recommended to use HandlerHelper to quickly create a Callback Handler for a specific type:
+When a handler only targets specific component types (e.g., in ReAct scenarios focusing on ChatModel and Tool), use `HandlerHelper` to quickly create typed handlers:
```go
handler := NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler()
```
-Where modelHandler is a further encapsulation of the callback handler for the Chat Model component:
+The `modelHandler` can use a typed helper for ChatModel callbacks:
```go
// ModelCallbackHandler is the handler for the model callback.
@@ -360,87 +251,353 @@ type ModelCallbackHandler struct {
}
```
-The above ModelCallbackHandler encapsulates three operations:
+This encapsulation provides:
+
+- Automatic filtering by component type (no need to switch on `RunInfo.Component`)
+- Only the timings supported by ChatModel (drops `OnStartWithStreamInput`); implement any subset
+- Typed `Input/Output` (`model.CallbackInput`, `model.CallbackOutput`) instead of `any`
+
+`HandlerHelper` supports official components: ChatModel, ChatTemplate, Retriever, Indexer, Embedding, Document.Loader, Document.Transformer, Tool, ToolsNode. For Lambda/Graph/Chain, it filters by type but you still implement generic `callbacks.Handler` for timings and conversions:
-1. No longer needing to determine RunInfo.Component to select the callback triggered by ChatModel, as the filtering is already done automatically.
-2. Only requiring the implementation of the trigger timings supported by the Chat Model component, here removing unsupported OnStartWithStreamInput. Additionally, if the user only focuses on specific trigger timings supported by the Chat Model, such as only OnStart, they can only implement OnStart.
-3. Input/Output is no longer of any type but has been converted to model.CallbackInput, model.CallbackOutput.
+```go
+handler := NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler).Handler()
+```
-HandlerHelper supports all official components. The current list includes: ChatModel, ChatTemplate, Retriever, Indexer, Embedding, Document.Loader, Document.Transformer, Tool, ToolsNode.
+### HandlerBuilder
-For components like Lambda, Graph, Chain, which have indeterminate input and output types, you can also use HandlerHelper, but it can only achieve the first point mentioned above, i.e., automatic filtering by component type. Points 2 and 3 still need to be implemented by the user themselves:
+If a handler needs to target multiple component types but only a subset of timings, use `HandlerBuilder`:
```go
-handler := NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)...Handler()
+handler := NewHandlerBuilder().OnStartFn(fn).Build()
```
-At this time, NewHandlerHelper().Lambda() needs to pass in callbacks.Handler, which can be achieved using the HandlerBuilder below.
+## Usage Notes
+
+- Prefer typed inputs/outputs for provider-specific handlers
+- Use global handlers for common concerns; node-specific handlers for fine-grained control
+- Remember handler order is unspecified; design idempotent handlers
-### **HandlerBuilder**
+### Best Practices
-If the user's Handler needs to focus on multiple component types but only on specific trigger timings, HandlerBuilder can be used:
+#### In Graph
+
+- Actively use Global Handlers for always-on concerns.
```go
-handler := NewHandlerBuilder().OnStartFn(fn)...Build()
+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
+}
+```
+
+- Inject per-run handlers with `WithCallbacks` and target nodes via `DesignateNode` or by path.
+
+```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)
+}
```
-## **Best Practices**
+
+
+#### Outside Graph
-### **Usage in Graph**
+This scenario: you do not use Graph/Chain/Workflow orchestration, but you directly call components like ChatModel/Tool/Lambda and still want callbacks to trigger.
-- Actively use Global Handlers, registering handlers that are always in effect.
-- Inject Handlers at runtime using the `WithHandlers` option, and designate the Node, nested internal Graph, or internal Graph's Node where they apply using `DesignateNode` or `DesignateNodeByPath`.
+You must manually set correct `RunInfo` and Handlers because there is no Graph to do it for you.
-### **Usage Outside of Graph**
+```go
+package main
+
+import (
+ "context"
+
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/compose"
+)
+
+func innerLambda(ctx context.Context, input string) (string, error) {
+ // As provider of ComponentB: ensure default RunInfo when entering the component (Name cannot default)
+ ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda)
+ ctx = callbacks.OnStart(ctx, input)
+ out := "inner:" + input
+ ctx = callbacks.OnEnd(ctx, out)
+ return out, nil
+}
-Use `InitCallbacks` to inject `RunInfo` and `Handlers`. You need to set the fields of `RunInfo` yourself。Global Handlers will be automatically injected。
+func outerLambda(ctx context.Context, input string) (string, error) {
+ // As provider of ComponentA: ensure default RunInfo when entering
+ ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda)
+ ctx = callbacks.OnStart(ctx, input)
+ // Recommended: replace RunInfo before calling inner component, ensuring correct name/type/component
+ ctxInner := callbacks.ReuseHandlers(ctx,
+ &callbacks.RunInfo{Name: "ComponentB", Type: "Lambda", Component: compose.ComponentOfLambda},
+ )
+ out1, _ := innerLambda(ctxInner, input) // inner RunInfo.Name = "ComponentB"
+
+ // Without replacement: framework clears RunInfo after a complete callback cycle; EnsureRunInfo adds defaults (Name empty)
+ out2, _ := innerLambda(ctx, input) // inner RunInfo.Name == ""
+
+ final := out1 + "|" + out2
+ ctx = callbacks.OnEnd(ctx, final)
+ return final, nil
+}
+
+func main() {
+ // Standalone components outside graph: initialize RunInfo and Handlers
+ h := callbacks.NewHandlerBuilder().Build()
+ ctx := callbacks.InitCallbacks(context.Background(),
+ &callbacks.RunInfo{Name: "ComponentA", Type: "Lambda", Component: compose.ComponentOfLambda},
+ h,
+ )
+
+ _, _ = outerLambda(ctx, "ping")
+}
```
-ctx = callbacks.InitCallbacks(ctx, runInfo, handlers...)
-componentA.Invoke(ctx, input)
+
+Notes:
+
+- Initialization: use `InitCallbacks` to set the first `RunInfo` and Handlers when using components outside graph/chain so subsequent components receive the full callback context.
+- Internal calls: before Component A calls Component B, use `ReuseHandlers` to replace `RunInfo` (keeping existing handlers) so B receives correct `Type/Component/Name`.
+- Without replacement: after a complete set of callbacks, Eino clears `RunInfo` from the current context; providers can call `EnsureRunInfo` to supply default `Type/Component` to keep callbacks working, but `Name` cannot be inferred and will be empty.
+
+#### Component Nesting
+
+Scenario: inside a component (e.g., a Lambda), manually call another component (e.g., ChatModel).
+
+If the outer component’s context has handlers, the inner component receives the same handlers. To control whether the inner component triggers callbacks:
+
+1) Want callbacks triggered: set `RunInfo` for the inner component using `ReuseHandlers`.
+
+```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"
+)
+
+// Outer lambda calls ChatModel inside
+func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda {
+ return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ // 1) Reuse outer handlers and set RunInfo explicitly for the inner component
+ innerCtx := callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{
+ Type: "InnerCM",
+ Component: components.ComponentOfChatModel,
+ Name: "inner-chat-model",
+ })
+
+ // 2) Build input messages
+ msgs := []*schema.Message{{Role: schema.User, Content: input}}
+
+ // 3) Call ChatModel (inner implementation triggers its callbacks)
+ out, err := cm.Generate(innerCtx, msgs)
+ if err != nil {
+ return "", err
+ }
+ return out.Content, nil
+ })
+}
```
-If componentA invokes componentB internally(e.g. ToolsNode invokes Tool), you need to replace `RunInfo` before invoking componentB:
+If the inner ChatModel’s `Generate` does not trigger callbacks, the outer component should trigger them around the inner call:
+```go
+func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda {
+ return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ // Reuse outer handlers and set RunInfo explicitly for inner component
+ ctx = callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{
+ Type: "InnerCM",
+ Component: components.ComponentOfChatModel,
+ Name: "inner-chat-model",
+ })
+
+ // Build input messages
+ msgs := []*schema.Message{{Role: schema.User, Content: input}}
+
+ // Explicitly trigger OnStart
+ ctx = callbacks.OnStart(ctx, msgs)
+
+ // Call ChatModel
+ resp, err := cm.Generate(ctx, msgs)
+ if err != nil {
+ // Explicitly trigger OnError
+ ctx = callbacks.OnError(ctx, err)
+ return "", err
+ }
+
+ // Explicitly trigger OnEnd
+ ctx = callbacks.OnEnd(ctx, resp)
+
+ return resp.Content, nil
+ })
+}
```
-func ComponentARun(ctx, inputA) {
- // reuse handlers exist in ctx(including Global Handlers), only replace RunInfo
- ctx = callbacks.ReuseHandlers(ctx, newRunInfo)
- componentB.Invoke(ctx, inputB)
-
- // replace both RunInfo and Handlers
- ctx = callbacks.InitCallbacks(ctx, newRunInfo, newHandlers...)
- componentB.Invoke(ctx, inputB)
+
+2) Do not want inner callbacks: assume the inner component implements `IsCallbacksEnabled()` returning true and calls `EnsureRunInfo`. By default, inner callbacks will trigger. To disable, pass a new context without handlers to the inner component:
+
+```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) {
+ // Use a brand-new context; do not reuse outer 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
+ })
}
```
-### **Handler Reading/Writing of Input & Output**
+Sometimes you may want to disable a specific handler for inner components but keep others. Implement filtering by `RunInfo` inside that handler:
-When Input & Output flow through a graph, it is a direct variable assignment. As shown in the figure below, NodeA.Output, NodeB.Input, NodeC.Input, as well as the input & output obtained in each Handler, if they are reference types such as structure pointers or Maps, are all the same piece of data. Therefore, whether inside a Node or a Handler, modifying Input & Output is not recommended, as it can lead to concurrency issues. Even in a synchronous situation, Node B and Node C may have concurrency, resulting in concurrency between handler1 and handler2 inside them. When there are asynchronous processing logics, there are even more possible concurrent scenarios.
+```go
+package main
-
+import (
+ "context"
+ "log"
-In the scenario of stream transmission, the input streams in all successor nodes and their handlers are streams obtained by StreamReader.Copy(n) and can be read independently of each other. However, for each chunk in the stream, it is a direct variable assignment. If the chunk is a reference type such as a struct pointer or a map, the streams after each copy will read the same data. Therefore, within Node and Handler, it is also not recommended to modify the chunk of the stream, as there are concurrency issues.
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/cloudwego/eino/components"
+ "github.com/cloudwego/eino/compose"
+)
+
+// A selective handler: no-ops for the inner 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" {
+ 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" {
+ return ctx
+ }
+ log.Printf("[OnEnd] %s/%s (%s)", info.Type, info.Name, info.Component)
+ return ctx
+ }).
+ Build()
+}
-
+// Composition example: outer call triggers; selective handler filters out inner ChatModel
+func Example(cm model.BaseChatModel) (compose.Runnable[string, string], error) {
+ handler := newSelectiveHandler()
-### **Information Transfer Between Handlers**
+ chain := compose.NewChain[string, string]().
+ AppendLambda(OuterLambdaCallsChatModel(cm))
-You can transfer information between different time points of the same Handler through `ctx`. For example, in `OnStart`, use `context.WithValue` to return a new context, and retrieve this value from the context in `OnEnd`.
+ return chain.Compile(
+ context.Background(),
+ compose.WithCallbacks(handler),
+ )
+}
+```
-There's no guaranteed execution order between different Handlers, so it's not advised to transfer information between Handlers via this mechanism. Essentially, you cannot ensure that the context returned by one Handler will definitely be used in the execution function of the next Handler.
+### Read/Write Input & Output Carefully
-Should you need to transfer information between Handlers, it’s recommended to set a global, request-scoped variable in the outermost context (like the context passed during graph execution) as a public information storage space. You can read and update this public variable as needed in each Handler. For streams, extra attention is needed to ensure the thread safety of this public variable.
+Inputs/outputs flow by direct assignment; pointers/maps refer to the same data across nodes and handlers. Avoid mutations inside nodes and handlers to prevent race conditions.
-### **Always Close Streams**
+
-Take a node like `ChatModel`, which has true stream output, for example. When there is a Callback aspect, the output stream from `ChatModel`:
+### Stream Closing
-- Must be consumed as input by downstream nodes and also be consumed by the Callback aspect.
-- A single frame (Chunk) in a stream can only be consumed by one consumer, meaning the stream is not broadcast.
+With true streaming components (e.g., ChatModel streams), callback consumers and downstream nodes both consume the stream. Streams are copied per consumer; ensure callback readers close their streams to avoid blocking resource release.
-Thus, the stream needs to be duplicated, with the duplication relationship as follows:
+
-
+### Passing Information Between Handlers
-- If one of the Callbacks does not close the corresponding stream, it may result in the original Stream being unable to close and release resources.
+Use the returned `context.Context` to pass information across timings within the same handler (e.g., set with `context.WithValue` in `OnStart`, read in `OnEnd`). Do not rely on ordering between different handlers; if sharing data is required, store request-scoped shared state on the outermost context and ensure concurrency safety.
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
index 79f7e9e129d..d11042c20a1 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-03-28"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: Chain/Graph Orchestration Introduction'
weight: 1
---
-> All code examples in this document can be found at: [https://github.com/cloudwego/eino-examples/tree/main/compose](https://github.com/cloudwego/eino-examples/tree/main/compose)
+> All sample code is available at: [https://github.com/cloudwego/eino-examples/tree/main/compose](https://github.com/cloudwego/eino-examples/tree/main/compose)
## Graph Orchestration
@@ -47,7 +47,7 @@ func main() {
_ = g.AddEdge(nodeOfPrompt, nodeOfModel)
_ = g.AddEdge(nodeOfModel, compose.END)
- r, err := g.Compile(ctx, compose.WithMaxRunSteps(10))
+ r, err := g.Compile(ctx)
if err != nil {
panic(err)
}
@@ -86,8 +86,8 @@ func (m *mockChatModel) Stream(ctx context.Context, input []*schema.Message, opt
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)
+ sw.Send(schema.AssistantMessage("the weather is", nil), nil)
+ sw.Send(schema.AssistantMessage("good", nil), nil)
}()
return sr, nil
}
@@ -161,15 +161,15 @@ func main() {
userInfoTool := utils.NewTool(
&schema.ToolInfo{
Name: "user_info",
- Desc: "Query the user's company, position, and salary information based on the user's name and email. ",
+ Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"name": {
Type: "string",
- Desc: "The user's name",
+ Desc: "用户的姓名",
},
"email": {
Type: "string",
- Desc: "User's mailbox",
+ Desc: "用户的邮箱",
},
}),
},
@@ -212,8 +212,8 @@ func main() {
)
// 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
+ // input type is 1st Graph Node's input type: map[string]any
+ // output type is last Graph Node's output type: []*schema.Message
g := compose.NewGraph[map[string]any, []*schema.Message]()
// 7. add ChatTemplate into graph
@@ -225,16 +225,13 @@ func main() {
// 9. add ToolsNode into graph
_ = g.AddToolsNode(nodeKeyOfTools, toolsNode)
- // 10. add connection between nodes
+ // 10. connect 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]
+ // compile Graph[I, O] to Runnable[I, O]
r, err := g.Compile(ctx)
if err != nil {
logs.Errorf("Compile failed, err=%v", err)
@@ -243,16 +240,14 @@ func main() {
out, err := r.Invoke(ctx, map[string]any{
"message_histories": []*schema.Message{},
- "user_query": "My name is zhangsan, and my email is [zhangsan@bytedance.com](mailto:zhangsan@bytedance.com). Please recommend a property for me.",
+ "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)
- }
+ for _, msg := range out { logs.Infof(" %v", msg) }
}
type userInfoRequest struct {
@@ -296,19 +291,17 @@ func (l *loggerCallbacks) OnEndWithStreamOutput(ctx context.Context, info *callb
### Graph with state
-Graph can own a 'global' state, created by using the WithGenLocalState Option during NewGraph:
+Graph can have a graph-level "global" state. Enable it via `WithGenLocalState` when creating the Graph:
```go
// compose/generic_graph.go
// type GenLocalState[S any] func(ctx context.Context) (state S)
-func WithGenLocalState[S any](gls GenLocalState[S]) NewGraphOption {
- // --snip--
-}
+func WithGenLocalState[S any](gls GenLocalState[S]) NewGraphOption { /* ... */ }
```
-When adding nodes, add Pre/Post Handlers to process this State:
+Add nodes with Pre/Post Handlers to process state:
```go
// compose/graph_add_node_options.go
@@ -316,16 +309,11 @@ When adding nodes, add Pre/Post Handlers to process this State:
// 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--
-}
+func WithStatePreHandler[I, S any](pre StatePreHandler[I, S]) GraphAddNodeOpt { /* ... */ }
+func WithStatePostHandler[O, S any](post StatePostHandler[O, S]) GraphAddNodeOpt { /* ... */ }
```
-Within nodes, use `ProcessState` to process State by passing in a function that can read/write state:
+Inside a node, use `ProcessState` to read/write state:
```go
// flow/agent/react/react.go
@@ -342,7 +330,7 @@ err = compose.ProcessState[*state](ctx, func(_ context.Context, state *state) er
})
```
-Full example:
+Complete example:
```go
package main
@@ -367,151 +355,70 @@ func main() {
const (
nodeOfL1 = "invokable"
- nodeOfL2 = "streamable"
- nodeOfL3 = "transformable"
+ nodeOfL2 = "streamable"
+ nodeOfL3 = "transformable"
)
- type testState struct {
- ms []string
- }
-
- gen := func(ctx context.Context) *testState {
- return &testState{}
- }
+ 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))
-
- go func() {
- for _, field := range strings.Fields(outStr) {
- sw.Send(field+" ", nil)
- }
- sw.Close()
- }()
-
- return sr, nil
+ 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) (*schema.StreamReader[string], error) {
+ outStr := "StreamableLambda: " + input
+ sr, sw := schema.Pipe[string](utf8.RuneCountInString(outStr))
+ 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
- }
-
+ 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
+ l3 := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (*schema.StreamReader[string], error) {
+ prefix := "TransformableLambda: "
+ sr, sw := schema.Pipe[string](20)
+ go func() {
+ defer func() { if panicErr := recover(); 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 } ; 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
+ 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
- }
+ 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
- }
+ 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)
- }
+ 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)
@@ -519,31 +426,15 @@ func main() {
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)
- }
+ 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 can be regarded as a simplified encapsulation of Graph.
+> Chain can be considered a simplified wrapper over Graph
```go
package main
@@ -570,105 +461,54 @@ func main() {
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) {
- if rand.Intn(randLimit) == 1 {
- return "b1", nil
- }
-
+ 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")
- }
-
+ 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
+ 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 "What is your cry like? ", 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
+ parallel.AddLambda("role", compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (string, error) {
+ 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" }
cm, err := openai.NewChatModel(context.Background(), modelConf)
- if err != nil {
- log.Panic(err)
- return
- }
+ 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)
+ 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
- }
+ chain.AppendLambda(compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) {
+ logs.Infof("in view lambda: %v", kvs)
+ return kvs, nil
+ })).AppendBranch(compose.NewChainBranch(branchCond).AddLambda("b1", b1).AddLambda("b2", b2)).AppendPassthrough().AppendParallel(parallel).AppendGraph(rolePlayerChain).AppendLambda(compose.InvokableLambda(func(ctx context.Context, m *schema.Message) (string, error) {
+ logs.Infof("in view of messages: %v", m.Content)
+ return m.Content, nil
+ }))
+ 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
- }
-
+ if err != nil { log.Panic(err); return }
logs.Infof("output is : %v", output)
}
```
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
new file mode 100644
index 00000000000..f226fa7281d
--- /dev/null
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
@@ -0,0 +1,401 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino: Interrupt & CheckPoint Manual'
+weight: 7
+---
+
+> Note: A bug in v0.3.26 broke CheckPoint serialization. For new CheckPoint usage, use v0.3.26+ (preferably latest).
+>
+> Eino provides a compatibility branch for users with pre-v0.3.26 checkpoints to migrate; once old data is invalidated, upgrade to mainline. The branch incurs overhead and is not merged due to typical short checkpoint lifetimes.
+
+## Introduction
+
+`Interrupt & CheckPoint` lets you pause a Graph at specified locations and resume later. For `StateGraph`, you can modify `State` before resuming.
+
+> Resuming restores inputs and per-node runtime data. Ensure the Graph orchestration is identical and pass the same CallOptions again (unless you explicitly rely on CallOptions to carry resume-time data).
+
+## Using Static Interrupt
+
+Static Interrupt supports pausing before or after specified nodes. Set `WithInterruptAfterNodes` and `WithInterruptBeforeNodes` at compile:
+
+```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 */}
+}
+```
+
+> Tip: Currently only compile-time static breakpoints are supported. If you need request-time configuration, please open an issue.
+
+Extract interrupt info from the run error:
+
+```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)
+```
+
+Example:
+
+```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
+}
+```
+
+> During interrupt, the output is empty and should be ignored.
+
+## Using CheckPoint
+
+CheckPoint records Graph runtime state to support resuming.
+
+### Implement CheckpointStore
+
+`CheckpointStore` is a KV storage with key type `string` and value type `[]byte`. Eino does not provide a default implementation; implement your own to persist checkpoints.
+
+```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)
+}
+```
+
+### Register Types For Serialization
+
+Saving and loading checkpoints involves serialization of node inputs/outputs and `State`. For simple or Eino built-in types (e.g., `Message`, `Document`), no action is needed. For custom structs, register types in advance with `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.
+ // Recommended to register within init() in the same file where the type is declared.
+ schema.RegisterName[*MyState]("my_state_v1")
+}
+```
+
+After registration, type metadata is included during serialization. On deserialization, Eino can restore the correct type even when the destination is `interface{}`. The key uniquely identifies the type; once chosen, do not change it, otherwise persisted checkpoints cannot be restored.
+
+> Struct unexported fields are inaccessible and thus not stored/restored.
+
+By default, Eino uses its built-in serializer. If a registered type implements `json.Marshaler` and `json.Unmarshaler`, those custom methods are used.
+
+```
+// encoding/json
+
+type Marshaler interface {
+ MarshalJSON() ([]byte, error)
+}
+
+type Unmarshaler interface {
+ UnmarshalJSON([]byte) error
+}
+```
+
+Eino also provides an option to use `gob` serialization:
+
+```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{}))
+```
+
+Choose based on preference; avoid switching later, as historical data will be incompatible.
+
+### Enable CheckPoint
+
+Bind `CheckpointStore` at compile and set interrupt nodes if needed:
+
+```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 */}
+}
+```
+
+At request time, provide a checkpoint ID:
+
+```go
+func WithCheckPointID(checkPointID string, sm StateModifier) Option
+```
+
+The checkpoint ID is used as the `CheckpointStore` key. During execution, if the ID exists, the graph resumes from it; on interrupt, the graph stores its state under that ID.
+
+```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
+ }),
+)
+```
+
+> During resume, input is ignored; pass a zero value.
+
+## Dynamic Interrupt
+
+Nodes can trigger dynamic interrupts by returning special errors.
+
+### Prior to 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
+```
+
+When the graph receives such an error, it interrupts. On resume, the node runs again; before rerun, `StateModifier` is applied if configured. The rerun’s input is replaced with a zero value rather than the original; if the original input is needed, save it into `State` beforehand.
+
+### From Eino v0.7.0 onward
+
+Support is added for local state persistence, exposing inner interrupt signals, and parallel interrupts:
+
+```
+// 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)
+```
+
+See design details: [Eino human-in-the-loop framework: architecture guide](/docs/eino/core_modules/eino_adk/agent_hitl)
+
+## CheckPoint in Streaming
+
+Streaming checkpoints require concatenation of chunks. Register a concat function:
+
+```go
+func RegisterStreamChunkConcatFunc[T any](fn func([]T) (T, error))
+
+// example
+type TestStruct struct {
+ Body string
+}
+
+// RegisterStreamChunkConcatFunc is not thread-safe; call during initialization
+RegisterStreamChunkConcatFunc(func(ss []TestStruct)(TestStruct, error){
+ ret := TestStruct{Body:""}
+ for i := range ss {
+ ret.Body += ss[i].Body
+ }
+ return ret, nil
+})
+```
+
+Eino provides defaults for `*schema.Message`, `[]*schema.Message`, and `string`.
+
+## Interrupt & CheckPoint in Nested Graphs
+
+When the parent sets a `CheckpointStore`, use `WithGraphCompileOptions` during `AddGraphNode` to configure child interrupts:
+
+```go
+g.AddGraphNode("node1", subGraph, WithGraphCompileOptions(
+ WithInterruptAfterNodes([]string{"node2"}),
+))
+
+g.Compile(ctx, WithCheckPointStore(cp))
+```
+
+If a child interrupts, resuming modifies the child’s state. TODO: clarify Path usage in `StateModifier`.
+
+## Recovery
+
+Recovery: subsequent graph runs after an interrupt and checkpoint save.
+
+### Prior to Eino v0.7.0
+
+Modify `State` to affect resume behavior.
+
+```go
+// compose/checkpoint.go
+
+type StateModifier func(ctx context.Context, path NodePath, state any) error
+func WithStateModifier(sm StateModifier) GraphCompileOption
+```
+
+`StateModifier` is applied during graph resume, before execution. `path` applies to nested graphs; for non-nested graphs, it is an empty slice.
+
+```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
+ }),
+)
+```
+
+> During resume, input is ignored; pass a zero value.
+
+### From Eino v0.7.0 onward
+
+In addition to `StateModifier`, you can selectively resume particular interrupt points and provide resume data:
+
+```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` is retrieved from the interrupt error:
+
+```go
+interruptInfo, isInterrupt := ExtractInterruptInfo(err)
+if isInterrupt {
+ // possibly multiple interrupt points; take the first for illustration
+ interruptID = interruptInfo.InterruptContexts[0].ID
+}
+```
+
+`resumeData` is defined by the interrupt point. For example, a Tool interrupts to request approval and defines `ApprovalResult` as resume data:
+
+```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 { // 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
+}
+```
+
+## Examples
+
+- https://github.com/cloudwego/eino-examples/tree/main/compose/graph/react_with_interrupt
+- https://github.com/cloudwego/eino/blob/main/compose/resume_test.go
+
+- `TestInterruptStateAndResumeForRootGraph`: simple dynamic interrupt
+- `TestInterruptStateAndResumeForSubGraph`: subgraph interrupt
+- `TestInterruptStateAndResumeForToolInNestedSubGraph`: nested subgraph tool interrupt
+- `TestMultipleInterruptsAndResumes`: parallel interrupts
+- `TestReentryForResumedTools`: tool interrupt in ReAct Agent, multiple re-entries after resume
+- `TestGraphInterruptWithinLambda`: Lambda node contains a standalone Graph and interrupts internally
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md
deleted file mode 100644
index a96dabaa3c4..00000000000
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md
+++ /dev/null
@@ -1,246 +0,0 @@
----
-Description: ""
-date: "2025-08-07"
-lastmod: ""
-tags: []
-title: 'Eino: Interrupt & CheckPoint User Manual'
-weight: 6
----
-
-# Introduction
-
-Using the Interrupt & CheckPoint feature, it is possible to pause the execution of the Graph at a specified location and resume it from the breakpoint later, and if it is a StateGraph, the State can also be modified before resuming from the breakpoint.
-
-> 💡
-> Resume from breakpoint can only restore the data generated by each node during input and runtime,and it is necessary to ensure that the Graph orchestration andCallOptionsare exactly the same.
-
-# Use Interrupt
-
-Interrupt supports pausing the Graph before or after the execution of a specified Node. During Compile, pass in the WithInterruptAfterNodes and WithInterruptBeforeNodes Options to set 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 */}
-}
-```
-
-> 💡
-> Currently, setting breakpoints is only supported during Compile. If you need to set them during requests, feel free to submit your suggestions~
-
-Whether the current run is interrupted and the interrupt information can be obtained from the error returned by the run:
-
-```go
-// compose/checkpoint.go
-
-type InterruptInfo struct {
- State any
- BeforeNodes []string
- AfterNodes []string
- SubGraph map[string]InterruptInfo
-}
-
-func ExtractInterruptInfo(err error) (info *InterruptInfo, existed bool) {}
-```
-
-For example:
-
-```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
-}
-```
-
-> 💡
-> When Interrupt occurs, the output is null, which is meaningless.
-
-# Using CheckPoint
-
-CheckPoint records the running state of the Graph, and using CheckPoint allows resuming the operation after an Interrupt.
-
-## Implement CheckPointerStore
-
-CheckPointStore is a KV storage interface where the key type is string and the value type is []byte. We do not provide encapsulation or default implementation, so users need to implement it themselves to store checkpoints.
-
-```go
-// coompose/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)
-}
-```
-
-## Register serialization method
-
-The saving and loading of CheckPoint involve the serialization and deserialization of the input and output of Graph nodes as well as State. When only simple types or Eino built-in types (such as Message or Document) are used, users do not need to perform additional operations; when custom structs are introduced, types need to be registered in advance, and Eino provides a registration method RegisterSerializableType:
-
-```go
-import "github.com/cloudwego/eino/compose"
-
-type MyStruct struct {
- // struct body
-}
-
-// func RegisterSerializableType[T any](name string) error
-err = compose.RegisterSerializableType[MyStruct]("MyStruct")
-```
-
-The registered type will have additional type information recorded during serialization. Therefore, during deserialization, even if the type is not specified (such as deserializing to interface{}), Eino can still deserialize the correct type. The key in the registration method uniquely identifies this type, and once the key is determined, it must be ensured that it cannot be changed; otherwise, the persisted checkpoint cannot be correctly restored.
-
-> 💡
-> The unexported fields of Struct cannot be accessed, so they will not be stored/restored
-
-If the registered type implements json Marshaler and Unmarshaler, the serialization and deserialization of this type will use custom methods.
-
-```
-// encoding/json
-
-type Marshaler interface {
- MarshalJSON() ([]byte, error)
-}
-
-type Unmarshaler interface {
- UnmarshalJSON([]byte) error
-}
-```
-
-## Turn on CheckPoint
-
-After creating the CheckPointStore, pass it as an option during Compile Graph and bind the CheckPointer to the 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 */}
-}
-```
-
-After that, CheckPoint can be introduced via CallOption when making a request:
-
-```
-// compose/checkpoint.go
-
-func WithCheckPointID(checkPointID string, sm StateModifier) Option
-type StateModifier func(ctx context.Context, path NodePath, state any) error
-func WithStateModifier(sm StateModifier) GraphCompileOption
-```
-
-The Checkpoint id will be used as the key for the CheckPointStore. When the graph runs, it will check if this id exists in the CheckPointStore. If it exists, the graph will resume execution from the checkpoint; interrupt will save the graph state to this id.
-
-StateModifier takes effect when the Graph resumes running, can modify the State before running, and the path takes effect in nested graphs, being an empty array when not nested.
-
-```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
- }),
-)
-```
-
-> 💡
-> When resuming, the input will not be read, so just pass an empty input at this time.
-
-## Dynamic Interrupt
-
-A node returning a special error can dynamically trigger an Interrupt:
-
-```
-// eion/compose/checkpoint.go
-var InterruptAndRerun = errors.New("interrupt and rerun")
-```
-
-After receiving this error returned by the node, Eino Graph will interrupt. When resuming operation, it will run this node again, and before running again, it will call StateModifier to modify the state (if configured).
-
-In this case, when the node is run again, the input will be replaced with a null value instead of the original input. If the original input is still needed when running again, it needs to be saved to the State in advance.
-
-# CheckPoint in Streaming
-
-Streaming requires splicing data streams when saving CheckPoint, so a splicing method needs to be registered:
-
-```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 provides concat methods for *schema.Message, []*schema.Message, and string by default.
-
-# Interrupt&CheckPoint in the nested graph
-
-When the parent graph passes a CheckPointer, passing InterruptNodes via WithGraphCompileOptions when calling AddAnyGraph can enable Interrupt&CheckPoint for the child graph. If the parent graph does not set a CheckPointer, an error will be reported during Compile.
-
-```go
-/* graph compose code
-xxx
-*/
-g.AddAnyGraph("node1", subGraph, WithGraphCompileOptions(
- WithInterruptAfterNodes([]string{"node2"}),
-))
-
-g.Compile(ctx, WithCheckPointer(cp))
-```
-
-If interrupted in the subgraph, the state modified upon resume should be the subgraph state. TODO, explain the use of Path in StateModifier
-
-# Example
-
-[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)
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
index 2dd05834e4c..64561088024 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
@@ -1,479 +1,282 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino: The design concept of orchestration'
+title: 'Eino: Orchestration Design Principles'
weight: 2
---
-In the LLM application orchestration solutions, the most popular ones are langchain and langgraph, which officially provide SDKs for Python and TypeScript. These two languages are known for their flexibility, which brings great convenience to SDK development but also causes significant confusion and cognitive burden for SDK users.
+`langchain`/`langgraph` are popular orchestration solutions in Python/TS — both highly flexible languages. Flexibility accelerates SDK development but often burdens users with ambiguity. Go’s simplicity and static typing help reduce cognitive load. Eino embraces this with “deterministic types” plus “compile-time type checking”.
-As an extremely simple programming language, Go's defined `static types` are one of the key reasons why it remains straightforward, and Eino maintains this critical characteristic: `defined types` + `compile-time type checking`.
+## Upstream–Downstream Type Alignment as a First Principle
-## Basic Principle of `Type Alignment` in Upstream and Downstream
+Eino’s orchestration centers on Graph (and simplified Chain). Fundamentally, it’s “logic nodes” plus “upstream/downstream relations”. At runtime, outputs of one node become inputs of the next.
-The most fundamental orchestration method in eino is the graph, along with the simplified wrapped chain. Regardless of the orchestration method, it essentially consists of `logical nodes` + `upstream and downstream relationships`. At runtime, the product of the orchestration starts from one logical node and then proceeds to run the next node that is connected to the current node.
+We assume: the upstream output can be fed to the downstream input.
-This entails a basic assumption: **the output value of the previous node can serve as the input value for the next node.**
+In Go, two approaches:
-In Go, there are two basic approaches to achieving this assumption:
+1) Use generalized types (e.g., `any`, `map[string]any`) everywhere.
+ - With `any`, developers must assert types repeatedly; high cognitive load.
+ - With `map[string]any`, nodes extract values by keys. Still requires type assertions, not ideal.
-1. Convert the input and output of different nodes into a more generalized type, such as `any` or `map[string]any`.
- 1. Adopting the approach to generalize into any, but the corresponding cost is that developers need to explicitly convert it into a specific type when writing code to use it. This significantly increases the mental burden on developers, hence this approach was ultimately abandoned.
- 2. The langchain approach can be seen as passing `map[string]any` throughout the process, where each logical node retrieves the corresponding value with the corresponding key based on its needs. In langchaingo's implementation, this approach is adopted. However, in Go, they still need to be used with `type assertions`. This approach still imposes a significant mental burden on the developer.
-2. Keep the input and output types of each node as expected by the developer, ensuring type consistency between upstream and downstream during the Compile phase.
+2) Preserve each node’s expected types, and enforce upstream–downstream compatibility at compile time.
-Approach 2 is the final approach selected by eino. This approach is the easiest to understand during orchestration. The whole process is like `building blocks`, where the protruding and recessed parts of each block have their own specifications, and only matching specifications can form upstream and downstream relationships.
+Eino chooses (2). Orchestration becomes like “LEGO”: only matching studs/sockets connect.
-As shown in the diagram below:
+
-
+Only downstream nodes that understand upstream outputs can run. Eino makes this explicit so developers can build with confidence instead of guessing with `any`.
-For orchestration, it can only run properly if the downstream can recognize and process the upstream output. This basic assumption is clearly expressed in eino, allowing developers to be confident and clear about how orchestration logic operates and flows when using eino, rather than guessing whether the passed values are correct from a series of any.
+### Type Alignment in Graph
-### **Type Alignment in Graph**
+#### Edges
-#### **Edge**
+
-In a graph, the output of a node will flow to the next node along an `edge`, so the nodes connected by an edge must have type alignment.
+Edges require assignable types:
-As shown in the figure below:
+1) Same types: e.g., upstream `*schema.Message` → downstream `*schema.Message`.
+2) Downstream expects an interface that upstream implements. Special case: downstream `any` — everything assigns.
+3) Upstream is an interface, downstream is a concrete type: depends on runtime concrete type; compile-time cannot guarantee. Only when the upstream concrete type implements the downstream expectation will it work.
-> This is a scenario simulating ① Direct conversation with a LLM ② Using RAG mode, and the final results can be used to compare the effects of the two modes
+Yellow paths show Eino’s map conversion: when downstream needs `map[string]any` but upstream doesn’t produce it, use `compose.WithOutputKey("outkey")` to wrap upstream output into a map with the given key. Similarly, `compose.WithInputKey("inkey")` lets downstream pick a specific key from upstream’s map output.
-
+#### Branches
-The green part in the figure represents the ordinary edge connection, which requires that the upstream output must be able to be `assigned` downstream, and the types that can be accepted are:
+A node with multiple edges runs all downstream nodes. A `Branch` chooses exactly one downstream based on a condition function. All branch targets must be type-compatible with upstream outputs.
-① The same type for both upstream and downstream: For example, the upstream output is *schema.Message, and the downstream input is also *schema.Message.
+
-② The downstream accepts an interface, and the upstream implements that interface: For example, the upstream structure implements the Format() interface, and the downstream accepts an interface{ Format() }. A special situation is when the downstream is any (empty interface), the upstream definitely implements any, so they can certainly connect.
+### Type Alignment in Chain
-③ The upstream is an interface, and the downstream is a specific type: When the downstream specific type implements the upstream's interface type, it may or may not work, which cannot be determined at compile time, only at runtime when the explicit type of upstream is determined. For detailed description, see: [Eino: The design concept of orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+#### Chain
-The yellow part in the figure represents another type conversion mechanism provided by eino, that is: if downstream receives a type `map[string]any`, but the upstream output type is not map[string]any, you can use `graph.AddXXXNode(node_key, xxx, compose.WithOutputKey("outkey")` to convert the upstream output type to map[string]any, where the key of the map is the OutputKey specified in the option. This mechanism is generally convenient to use when multiple edges converge to a single node.
+
-#### **Branch**
-
-If a node is connected to multiple edges, the downstream nodes of each edge will run once. Branch is another mechanism: a branch followed by n nodes will only run the node corresponding to the key returned by the condition. The nodes following the same branch must also be type aligned.
-
-As shown in the figure below:
-
-> This is a scenario simulating the running logic of a react agent
-
-
-
-You can see that a branch itself has a `condition`, the input of this function must be type aligned with the upstream. At the same time, the nodes following the branch must also be able to receive the upstream's output just like the condition.
-
-### Type Alignment in Chains
-
-#### **Chain**
-
-From an abstract perspective, a chain is a `sequence`, as shown below:
-
-
-
-The types of logical nodes can be divided into three categories:
-
-- Orchestrable components (e.g., chat model, chat template, retriever, lambda, graph, etc.)
-- Branch nodes
-- Parallel nodes
-
-As can be seen, from the perspective of a chain, whether it is a simple node (e.g., chat model) or a complex node (e.g., graph, branch, parallel), they are treated the same. During execution, each step corresponds to the execution of a node.
-
-Therefore, the types of upstream and downstream nodes in a chain must be aligned, as shown below:
+All node pairs must align. Example:
```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 := compose.NewChain[map[string]interface{}, string]()
+chain.
+ AppendChatTemplate(&fakeChatTemplate{}).
+ AppendLambda(&fakeLambda{}).
+ AppendChatModel(&fakeChatModel{}).
+ AppendLambda(&fakeLambda{})
```
-The logic above can be represented by the following diagram:
-
-If the types of upstream and downstream nodes are not aligned, the chain will return an error during chain.Compile(). In contrast, the graph will report an error when calling graph.AddXXXNode().
+Misalignment causes compile errors: Chain errors at `Compile()`, Graph errors at `AddXXXNode()`.
-#### parallel
+#### Parallel
-Parallel is a special type of node within a chain. From the perspective of the chain, a parallel node is no different from other nodes. Internally, the basic topology of a parallel node is as follows:
+
-
+Parallel assumes exactly one node per branch (that node can itself be a Graph). All parallel nodes must accept the upstream’s output type. Parallel outputs a `map[string]any`, with keys from `AddXXX(outKey, ...)` and values as node outputs.
-One structure formed by multiple edges in the graph is illustrated here. The basic assumption is that there is exactly one node on each edge of a parallel node. Of course, that one node can also be a graph. However, note that the current framework does not directly provide the capability to nest branch or parallel within a parallel node.
+
-Each node within a parallel node has the same upstream node, and thus they must align with the output type of the upstream node. For example, if the upstream node outputs `*schema.Message` as shown in the diagram, then each node must be able to receive this type. The receiving method is consistent with that in the graph, typically using `same type`, `interface definition`, `any`, or `input key option`.
+#### Branch in Chain
-The output of a parallel node is always a `map[string]any`, where the key is specified as output_key when calling `parallel.AddXXX(output_key, xxx, opts...)`, and the value is the actual output of the node.
-
-An example of constructing a parallel node is as follows:
-
-```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)
-}
-```
-
-A parallel node from the perspective of a chain looks like this:
-
-> The diagram simulates the same question being answered by different LLMs. The results can be used for comparison.
-
-
-
-> It is important to note that this structure is only a logical perspective. Since the chain itself is also implemented using a graph, the parallel node will be spread out in the underlying graph.
-
-#### branch
-
-The branch in a chain is similar to the branch in a graph; all nodes in the branch must be aligned with the type of upstream nodes. This will not be elaborated further here. The special nature of a chain branch is that all possible branch nodes in the branch will either connect to the same node in the chain or all connect to END.
+Similar to Graph; all branch targets must align. Chain branches typically converge to the same downstream node or END.
### Type Alignment in Workflow
-The dimensions of type alignment in Workflow have been changed from the overall Input & Output to the field level. Specifically, it can be divided into:
+Field-level mapping replaces whole-object alignment:
-- The overall output of the upstream is type-aligned to a specific field downstream.
-- A specific field of upstream output is type-aligned to the overall downstream.
-- A specific field of upstream output is type-aligned to a specific field of downstream input.
+- Whole output → specific field
+- Specific field → whole input
+- Specific field → specific field
-The principles and rules are the same as for overall type alignment.
+Same principles apply as whole-object alignment.
-### Type Alignment of StateHandler
+### StateHandlers
-StatePreHandler: The input type needs to be aligned with the non-streaming input type of the corresponding node.
+StatePreHandler: input type must align with the node’s non‑streaming input type.
```go
-// The input type is []*schema.Message, which is aligned with the non-streaming input type of ChatModel
+// input type: []*schema.Message, aligns to ChatModel non‑streaming input
preHandler := func(ctx context.Context, input []*schema.Message, state *state) ([]*schema.Message, error) {
// your handler logic
}
+
AddChatModelNode("xxx", model, WithStatePreHandler(preHandler))
```
-StatePostHandler: The input type needs to be aligned with the non-streaming output type of the corresponding node.
+StatePostHandler: input type must align with the node’s non‑streaming output type.
```go
-// The input type is *schema.Message, which is aligned with the non-streaming output type of ChatModel
+// input type: *schema.Message, aligns to ChatModel non‑streaming output
postHandler := func(ctx context.Context, input *schema.Message, state *state) (*schema.Message, error) {
// your handler logic
}
+
AddChatModelNode("xxx", model, WithStatePostHandler(postHandler))
```
-StreamStatePreHandler: The input type needs to be aligned with the streaming input type of the corresponding node.
+StreamStatePreHandler: input type must align with the node’s streaming input type.
```go
-// The input type is *schema.StreamReader[[]*schema.Message], which is aligned with the streaming input type of ChatModel
+// input type: *schema.StreamReader[[]*schema.Message], aligns to ChatModel streaming input
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: The input type needs to be aligned with the streaming output type of the corresponding node.
+StreamStatePostHandler: input type must align with the node’s streaming output type.
```go
-// The input type is *schema.StreamReader[*schema.Message], which is aligned with the streaming output type of ChatModel
+// input type: *schema.StreamReader[*schema.Message], aligns to ChatModel streaming output
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))
-```
-
-### **Alignment of Types under invoke and stream**
-
-In Eino, the result of orchestration is either a graph or a chain. To execute it, you need to use `Compile()` to generate a `Runnable` interface.
-One important function of Runnable is to provide four calling methods: "Invoke", "Stream", "Collect", and "Transform".
-
-> You can check the introduction of the above calling methods and detailed runnable introduction in: [Eino: Overview](/docs/eino/overview)
-
-Suppose we have a `Graph[[]*schema.Message, []*schema.Message]`, which contains a ChatModel node and a Lambda node. After compiling, it becomes a `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)
-}
+AddChatModelNode("xxx", model, WithStreamStatePostHandler(postHandler))
```
-When we call the compiled Runnable in Stream mode, the model node outputs `*schema.StreamReader[*Message]`, but the lambda node is an InvokableLambda that only accepts non-streaming `*schema.Message` as input. This complies with the type alignment rule because the Eino framework automatically concatenates streaming Messages into a complete Message.
-
-In stream mode, concatenating frames is a very common operation. During concatenation, all elements from `*StreamReader[T]` are first extracted and converted into `[]T`, and then an attempt is made to concatenate `[]T` into a complete `T`. The framework has built-in support for concatenating the following types:
+### Invoke vs Stream Alignment
-- `*schema.Message`: See `schema.ConcatMessages()` for details
-- `string`: The implementation logic is equivalent to `+=`
-- `[]*schema.Message`: See `compose.concatMessageArray()` for details
-- `Map`: Merge values with the same key; the merging logic is the same as above. If there is an un-combinable type, it fails (ps: it is not overwritten)
-- `Struct` or `Struct pointer`: First converted into `[]map[string]any`, then merged according to the map logic. The struct must not contain unexported fields.
-- Other slices: Can only be merged if there is exactly one non-zero value element in the slice.
+`Runnable` offers `Invoke/Stream/Collect/Transform`. See [Streaming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
-For other scenarios, or when users want to override the default behavior with custom logic, developers can implement their own concat method and register it to the global concatenation function using `compose.RegisterStreamChunkConcatFunc()`.
-
-Here is an example:
+Assume a `Graph[[]*schema.Message, []*schema.Message]` with a ChatModel node and a Lambda node, compiled to `Runnable[[]*schema.Message, []*schema.Message]`:
```go
-// Suppose our own structure is as follows
-type tStreamConcatItemForTest struct {
- s string
-}
-
-// Implement a splicing method
-func concatTStreamForTest(items []*tStreamConcatItemForTest) (*tStreamConcatItemForTest, error) {
- var s string
- for _, item := range items {
- s += item.s
- }
-
- return &tStreamConcatItemForTest{s: s}, nil
-}
-
-func Init() {
- // Register in the global splicing method
- compose.RegisterStreamChunkConcatFunc(concatTStreamForTest)
+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, _ := g1.Compile(ctx)
+c, _ := runner.Invoke(ctx, []*schema.Message{ schema.UserMessage("what's the weather in beijing?") })
+s, _ := runner.Stream(ctx, []*schema.Message{ schema.UserMessage("what's the weather in beijing?") })
+var fullStr string
+for {
+ chunk, err := s.Recv()
+ if err != nil {
+ if err == io.EOF { break }
+ panic(err)
+ }
+ fullStr += chunk
}
```
-### **Runtime Type Alignment Check Scenarios**
+In Stream mode, ChatModel outputs `*schema.StreamReader[*schema.Message]`, while the downstream InvokableLambda expects non‑stream `*schema.Message`. Eino auto‑concatenates streamed frames into a full message, satisfying type alignment.
-Eino's Graph type alignment check will verify if the types of the two nodes match during `err = graph.AddEdge("node1", "node2")`. This allows for type mismatch errors to be identified either during the `graph construction process` or the `Compile process`, adhering to rules ①②③ as listed in [Eino: The design concept of orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles).
+Concatenation behavior:
+- `*schema.Message`: see `schema.ConcatMessages()`
+- `string`: equivalent to `+=`
+- `[]*schema.Message`: concatenated via framework helper
+- `Map`: merge values by key with type‑appropriate concatenation; fails if types cannot be merged
+- Other slices: only concatenated when exactly one element is non‑zero
-When the upstream node's output is an `interface`, and the downstream node type implements that `interface`, it is likely that upstream can be converted to downstream type (type assertion). However, whether the conversion succeeds can only be determined during the `runtime process`, so type checks in this scenario are deferred to runtime.
+You can override defaults by registering custom concat functions via `compose.RegisterStreamChunkConcatFunc`.
-The structure is shown in the diagram below:
+### Runtime Type Checks
-
+Graph verifies type alignment during `AddEdge("node1", "node2")` and at `Compile()` for rules above. When upstream outputs an interface and the downstream expects a concrete type, the final assignability is only known at runtime once the upstream concrete type is available; the framework performs runtime checks for that scenario.
-This scenario is suitable for developers who can handle upstream and downstream type alignment on their own and choose the appropriate downstream execution nodes based on different types.# User Manual for Eino
+
-## Opinionated **Design Choices**
+## Opinionated Design Choices
-### Principle of Read-Only External Variables
+### External Variables Are Read-Only
-When data in Eino's Graph flows among Nodes, Branches, and Handlers, it is always variable assignment, not copying. When the Input is of a reference type, such as a Struct pointer, map, or slice, modifying the Input inside Nodes, Branches, or Handlers will have side effects on the outside and may lead to concurrency issues. Therefore, Eino follows the principle of read-only external variables: do not modify the Input inside Nodes, Branches, or Handlers. If modification is required, make a copy first.
+Data flows in Graph across Nodes, Branches, and Handlers via direct assignment, not deep copies. When inputs are reference types (struct pointers, maps, slices), mutating them inside Nodes/Branches/Handlers causes side effects and potential races. Treat external inputs as read-only; if mutation is needed, copy first. The same applies to streamed chunks in `StreamReader`.
-This principle also applies to Chunks in the StreamReader.
+### Fan‑in and Merge
-### **Fan-In and Merging**
+Multiple upstreams can feed into one downstream. Define how to merge outputs:
-**Fan-in**: Outputs from multiple predecessor nodes converge to a successor node and together serve as the input for the successor node. It is necessary to clearly define how the outputs of multiple predecessor nodes are **merged**. Eino's choice is as follows. First, it requires that the **actual types** of the outputs of multiple predecessor nodes must be the same and of a mergeable type.
-
-Mergeable types can be:
-
-- Map type, and the keys are not repeated among each other.
-- Any type, and a merge function is registered through `compose.RegisterValuesMergeFunc`.
-
-Second,
-
-- In non-streaming scenarios,
- - if the input type has a registered merge function, it will be merged into a single value using the registered function.
- - if the input type is a Map, it will be merged into a single Map containing all key-value pairs from all upstream sources.
-- In streaming scenarios, multiple upstream StreamReaders of the same type are merged into one StreamReader. When actually receiving data from the merged StreamReader, the effect is to read fairly from multiple upstream StreamReaders.
-
-When adding a node (AddNode), you can add the WithOutputKey option to convert the output of the node into a Map:
+- Without custom merge functions, upstream actual types must be identical and be a `map`; keys must be disjoint. Non‑stream: merged into one map; stream: merged into one `StreamReader` with fair reading.
+- Use `WithOutputKey` to convert a node’s output into a map:
```go
-// The output of this node will be changed from string to map[string]any.
-// And there is only one element in the map. The key is your_output_key, and the value is the actual string output by the node.
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"))
```
-You can also register custom merge method to support any type:
+- Register custom merge:
```go
// eino/compose/values_merge.go
func RegisterValuesMergeFunc[T any](fn func([]T) (T, error))
```
-Workflow can map the output fields of multiple predecessor nodes to different input fields of the successor node. Eino converts the Struct output from each predecessor to a Map before any merge process, still conforming to the above rules.
-
-### **Streaming Processing**
-
-Eino believes that components should only need to implement genuine streaming paradigms from the business scenario. For example, a ChatModel does not need to implement Collect. Therefore, in orchestration scenarios, Eino automatically completes the **missing streaming paradigms** for all nodes.
-
-When running a Graph via Invoke, all internal nodes operate in Invoke mode. When running a Graph via Stream, Collect, or Transform, all internal nodes operate in Transform mode.
-
-**Auto Concatenate**: In scenarios where Stream chunks are concatenated into complete content, the user-defined concatenation function registered by the user is preferred. Otherwise, default behaviors provided by the framework are performed, including Message, Message arrays, String, Map, and Struct/Struct pointers.
+Workflow maps fields across nodes; upstream structs are converted to maps, so the same merge rules apply.
-**Auto Boxing**: In scenarios requiring the conversion of non-stream type T to StreamReader[T], the framework automates the process.
+### Streaming Handling
-**Auto Merge**: See the above section on "Fan-In and Merging."
+- Auto concatenate: prefer user‑registered concat functions, then framework defaults (Message, Message array, string, map, struct and pointers)
+- Auto boxing: convert non‑stream `T` to `StreamReader[T]`
+- Auto merge: see Fan‑in above
+- Auto copy: duplicate streams where needed (fan‑out to multiple downstreams, callbacks)
-**Auto Copy**: In scenarios requiring the replication of streams, the framework automatically handles stream copying, including instances where a single stream fans out to multiple downstream nodes, or a single stream enters one or more callback handlers.
+All orchestration elements can sense/handle streams (branch, state handler, callback handler, passthrough, lambda, etc.). See [Streaming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
-Finally, Eino requires all orchestration elements to be aware of and capable of handling streams. This includes branches, state handlers, callback handlers, passthroughs, lambdas, etc.
+### Global State
-For more details on Eino's streaming capabilities, refer to [Eino Points of Streaming Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials).
+- Provide `State` via `compose.WithGenLocalState` when creating a Graph; request‑scoped and readable/writable across steps
+- Use `StatePreHandler` and `StatePostHandler` to read/write state and optionally replace node input/output; match input types to node non‑stream types (and the streaming variants for stream types)
+- External handlers modify inputs/outputs outside nodes, preserving node statelessness
+- Internal state access: `compose.ProcessState[S any](ctx context.Context, handler func(context.Context, S) error)`
+- All state access is synchronized by the framework
-### **Global State**
+### Callback Injection
-**State**: In NewGraph, pass in the creation method of State through `compose.WithGenLocalState`. This globally scoped state at the request level can be read from and written to during various stages of a single request.
+Components may or may not implement callback aspects. If a component implements `Checker` with `IsCallbacksEnabled()==true`, the framework uses the component’s internal callbacks; otherwise it wraps external callbacks reporting only input/output. Graph always injects callbacks with `RunInfo` for itself. See [Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual).
-Eino recommends using `StatePreHandler` and `StatePostHandler`:
+### Option Distribution
-- StatePreHandler: Before the execution of each node, read from and write to State. Replace the node's input as needed.
-- StatePostHandler: After the execution of each node, read from and write to State. Replace the node's output as needed.
+- Global by default — applies to all nodes, including nested graphs
+- Component‑type options — e.g., `AddChatModelOption` applies to all ChatModel nodes; Lambda with its own option type can be targeted similarly
+- Specific nodes — `DesignateNode(key ...string)`
+- Nested graphs or their nodes — `DesignateNodeWithPath(path ...*NodePath)`
-For streaming scenarios, use the corresponding `StreamStatePreHandler` and `StreamStatePostHandler`.
+See [CallOption Capabilities](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities).
-These state handlers are located outside the nodes and affect the nodes by modifying the Input or Output, thus ensuring the "stateless" property of the nodes.
+### Graph Nesting
-If you need to read from and write to State inside the nodes, Eino provides the `ProcessState[S any](ctx context.Context, handler func(context.Context, S) error) error` function.
+Compiled graphs (`Runnable`) are Lambda‑like; you can wrap them as a Lambda and nest into other graphs, or add subgraphs pre‑compile via `AddGraph`.
-The Eino framework will add locks at all positions where the State is read from or written to.
+- Lambda wrapping adds an extra Lambda level in traces/callbacks
+- Lambda wrapping carries options via Lambda options, not `DesignateNodeWithPath`
+- Lambda wrapping requires pre‑compilation; `AddGraph` compiles the inner graph with the parent
-### **Callback Injection**
+### Internal Mechanics
-Eino's orchestration framework considers that components entering the orchestration might have internally embedded Callback aspects or might not. This information is determined by whether the component implements the `Checker` interface and the return value of the `IsCallbacksEnabled` method in the interface.
+#### Execution Sequence
-- When `IsCallbacksEnabled` returns true, Eino's orchestration framework uses the component's internally implemented Callback aspects.
-- Otherwise, it automatically wraps the component implementation with external Callback aspects, (only) reporting input and output.
+Full streaming execution sequence for an InvokableLambda (string→int) with State handlers, InputKey/OutputKey and external callbacks:
-In either case, RunInfo will be automatically inferred.
+
-Additionally, for the Graph as a whole, Callback aspects will always be injected, with RunInfo being the Graph itself.
+Workflow performs field mapping after `StatePostHandler` and stream copy, and before `StatePreHandler` via merge.
-For a complete explanation of Eino's Callback capabilities, see [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual).
+#### Execution Engines
-### **Option Allocation**
+- `NodeTriggerMode == AnyPredecessor` → pregel engine (directed cyclic graph)
+ - After current nodes run, all successors form a SuperStep and run together
+ - Supports Branch and cycles; may need passthrough nodes to shape SuperSteps
-Eino supports various dimensions of Call Option allocation methods:
+
-- Default global allocation, i.e., allocated to all nodes, including nested internal graphs.
-- An option can be added to a specific component type, in which case it is by default allocated to all nodes of that type, such as AddChatModelOption. Lambdas that have defined unique Option types can also specify the Option for themselves in this way.
-- Any specific nodes can be designated using `DesignateNode(key ...string)`.
-- Any depth of nested graphs, or any specific nodes within them, can be designated using `DesignateNodeWithPath(path ...*NodePath)`.
-
-For a complete explanation of Eino's Call Option capabilities, see [Eino: CallOption capabilities and specification](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities).
-
-### **Graph Nesting**
-
-The outcome of graph orchestration, `Runnable`, is very similar in interface form to Lambda. Therefore, a compiled graph can be simply encapsulated as a Lambda and nested into other graphs as a Lambda node.
-
-Another way is that, before compilation, Graph, Chain, Workflow, etc., can be nested directly into other graphs using AddGraph. The differences between the two methods are:
-
-- The Lambda method adds an additional Lambda node in the trace. From the perspective of other Callback handlers, there will also be an additional layer.
-- The Lambda method requires using the Lambda's Option to inherit CallOption and cannot use DesignateNodeWithPath.
-- The Lambda method requires the internal graph to be precompiled. Directly using AddGraph allows the internal graph to be compiled together with the parent graph.
-
-## **Internal Mechanism**
-
-### **Execution Sequence**
-
-Taking an InvokableLambda (input as string, output as int) with StatePreHandler, StatePostHandler, InputKey, OutputKey added, and no Callback aspect implemented internally as an example, the complete flow execution sequence in the diagram is as follows:
-
-
-
-In the workflow scenario, field mapping occurs in two places:
-
-- After the node execution's StatePostHandler and the "stream replication" step, each downstream-required field will be separately extracted.
-- After the "merge" step before node execution, and before StatePreHandler, the extracted upstream field values will be converted to the current node's input.
-
-### **Runtime Engine**
-
-When `NodeTriggerMode == AnyPredecessor`, the graph executes using the pregel engine, corresponding to a directed graph with cycles. Characteristics include:
-
-- One or more currently executing nodes' all subsequent nodes collectively execute as a SuperStep. At this time, these new nodes become the "current" nodes.
-- Supports Branch, supports loops in the graph, but it may require manually adding passthrough nodes to ensure SuperStep nodes meet expectations, as illustrated below:
-
-
-
-In the above image, Node 4 and Node 5 are executed together as per rules, which likely does not meet expectations. It needs to be changed to:
+Refactor with passthrough to meet expectations:
-When `NodeTriggerMode == AllPredecessor`, the graph executes using the dag engine, corresponding to a directed acyclic graph. Characteristics include:
-
-- Each node has a specific predecessor node, and this node is only executable once all predecessor nodes are complete.
-- An eager mode can be selected, where there is no SuperStep concept. Each node, upon completion, immediately checks which subsequent nodes can be run and executes them at the earliest time.
-- Does not support cycles in the graph, as it breaks the "each node has a specific predecessor node" assumption.
-- Support Branch. At runtime, mark the unselected nodes of Branch as skipped, which does not affect the semantics of AllPredecessor.
-- No need for manual SuperStep alignment.
-
-> 💡
-> When NodeTriggerMode is set to AllPredecessor, the node will execute after all predecessors are ready, but it will not execute immediately. Instead, it still follows the SuperStep - running new runnable nodes after a batch of nodes have completed execution.
->
-> If compose.WithEagerExecution() is passed in Compile, the ready nodes will run immediately.
+- `NodeTriggerMode == AllPredecessor` → DAG engine (directed acyclic graph)
+ - Each node runs only after all predecessors complete
+ - Cycles not supported; Branch is supported (unselected branch nodes are marked skipped at runtime)
+ - SuperStep semantics apply; use `compose.WithEagerExecution()` to run ready nodes immediately. In v0.4.0+, AllPredecessor defaults to eager execution.
-In summary, the pregel mode is flexible and powerful but comes with additional mental overhead, while the dag mode is clear and simple but limited in application scenarios. In the Eino framework, Chain uses the pregel mode, Workflow uses the dag mode, and Graph supports both; users can choose between pregel and dag.
+Summary: pregel is flexible but cognitively heavy; DAG is clear but constrained. In Eino, Chain uses pregel, Workflow uses DAG, Graph supports both selectable by users.
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
index b20f5ab4882..be7d6a00160 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
@@ -1,51 +1,49 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Eino Points of Streaming Orchestration
-weight: 3
+title: Eino Streaming Essentials
+weight: 4
---
> 💡
-> It is recommended to read: [Eino: Overview](/docs/eino/overview) [Eino: The design concept of orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
+> Recommended reading first: [Eino: Overview](/docs/eino/overview) and [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles)
-## **Overview of Streaming Orchestration**
+## Streaming in Orchestration: Overview
-
+
-When orchestrating a streaming Graph, several key elements need to be considered:
+Key factors when orchestrating streaming graphs:
-- Which types of Lambda operators are included in the components/Lambda: Choose from Invoke, Stream, Collect, Transform
-- In orchestration topology, whether the input and output of upstream and downstream nodes are both streams or both non-streams.
-- If the stream types of upstream and downstream nodes do not match, the operations of Streaming and Concat are needed
- - Streaming: Converts T into a Stream[T] with a single Chunk
- - Concat: Merges Stream[T] into a complete T. Each "frame" of Stream[T] is a part of the complete T.
+- Which Lambda operators does the component/Lambda offer: choose among Invoke, Stream, Collect, Transform.
+- In the topology, do upstream outputs and downstream inputs match (both streaming or both non-streaming)?
+- If they don’t match, use streaming helpers:
+ - Streaming: wrap `T` into a single-chunk `Stream[T]`.
+ - Concat: merge `Stream[T]` into a complete `T`. Each frame in `Stream[T]` is a piece of the final `T`.
-## **Implications of Eino's Streaming Programming**
+## Semantics of Streaming in Eino
-- Some components naturally support frame-by-frame output, where each output is a part of a complete output parameter, i.e., "streaming" output. After the streaming output is complete, the downstream will need to concatenate (concat) these "frames" into a complete output parameter. A typical example is ChatModel.
-- Some components naturally support frame-by-frame input, allowing meaningful business processing to start even with incomplete input parameters, or to complete the business processing cycle. For example, in the react agent used to determine whether to call a tool or end the operation, it can make decisions from the first frame of the LLM's streaming output by checking if the message contains a tool call.
-- Therefore, a component can have "non-streaming" input and "streaming" input from the input perspective, and "non-streaming" output and "streaming" output from the output perspective.
-- Combining these, there are four possible streaming programming paradigms.
+- Some components naturally emit frames — partial outputs of the final result — i.e., streaming output. Downstream must concat frames into a complete output. A typical example is an LLM.
+- Some components naturally accept frames and can begin meaningful processing before receiving the full input. For example, in a ReAct agent, a branch may decide to call a tool or end execution by inspecting the first frame of the ChatModel’s output.
+- Thus, each component may accept non-streaming or streaming input, and produce non-streaming or streaming output.
+- Combined, there are four streaming paradigms:
-| Function Name | Pattern Description | Interactive Mode Name | Lambda Construction Method | Description |
-| Invoke | Non-streaming input, non-streaming output | Ping-Pong Mode | compose.InvokableLambda() | |
-| Stream | Non-streaming input, streaming output | Server-Streaming Mode | compose.StreamableLambda() | |
-| Collect | Streaming input, non-streaming output | Client-Streaming | compose.CollectableLambda() | |
-| Transform | Streaming input, streaming output | Bidirectional-Streaming | compose.TransformableLambda() | |
+| Function | Pattern | Interaction | Lambda Constructor | Notes |
+| Invoke | Non-streaming in, non-streaming out | Ping-Pong | compose.InvokableLambda() | |
+| Stream | Non-streaming in, streaming out | Server-Streaming | compose.StreamableLambda() | |
+| Collect | Streaming in, non-streaming out | Client-Streaming | compose.CollectableLambda() | |
+| Transform | Streaming in, streaming out | Bidirectional-Streaming | compose.TransformableLambda() | |
-## **Streamlining from the Perspective of Individual Components**
+## Streaming at the Single-Component Level
-Eino is a "component first" framework, where components can be used independently. When defining component interfaces, should we consider streaming programming? The simple answer is no. The complex answer is "it depends on the real business scenario."
+Eino is a “component-first” framework; components can be used independently. When defining component interfaces, streaming is guided by real business semantics.
-### **Business Paradigm of the Component Itself**
+### Business Semantics of Components
-A typical component, such as Chat Model, Retriever, etc., defines interfaces based on the actual business semantics. If it supports a certain streaming paradigm in practice, that paradigm is implemented. If a certain streaming paradigm has no real business scenario, then it does not need to be implemented. For example:
-
-- Chat Model, in addition to the non-streaming paradigm like Invoke, naturally supports the streaming paradigm like Stream. Therefore, within the Chat Model interface, both Generate and Stream interfaces are implemented. However, Collect and Transform do not correspond to a real business scenario, so the corresponding interfaces are not implemented:
+- ChatModel: besides `Invoke` (non-streaming), it naturally supports `Stream` (streaming). It therefore implements `Generate` and `Stream`, but not `Collect` or `Transform`:
```go
type ChatModel interface {
@@ -56,7 +54,7 @@ type ChatModel interface {
}
```
-- Retriever, in addition to the non-streaming paradigm like Invoke, does not have business scenarios for the other three streaming paradigms, hence only the Retrieve interface is implemented:
+- Retriever: only `Invoke` has real use; the other paradigms don’t fit typical scenarios, so it exposes just `Retrieve`:
```go
type Retriever interface {
@@ -64,12 +62,12 @@ type Retriever interface {
}
```
-### **Specific Paradigms Supported by Components**
+### Which Paradigms Components Implement
-| Component Name | Whether Invoke is Implemented | Whether Stream is Implemented | Whether Collect is Implemented | Whether Transform is Implemented |
-| Chat model | yes | yes | no | no |
-| Chat template | yes | no | no | no |
+| Component | Invoke | Stream | Collect | Transform |
+| ChatModel | yes | yes | no | no |
+| ChatTemplate | yes | no | no | no |
| Retriever | yes | no | no | no |
| Indexer | yes | no | no | no |
| Embedder | yes | no | no | no |
@@ -78,36 +76,36 @@ type Retriever interface {
| Tool | yes | yes | no | no |
-In the official Eino components, except for the Chat Model and Tool that additionally support the stream, all other components only support Invoke. For specific component introductions, refer to: [Eino: Components](/docs/eino/core_modules/components)
+Official Eino components: only `ChatModel` and `Tool` also support `Stream`; all others support `Invoke` only. See: [Eino: Components](/docs/eino/core_modules/components)
-The streaming paradigms Collect and Transform are currently only used in orchestration scenarios.
+`Collect` and `Transform` are generally useful only within orchestration.
-## **Streamlined Perspective from Multiple Component Orchestration**
+## Streaming Across Multiple Components (Orchestration)
-### **Stream Paradigm of Components in Orchestration**
+### Component Paradigms in Orchestration
-A component, when used alone, has a defined stream paradigm for its input and output, which cannot exceed the boundaries of the component’s defined interface.
+Standalone, a component’s input/output are fixed by its interface. For example:
-- For example, a Chat Model can only have non-streaming []Message as input, and the output can be either non-streaming Message or streaming StreamReader[Message], because the Chat Model only implements the Invoke and Stream paradigms.
+- ChatModel inputs non-streaming `[]Message` and outputs either non-streaming `Message` or streaming `StreamReader[Message]`.
-However, once a component is used in an "orchestration" scenario with multiple components, its input and output become less fixed and rely on the "upstream output" and "downstream input" within the orchestration context. Take the typical orchestration diagram of the React Agent as an example:
+In orchestration, inputs/outputs depend on upstream/downstream. Consider a typical ReAct agent:
-In the diagram above, if the Tool is a StreamableTool, i.e., the output is StreamReader[Message], then Tool -> ChatModel could have a streaming output. However, the Chat Model does not have a business scenario for receiving streaming input nor a corresponding interface. In this case, the Eino framework will automatically help the ChatModel gain the capability to receive streaming input:
+If the Tool is `StreamableTool` (output is `StreamReader[Message]`), then Tool → ChatModel may be streaming. However, ChatModel does not accept streaming input. Eino automatically bridges this by concatenating streams into non-streaming input:
-
+
-The Concat message stream above is a capability automatically provided by the Eino framework. Even if it's not a message and is arbitrary T, as long as certain conditions are met, the Eino framework will automatically convert StreamReader[T] to T. These conditions are: **In orchestration, when the upstream output of a component is StreamReader[T], but the component only provides T as the input business interface, the framework will automatically concatenate StreamReader[T] into T before inputting it to this component.**
+Eino’s automatic `StreamReader[T] → T` conversion applies whenever a component expects `T` but upstream produces `StreamReader[T]`. You may need to provide a custom concat function for `T`.
> 💡
-> The process of concatenating StreamReader[T] into T by the framework may require the user to provide a Concat function. Refer to the chapter on "**Fan-In and Merging**" in [Eino: The design concept of orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles).
+> The `StreamReader[T] → T` conversion may require a user-provided concat function. See [Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) under “merge frames”.
-On the other hand, consider an opposite example. Again, let's look at a more complete orchestration diagram of the React Agent:
+Conversely, consider another ReAct diagram:
-In the above diagram, the branch receives the message output from the chat model and decides whether to end the agent's current run and output the message, or to call a Tool and send the result back to the Chat Model for further processing based on whether the message contains a tool call. Since this Branch can complete the logic determination with the first frame of the message stream, we define this Branch with the Collect interface, which means it has streaming input and non-streaming output:
+Here, `branch` reads the ChatModel’s output and decides whether to end or call a tool. Since `branch` can decide from the first frame, define it with `Collect` (streaming in, non-streaming out):
```go
compose.NewStreamGraphBranch(func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (endNode string, err error) {
@@ -122,40 +120,35 @@ compose.NewStreamGraphBranch(func(ctx context.Context, sr *schema.StreamReader[*
}
return nodeKeyTools, nil
-}
+})
```
-ReactAgent has two interfaces, Generate and Stream, which implement the Invoke and Stream programming paradigms respectively. When a ReactAgent is called in the Stream way, the output of the Chat Model is StreamReader[Message], thus the input to the Branch is StreamReader[Message], which matches the Branch condition's function signature and can run without any conversion.
+If the agent is invoked via `Stream`, ChatModel outputs `StreamReader[Message]`, matching the branch’s input.
-However, when a ReactAgent is called in the Generate way, the output of the Chat Model is Message, so the Branch's input will also be Message, which does not match the Branch Condition's StreamReader[Message] function signature. At this point, the Eino framework will automatically box the Message into a StreamReader[Message] and pass it to the Branch, where this StreamReader will contain only one frame.
+If the agent is invoked via `Generate`, ChatModel outputs `Message`. Eino automatically wraps `Message` into a single-frame `StreamReader[Message]` (pseudo-stream) to match the branch.
-> 💡
-> This kind of stream with only one frame is commonly known as a "pseudo-stream" because it does not bring the actual benefit of streaming, which is "low first packet latency." It merely packages the message to satisfy the requirements of the stream input-output interface signature.
+Summary: **When upstream outputs `T` but downstream expects `StreamReader[T]`, Eino wraps `T` into a single-frame `StreamReader[T]`.**
-In summary: ** In arrangement, when a component's upstream output is T, but the component only provides StreamReader[T] as the input business interface, the framework will automatically box T into a single-frame StreamReader[T] and then input it to this component.**
+### Streaming Paradigms of Orchestration Aids
-### **Stream Paradigm for Orchestrating Auxiliary Elements**
-
-As mentioned above, the Branch is not a standalone component, but rather an "orchestrating auxiliary element" that only makes sense in orchestrating scenarios. There are similar "components" that are meaningful only in orchestration scenarios, as detailed in the table below:
+`Branch` is an orchestration-only aid, not a standalone component. Others include:
-| Component | Usage Scenario | Whether Invoke is Implemented | Whether Stream is Implemented | Whether Collect is Implemented | Whether Transform is Implemented |
-| Branch | Dynamically select one from a group of downstream Nodes based on upstream output - Invoke is implemented when the complete input parameters can be judged only after receiving them - Collect is implemented when the judgment can be made after receiving some frames - Only one of the two can be implemented | yes | no | yes | no |
-| StatePreHandler | In the Graph, modify the State or/and Input before entering the Node. Stream is supported. | yes | no | no | yes |
-| StatePostHandler | In the Graph, modify the State or/and Output after the Node is completed. Stream is supported | yes | no | no | yes |
-| Passthrough | In parallel situations, in order to flatten the number of nodes in each parallel branch, Passthrough nodes can be added to the branch with fewer nodes. The input and output of the Passthrough node are the same, following the output of the upstream node or the input of the downstream node (expected to be the same). | yes | no | no | yes |
-| Lambda | Encapsulate business logic not defined in official components. Which paradigm the business logic belongs to, choose the corresponding streaming paradigm to implement. | yes | yes | yes | yes |
+| Element | Use Case | Invoke | Stream | Collect | Transform |
+| Branch | Select a downstream node dynamically based on upstream outputIf decision requires full input → InvokeIf decision can be made from early frames → CollectImplement exactly one | yes | no | yes | no |
+| StatePreHandler | Modify State/Input before entering a node. Streaming-friendly. | yes | no | no | yes |
+| StatePostHandler | Modify State/Output after a node. Streaming-friendly. | yes | no | no | yes |
+| Passthrough | Balance node counts across parallel branches by inserting passthroughs. Input equals output. | yes | no | no | yes |
+| Lambda | Encapsulate custom business logic; implement the paradigm that matches your logic. | yes | yes | yes | yes |
-Additionally, there is another "component" that only makes sense in orchestrating scenarios, which is to treat the orchestration product as a whole, such as the orchestrated Chain and Graph. These overall orchestration products can be called as "components" independently, or they can be added to higher-level orchestration products as nodes.
-
-## **Overall Orchestration Flow**
+Orchestration artifacts (compiled Chain/Graph) can be treated as components themselves — used standalone or as nodes within higher-level orchestration.
-### **"Business" Paradigm of Orchestrated Products**
+## Streaming at Orchestration Level (Whole Graph)
-Since the overall orchestrated product can be considered a "component," we can pose the question from a component perspective: Does the orchestrated product "component" have an interface paradigm that fits "business scenarios" like components such as Chat Model? The answer is both "yes" and "no."
+### “Business” Paradigms of Orchestration Artifacts
-- "No": Overall, the orchestrated products such as Graphs and Chains themselves do not have business attributes; they only serve abstract orchestration and, therefore, do not have an interface paradigm that fits business scenarios. Furthermore, orchestration needs to support various paradigms of business scenarios. Hence, the Runnable[I, O] interface representing orchestrated products in Eino does not make choices nor can it; it provides methods for all streaming paradigms:
+As a component, a compiled artifact has no business semantics — it serves orchestration. It must support all paradigms:
```go
type Runnable[I, O any] interface {
@@ -166,57 +159,57 @@ type Runnable[I, O any] interface {
}
```
-- "Yes": Specifically, a particular Graph or Chain definitely carries specific business logic and thus has a streaming paradigm suitable for that particular business scenario. For instance, a Graph similar to React Agent matches business scenarios such as Invoke and Stream, so the logical way to call this Graph is through Invoke and Stream. Although the Runnable[I, O] interface for orchestrated products includes methods like Collect and Transform, they are generally not needed in normal business scenarios.
+For a specific compiled graph, the correct paradigm depends on its business scenario. A ReAct-like graph typically fits `Invoke` and `Stream`.
-### **Paradigm of Orchestrating Internal Components at Runtime**
+### Runtime Paradigms of Components Inside a Compiled Graph
-From another perspective, since the entire orchestration product can be considered a "component," each "component" must have its own internal implementation. For example, the internal implementation logic of ChatModel might involve transforming the input []Message into respective model's API requests, then calling the model's API, and finally transforming the response into output Message. By analogy, what is the internal implementation of the "component" Graph? It is the data flowing within the Graph's components according to the user-specified flow direction and streaming paradigm. Here, "flow direction" is out of the current discussion scope, and the runtime streaming paradigm of each component within the Graph is determined by the overall triggering method of the Graph, specifically:
+Viewed as a “component”, a compiled Graph’s internal implementation is data flowing among its nodes according to your specified flow direction and streaming paradigms. Flow direction is out of scope here; runtime paradigms are determined by how the Graph is triggered.
-If the user calls the Graph via **Invoke**, then all components within the Graph are called using the Invoke paradigm. If a component does not implement the Invoke paradigm, the Eino framework automatically wraps the component's implemented streaming paradigm into the Invoke calling paradigm, with the following priority:
+When you call a compiled graph via **Invoke**, all internal components run in the `Invoke` paradigm. If a component does not implement `Invoke`, Eino wraps it using the first available option:
-- If the component implements Stream, then wrap the Invoke with Stream, i.e., automatically concat the output stream.
+- If `Stream` exists, wrap `Stream` as `Invoke` by concatenating the output stream.
-
+
-- Otherwise, if the component implements Collect, then wrap the Invoke with Collect, i.e., transform non-streamed input to single-frame stream.
+- Else if `Collect` exists, wrap `Collect` as `Invoke` by boxing non-streaming input into a single-frame stream.
-
+
-- If neither is implemented, then Transform must be implemented, wrapping the Invoke with Transform, i.e., transforming input to single-frame stream and concatenating output.
+- Else use `Transform`, wrapping it as `Invoke` by boxing input into a single-frame stream and concatenating the output stream.
-
+
-If the user calls the Graph via **Stream/Collect/Transform**, then all components within the Graph are called using the Transform paradigm. If a component does not implement the Transform paradigm, the Eino framework automatically wraps the component's implemented streaming paradigm into the Transform calling paradigm, with the following priority:
+When you call via **Stream / Collect / Transform**, all internal components run in the `Transform` paradigm. If a component does not implement `Transform`, Eino wraps using the first available option:
-- If the component implements Stream, then wrap the Transform with Stream, i.e., automatically concat the input stream.
+- If `Stream` exists, wrap `Stream` as `Transform` by concatenating the input stream.
-
+
-- Otherwise, if the component implements Collect, then wrap the Transform with Collect, i.e., transform non-streamed output to single-frame stream.
+- Else if `Collect` exists, wrap `Collect` as `Transform` by boxing non-streaming output into a single-frame stream.
-
+
-- If neither is implemented, then Invoke must be implemented, wrapping the Transform with Invoke, i.e., concatenating input stream and transforming output to single-frame stream
+- Else wrap `Invoke` as `Transform` by concatenating input streams and boxing outputs into single-frame streams.
-
+
-Combining the various cases enumerated above, Eino framework's automatic conversion between T and Stream[T] can be summarized as:
+In summary, Eino’s automatic conversions between `T` and `Stream[T]` are:
-- **T -> Stream[T]: Encapsulate the complete T into a single-frame Stream[T]. Non-streaming becomes pseudo-streaming.**
-- **Stream[T] -> T: Concat Stream[T] into a complete T. When Stream[T] is not a single-frame stream, a Concat method for T might need to be provided.**
+- **T → Stream[T]**: box `T` into a single-frame stream (pseudo-stream).
+- **Stream[T] → T**: concat the stream into a complete `T`. For non-single-frame streams, you may need to provide a concat function.
-After reading the above implementation principles, you may have questions: Why is it required that all internal components be invoked using Invoke for a graph's Invoke? And why is it required that all internal components be invoked using Transform for a graph's Stream/Collect/Transform? After all, counterexamples can be given:
+You might wonder why a graph-level `Invoke` enforces `Invoke` internally, and `Stream/Collect/Transform` enforces `Transform` internally. Consider these counterexamples:
-- Components A and B are orchestrated as a Chain and called using Invoke. A's business interface implements Stream, and B's business interface implements Collect. At this point, there are two choices for the invocation paradigm of the internal components of the graph:
- - A is called using stream, and B is called using collect. The overall Chain still has Invoke semantics while retaining true stream semantics internally. That is, the output stream of A does not need to be concatenated and can be fed into B in real time.
- - In Eino's current implementation, both A and B are called using Invoke, requiring the output stream of A to be concatenated and making B's input pseudo-streaming, losing the true streaming semantics internally.
-- Components A and B are orchestrated as a Chain and called using Collect. A implements both Transform and Collect, and B implements Invoke. Two choices:
- - A is called using Collect, and B using Invoke: The overall semantics remain Collect without requiring any automatic conversion and boxing operations by the framework.
- - In Eino's current implementation, both A and B are called using Transform. Since A's business interface implements Transform, A's output and B's input may be truly streaming. However, B's business interface only implements Invoke. Based on the above analysis, B's input parameters need to be concatenated from true streaming to non-streaming. At this point, users need to provide an additional concatenation function for B's input parameters, which could have been avoided.
+- Two components A and B composed into a Chain, called via `Invoke`. Suppose A implements `Stream`, B implements `Collect`.
+ - Choice 1: A runs as `Stream`, B runs as `Collect`. The overall Chain remains `Invoke` to the caller while preserving true streaming semantics inside (no concat; A’s output stream feeds B in real time).
+ - Current Eino behavior: both A and B run as `Invoke`. A’s output stream is concatenated to a full value; B’s input is boxed into a pseudo-stream. True streaming semantics inside are lost.
+- Two components A and B composed into a Chain, called via `Collect`. Suppose A implements `Transform` and `Collect`, B implements `Invoke`.
+ - Choice 1: A runs as `Collect`, B runs as `Invoke`. The overall remains `Collect` with no automatic conversions or boxing.
+ - Current Eino behavior: both A and B run as `Transform`. Since B only implements `Invoke`, its input may require concat from a real stream, potentially needing a user-provided concat function — which could have been avoided.
-In the two examples above, it is possible to find a clear, different approach from Eino's conventions but with a more optimal streaming invocation path. However, when generalized to any orchestration scenario, it is difficult to find a clearly defined, different rule from Eino's conventions that is universally better. For example, for A->B->C with Collect semantics, should Collect be used between A->B or B->C? Potential factors include the specific implementation of A, B, and C's business interfaces, possible considerations like "maximizing the use of true streaming," and whether specific parameters implement concatenation. If it is a more complex graph, the factors to consider will quickly increase. In such a case, even if the framework can define a clear, better, universally applicable rule, it can be challenging to explain, and the cost of understanding and usage will be high, likely exceeding the actual benefits of this new rule.
+Generalizing across arbitrary graphs, it’s difficult to define a universal rule that is always better and remains clear. Influencing factors include which paradigms components implement, aiming to maximize true streaming, and whether concat functions exist. For complex graphs, the number of factors grows quickly. Even if a more optimal universal rule exists, it would be hard to explain and use without exceeding the benefit it provides. Eino’s design therefore favors clarity and predictability.
-To conclude, we can say that the runtime invocation paradigms of the internal components in Eino's orchestration products are **By Design**, clearly defined as follows:
+By design:
-- **When the overall invocation is Invoke, all internal components are invoked using Invoke, with no internal streaming process.**
-- **When the overall invocation is Stream/Collect/Transform, all internal components are invoked using Transform. When a Stream[T] -> T concatenation process occurs, an additional concatenation function for T may be needed.**
+- **Invoke outside → Invoke inside**: no streaming inside.
+- **Stream/Collect/Transform outside → Transform inside**: streaming inside; `Stream[T] → T` may require a concat function.
diff --git a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
index a29ab68279f..7565c2a9655 100644
--- a/content/en/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
+++ b/content/en/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
@@ -1,78 +1,67 @@
---
Description: ""
-date: "2025-08-07"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: Workflow Orchestration Framework'
-weight: 7
+weight: 3
---
-## What is Eino Workflow?
+## What Is Eino Workflow?
-is a set of orchestrated APIs that are at the same architectural level as the Graph API:
+An orchestration API at the same architectural level as Graph:
-
-
-The essential characteristics are:
-
-- has the same level of capabilities as Graph API, and both are suitable framework tools for orchestrating the "information flow around large models".
- - Remains consistent in aspects such as node type, stream processing, callback, option, state, interrupt & checkpoint.
- - Implementing the AnyGraph interface allows it to be added as a subgraph to the parent Graph/Chain/Workflow when AddGraphNode is called.
- - can also add other Graph/Chain/Workflow as its own subgraph.
-- Field-level mapping capability: The input of a node can be composed of any combination of output fields from any predecessor nodes.
- - Natively supports struct, map, and mutual mapping between structs and maps at any nested level.
-- Separation of control flow and data flow: The Edge in a Graph determines both the execution order and data transfer. In a Workflow, they can be transferred together or separately.
-- Loops (i.e., loops similar to react agent's chatmodel->toolsNode->chatmodel) are not supported. NodeTriggerMode is fixed to AllPredecessor.
-
-## Why use Workflow
-
-### Flexible input and output types
-
-For example, if you need to arrange two lambda nodes, each containing two "existing business functions f1, f2", the input and output types are specific Structs that conform to the business scenario and are different from each other:
-
-
-
-When orchestrating the workflow, directly map the output field F1 of f1 to the input field F3 of f2, while retaining the original function signatures of f1 and f2. The achieved effect is:**Each node is "determined by business scenarios for input and output", without the need to consider "who provides my input and who uses my output"**.
-
-When graph is being orchestrated, due to the requirement of "type alignment", if f1 -> f2, then the output type of f1 and the input type of f2 need to be aligned, and one of the following two options must be chosen:
-
-- Define a new common struct, and change both the output type of f1 and the input type of f2 to this common struct. This has costs and may intrude into business logic.
-- Both the output type of f1 and the input type of f2 have been changed to map. The feature of strong type alignment has been lost.
-
-### Separation of control flow and data flow
+```mermaid
+flowchart LR
+ E[Eino compose engine]
+ G[Graph API]
+ W[Workflow API]
+ C[Chain API]
+ E --> G
+ E --> W
+ G --> C
+```
-Look at the following scenario:
+Key traits:
-
+- Same capability level as Graph; both orchestrate “LLM-centric information flow”.
+ - Node types, streaming, callbacks, options, state, interrupt & checkpoint are consistent.
+ - Implements `AnyGraph`; can be added as a child to Graph/Chain/Workflow via `AddGraphNode`.
+ - Can host other Graph/Chain/Workflow as children.
+- Field-level mapping: node input can be a composition of arbitrary fields from arbitrary predecessors.
+ - Natively supports `struct`, `map`, and arbitrarily nested combinations.
+- Control flow and data flow can be separated. In Graph, edges carry both; in Workflow, you may split them.
+- No cycles (e.g., ChatModel→ToolsNode→ChatModel loops). `NodeTriggerMode` is fixed to `AllPredecessor`.
-Node D simultaneously references certain output fields of A, B, and C. Among them, the dotted line from A to D is simply a "data flow" that does not transmit "control" information, i.e., whether A has completed execution or not does not determine whether D starts execution.
+## Why Workflow?
-The thick arrow between nodes D and E represents that node E does not reference any output from node D and is simply a "control flow" that does not transfer "data". That is, whether D has completed execution determines whether E starts execution, but the output of D does not affect the input of E.
+### Flexible Input/Output Types
-The other lines in the figure represent the combination of control flow and data flow.
+Easily orchestrate existing business functions with unique `struct` inputs/outputs and map fields directly between them, preserving original signatures without forcing common types or `map[string]any` everywhere.
-It should be noted that the prerequisite for data flow to be transferred is that there must be a control flow. For example, the data flow from A to D depends on the existence of the control flow A->branch->B->D or A->branch->C->D. That is, data flow can only reference the output of predecessor nodes.
+
-For example, this scenario of "cross-node" passing specific data:
+### Separate Control and Data Flow
-
+
-In the above figure, the input of the chat template node can be very clear:
+- Dashed lines denote data-only edges; no execution dependency (A’s completion doesn’t gate D’s start).
+- Bold arrows denote control-only edges; no data transfer (D’s completion gates E’s start but E doesn’t read D’s outputs).
+- Other edges combine control and data.
-`map[string]any{"prompt": "prompt from START", "context": "retrieved context"}`
+Data transfers require the existence of a control path; a node can only read from predecessors.
-Conversely, if you use the Graph or Chain API, you need to choose one of the two:
+Example: cross-node data passing in Workflow vs Graph/Chain. In Workflow, a ChatTemplate can take exactly `{"prompt": ..., "context": ...}` from START and a Retriever; in Graph/Chain this either requires heavy map wrapping or state usage.
-- Use OutputKey to convert the output type of the node (the START node cannot be added, so an additional passthrough node must be added), and the input of the ChatTemplate node will include the full output of START and retriever (instead of just the few fields that are actually needed).
-- The prompt of the START node is placed inside the state, and ChatTemplate reads from the state. An additional state has been introduced.
+
-## How to Use Workflow
+## Using Workflow
-### The simplest workflow
+### Simplest Workflow
-START -> node -> END
+START → node → END
-
+
```go
// creates and invokes a simple workflow with only a Lambda node.
@@ -80,72 +69,33 @@ START -> node -> END
// (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
+ wf.AddLambdaNode("lambda", compose.InvokableLambda(func(ctx context.Context, in int) (string, error) {
+ return strconv.Itoa(in), nil
+ })).AddInput(compose.START)
+ wf.End().AddInput("lambda")
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
+ if err != nil { logs.Errorf("workflow compile error: %v", err); return }
result, err := run.Invoke(context.Background(), 1)
- if err != nil {
- logs.Errorf("workflow run err: %v", err)
- return
- }
-
+ if err != nil { logs.Errorf("workflow run err: %v", err); return }
logs.Infof("%v", result)
}
```
-[Eino example link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/1_simple/main.go)
-
-Several core APIs:
-
-- `func NewWorkflow[I, O any](opts ...NewGraphOption) *Workflow[I, O]`
- - Build a new Workflow.
- - is exactly the same as `NewGraph` signature.
-- `func (wf *Workflow[I, O]) AddChatModelNode(key string, chatModel model.BaseChatModel, opts ...GraphAddNodeOpt) *WorkflowNode `
- - Add a new node to the Workflow.
- - The types of nodes that can be added are exactly the same as those in Graph.
- - The difference from Graph's AddXXXNode is that Workflow does not immediately return an error, but instead processes and returns errors uniformly during the final Compile.
- - AddXXXNode retrieves a WorkflowNode, and subsequent operations such as adding data field mapping to the Node are directly performed using Method Chaining
-- `func (n *WorkflowNode) AddInput(fromNodeKey string, inputs ...*FieldMapping) *WorkflowNode`
- - Add input data field mapping to a WorkflowNode
- - Returns WorkflowNode, allowing for continued method chaining.
-- `(wf *Workflow[I, O]) Compile(ctx context.Context, opts ...GraphCompileOption) (Runnable[I, O], error)`
- - Compile a Workflow.
- - is completely consistent with the signature of Compile Graph.
+Core APIs:
-### Data Field Mapping
+- `NewWorkflow[I, O](...)` — same as `NewGraph`
+- `AddXXXNode(key, comp, ...) *WorkflowNode` — same node types as Graph; returns `WorkflowNode` for chaining
+- `(*WorkflowNode).AddInput(fromKey string, inputs ...*FieldMapping) *WorkflowNode` — add field mappings
+- `Compile(ctx, ...) (Runnable[I, O], error)` — same signature as Graph
-START (input struct) -> [parallel lambda1, lambda2] -> END (output map)
+### Field Mapping
-Let's take an example of "counting the occurrences of characters in a string". The overall workflow takes an eino Message and a sub string as input, assigns Message.Content to a counter c1, assigns Message.ReasoningContent to another counter c2, calculates the occurrences of the sub string in parallel respectively, and then maps them to END:
+START (struct input) → [parallel lambda c1, c2] → END (map output).
-
+We demonstrate counting occurrences of a substring in two different fields. The workflow input is an Eino `Message` plus a `SubStr`; `c1` counts occurrences in `Content`, `c2` counts occurrences in `ReasoningContent`. The two lambdas run in parallel and map their results to END:
-In the above figure, the overall input of the workflow is the message Struct, the inputs of both c1 and c2 lambdas are the counter Struct, the outputs are both int, and the overall output of the workflow is map[string]any. The code is as follows:
+
```go
// demonstrates the field mapping ability of eino workflow.
@@ -155,7 +105,7 @@ func main() {
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 is a lambda function that counts occurrences of SubStr within FullStr
wordCounter := func(ctx context.Context, c counter) (int, error) {
return strings.Count(c.FullStr, c.SubStr), nil
}
@@ -179,17 +129,15 @@ func main() {
// 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
+ // map START's SubStr field to lambda c2's SubStr field
compose.MapFields("SubStr", "SubStr"),
- // map START's Message's ReasoningContent field to lambda c1's FullStr field
+ // map START's Message's ReasoningContent field to lambda c2'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'
+ // 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'
+ // 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
@@ -217,31 +165,31 @@ func main() {
}
```
-[Eino example code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/2_field_mapping/main.go)
+Eino example link: https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/2_field_mapping/main.go
-The main message of this example is that `the AddInput` method can pass 0-n data field mapping rules, and can be called multiple times `AddInput`. This means:
+The `AddInput` method accepts 0–n field mappings and can be called multiple times. This means:
-- A node can reference any number of fields from the output of a predecessor node.
-- A node can reference fields from any number of predecessor nodes.
-- A mapping can be "whole to field", "field to whole", "whole to whole", or a mapping between nested fields.
-- Different types above have different APIs to express this mapping:
- - Top-level field to top-level field: `MapFields(string, string)`
- - All outputs to top-level field: `ToField(string)`
- - Top-level field to all inputs: `FromField(string)`
- - Nested field to nested field: `MapFieldPaths(FieldPath, FieldPath)`, which is required when either the upstream or downstream is nested.
- - All outputs to nested field: `ToFieldPath(FieldPath)`
- - Nested field to all inputs: `FromFieldPath(FieldPath)`
- - All outputs to all inputs: Directly use `AddInput` without passing `FieldMapping`.
+- A node can reference any number of fields from a single predecessor’s output.
+- A node can reference fields from any number of predecessors.
+- A mapping can be whole→field, field→whole, whole→whole, or nested field↔nested field.
+- Different mapping types have different APIs:
+ - Top-level field → top-level field: `MapFields(string, string)`
+ - Whole output → top-level field: `ToField(string)`
+ - Top-level field → whole input: `FromField(string)`
+ - Nested field → nested field: `MapFieldPaths(FieldPath, FieldPath)`
+ - Whole output → nested field: `ToFieldPath(FieldPath)`
+ - Nested field → whole input: `FromFieldPath(FieldPath)`
+ - Whole output → whole input: `AddInput(fromKey)` with no `FieldMapping`
## Advanced Features
-### Only data flow, no control flow
+### Data-only Edges
-Imagine a simple scenario: START -> Addition Node -> Multiplication Node -> END. Here, the "Multiplication Node" multiplies a field from START by the result of the Addition Node:
+Imagine: START → adder → multiplier → END. The multiplier consumes one field from START and the adder’s result:
-
+
-In the above figure, the multiplication node is executed after the addition node, i.e., the "multiplication node" is controlled by the "addition node". However, the START node does not directly control the "multiplication node", but merely passes the data. In the code, pure data flow is specified through `AddInputWithOptions(fromNode, fieldMappings, WithNoDirectDependency)`:
+Use `AddInputWithOptions(fromNode, fieldMappings, WithNoDirectDependency)` to declare pure data dependencies:
```go
func main() {
@@ -300,9 +248,9 @@ func main() {
}
```
-[Eino examples Code Link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/3_data_only/main.go)
+Eino examples link: https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/3_data_only/main.go
-Newly introduced APIs in this example:
+New API:
```go
func (n *WorkflowNode) AddInputWithOptions(fromNodeKey string, inputs []*FieldMapping, opts ...WorkflowAddInputOpt) *WorkflowNode {
@@ -310,29 +258,13 @@ func (n *WorkflowNode) AddInputWithOptions(fromNodeKey string, inputs []*FieldMa
}
```
-and new Option:
-
-```go
-func WithNoDirectDependency() WorkflowAddInputOpt {
- return func(opt *workflowAddInputOpts) {
- opt.noDirectDependency = true
- }
-}
-```
-
-Combined, it can add pure "data dependencies" to nodes.
-
-### Only control flow, no data flow
+### Control-only Edges
-Imagine a scenario of "sequential bidding with confidential quotes": START -> Bidder 1 -> Qualification Check -> Bidder 2 -> END:
+Consider a “sequential bidding, prices kept confidential” scenario: START → bidder1 → branch → bidder2 → END.
-
+
-In the above figure, the normal connection is "control + data", the dashed line is "data only", and the bold line is "control only". The logic is: input an initial price, bidder 1 gives bid 1, a branch determines whether it is high enough; if it is high enough, it ends directly; otherwise, the initial price is given to bidder 2, who gives bid 2, and finally bids 1 and 2 are aggregated and output.
-
-When Bidder 1 gives an offer, issue an announcement saying "The bidder has completed the auction." Note that the line from bidder1 to announcer is a thick solid line, indicating "control only", because the amount needs to be kept confidential when issuing the announcement!
-
-The two bold lines branching out are both "control only" because neither bidder2 nor END depends on the data provided by the branch. In the code, the pure control flow is specified through `AddDependency(fromNode)`:
+Bold lines are control-only edges. After bidder1 bids, we announce completion without passing the bid amount. Use `AddDependency(fromNode)` to declare control without data:
```go
func main() {
@@ -343,7 +275,7 @@ func main() {
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
@@ -376,25 +308,6 @@ func main() {
// but no data passing between them.
AddInputWithOptions(compose.START, nil, compose.WithNoDirectDependency())
- wf := compose.NewWorkflow[float64, map[string]float64]()
-
- wf.AddLambdaNode("b1", compose.InvokableLambda(bidder1)).
- AddInput(compose.START)
-
- // 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, 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, nil, compose.WithNoDirectDependency())
-
wf.End().AddInput("b1", compose.ToField("bidder1")).
AddInput("b2", compose.ToField("bidder2"))
@@ -414,9 +327,9 @@ func main() {
}
```
-[Eino examples Code Link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/4_control_only_branch/main.go)
+Eino examples link: https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/4_control_only_branch/main.go
-New APIs introduced in this example:
+New API:
```go
func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode {
@@ -424,36 +337,28 @@ func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode {
}
```
-can specify a pure "control dependency" for a node via AddDependency.
-
### Branch
-In the example above, we added a branch in almost exactly the same way as with the Graph API:
+Add branches similarly to Graph, with Workflow branches carrying control only; data for branch targets is mapped via `AddInput*`:
```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}))
+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}))
```
-The semantics of branch are the same as those of branch in the AllPredecessor mode of Graph:
-
-- There is one and only one 'fromNode', meaning that a branch can have only one pre-control node.
-- Single selection (NewGraphBranch) and multiple selection (NewGraphMultiBranch) are supported.
-- The selected branch is executable, while the unselected branch is marked as skip.
-- A node can only be executed when all incoming edges are completed (successfully or skipped) and at least one edge succeeds (e.g., END in the example above).
-- If all incoming edges of a node are skip, then all outgoing edges of this node are automatically marked as skip.
+Branch semantics in Workflow (AllPredecessor mode) mirror Graph:
-Meanwhile, there is a core difference between the workflow branch and the graph branch:
+- Exactly one `fromNode` per branch
+- Single-select (`NewGraphBranch`) or multi-select (`NewGraphMultiBranch`)
+- Selected targets execute; unselected targets are marked skipped
+- A node executes only when all incoming edges finish (success or skip) and at least one succeeded
+- If all incoming edges are skip, all outgoing edges are auto-marked skip
-- The graph branch is always "control and data integrated", and the input of the downstream node of the branch must be the output of the branch fromNode.
-- The Workflow branch is always "control-only", and the inputs of the downstream nodes of the branch are specified by themselves through the AddInputWithOptions method.
+Workflow branches differ from Graph: Workflow branches are control-only; branch targets must declare their input mappings via `AddInput*`.
-New APIs involved:
+API:
```go
func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *WorkflowBranch {
@@ -467,15 +372,11 @@ func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *Wo
}
```
-is almost identical to Graph.AddBranch in signature and can add a branch to the workflow.
-
### Static Values
-Let's modify the above "auction" example by assigning a static configuration of "budget" to bidder 1 and bidder 2 respectively:
-
-
+Inject constants into node inputs via `SetStaticValue(fieldPath, value)`.
-budget1 and budget2 will be injected into the inputs of bidder1 and bidder2 respectively in the form of "static values". Use `SetStaticValue` method to configure static values for workflow nodes:
+
```go
func main() {
@@ -534,9 +435,9 @@ func main() {
}
```
-[Eino examples Code Link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/5_static_values/main.go)
+Eino examples link: https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/5_static_values/main.go
-New APIs involved here:
+API:
```go
func (n *WorkflowNode) SetStaticValue(path FieldPath, value any) *WorkflowNode {
@@ -545,22 +446,18 @@ func (n *WorkflowNode) SetStaticValue(path FieldPath, value any) *WorkflowNode {
}
```
-Set a static value for the specified field of the Workflow node through this method.
+### Streaming Effects
-### Streaming Effect
+Return to the “character counting” example, but now the workflow input is a stream of messages and the counting function returns a stream of counts:
-Returning to the previous "character count" example, if the input to our workflow is no longer a single message but a message stream, and our counting function can count each message chunk in the stream separately and return a "count stream":
+
-
+Changes:
-We will make some modifications to the previous example:
-
-- Change InvokableLambda to TransformableLambda so that it can consume streams and produce streams.
-- Change SubStr in the input to a static value and inject it into c1 and c2.
-- The overall input of the Workflow has been changed to *schema.Message.
-- Invoke the workflow in Transform mode and pass in a stream containing 2 *schema.Message.
-
-Completed code:
+- Use `TransformableLambda` to consume and produce streams
+- Make `SubStr` a static value injected into both `c1` and `c2`
+- Workflow input type becomes `*schema.Message`
+- Call the workflow via `Transform` with a stream of two messages
```go
// demonstrates the stream field mapping ability of eino workflow.
@@ -572,7 +469,7 @@ func main() {
}
// wordCounter is a transformable lambda function that
- // count occurrences of SubStr within FullStr, for each trunk.
+ // counts occurrences of SubStr within FullStr for each chunk.
wordCounter := func(ctx context.Context, c *schema.StreamReader[counter]) (
*schema.StreamReader[int], error) {
var subStr, cachedStr string
@@ -658,10 +555,10 @@ func main() {
return
}
- logs.Infof("%v", chunk)
+ logs.Infof("%v", chunk)
- contentCount += chunk["content_count"]
- reasoningCount += chunk["reasoning_content_count"]
+ contentCount += chunk["content_count"]
+ reasoningCount += chunk["reasoning_content_count"]
}
logs.Infof("content count: %d", contentCount)
@@ -669,50 +566,48 @@ func main() {
}
```
-[Eino examples Code Link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/6_stream_field_map/main.go)
+Eino examples link: https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/6_stream_field_map/main.go
-Based on the above example, we have summarized some characteristics of the workflow streaming:
+Characteristics of streaming in Workflow:
-- remains 100% Eino stream: four paradigms (invoke, stream, collect, transform), automatically converted, copied, spliced, and merged by the Eino framework.
-- The configuration of data field mapping does not require special handling of streams: regardless of whether the actual input and output are streams, the writing method of AddInput remains the same, and the Eino framework is responsible for handling stream-based mapping.
-- Static value, no special handling of the stream is required: Even if the actual input is a stream, SetStaticValue can be used in the same way. The Eino framework will place the static value in the input stream, but it is not necessarily the first chunk to be read.
+- Still 100% Eino stream: invoke/stream/collect/transform auto converted, copied, concatenated and merged by the framework
+- Field mappings need no special stream handling; Eino performs mapping over streams transparently
+- Static values need no special stream handling; they are injected into the input stream and may arrive after the first chunk
-### Scenarios of data field mapping
+### Field Mapping Scenarios
#### Type Alignment
-Workflow follows the same set of type alignment rules as Graph, except that the alignment granularity has changed from complete input-output alignment to type alignment between mapped paired fields. Specifically:
+- Identical types: passes compile and aligns at runtime
+- Different types but upstream assignable to downstream (e.g., upstream concrete type, downstream `any`): passes compile and aligns at runtime
+- Upstream not assignable to downstream (e.g., upstream `int`, downstream `string`): compile error
+- Upstream may be assignable only at runtime (e.g., upstream `any`, downstream `int`): compile defers; runtime checks actual upstream type and errors if not assignable
-- The types are exactly the same, and they will pass the validation during Compile, ensuring alignment.
-- Although the types are different, the upstream can be assigned to the downstream (for example, the specific type of the upstream and Any type of the downstream), and the validation will pass during compilation, ensuring alignment.
-- Upstream cannot be assigned to downstream (e.g., upstream int, downstream string), and an error will occur during compilation.
-- The upstream may be assignable to the downstream (e.g., upstream Any, downstream int), which cannot be determined at compile time and will be postponed until execution, when the actual type of the upstream is retrieved and then judged. At this time, if it is judged that the upstream cannot be assigned to the downstream, an error will be thrown.
+#### Merge Scenarios
-#### Scenarios of Merge
+Merging applies when a node’s input is mapped from multiple `FieldMapping`s:
-Merge refers to the situation where the input of a node is mapped from multiple `data field mapping`.
+- Map to multiple different fields: supported
+- Map to the same single field: not supported
+- Map whole input along with field mappings: conflict, not supported
-- maps to multiple different fields: Supported
-- Mapping to the same field: Not supported
-- is mapped to the whole, and also mapped to the data field: conflict, not supported
+#### Nested `map[string]any`
-#### nested map[string]any
+For mappings like `ToFieldPath([]string{"a","b"})` where the target input type is `map[string]any`, the framework ensures nested maps are created:
-For example, this data field mapping:`ToFieldPath([]string{"a","b"})`, the input type of the target node is `map[string]any`, and the order during mapping is:
+1. Level "a": `map[string]any{"a": nil}`
+2. Level "b": `map[string]any{"a": map[string]any{"b": x}}`
-1. Level 1 "a", the result at this time is `map[string]any{"a": nil}`
-2. Level 2 "b", where the result is `map[string]any{"a": map[string]any{"b": x}}`
-
-It can be seen that at the second level, the Eino framework automatically replaced any with the actual `map[string]any`
+At the second level, Eino replaces `any` with the actual `map[string]any` as needed.
#### CustomExtractor
-In some scenarios, the semantics of standard data field mapping cannot support, for example, when the upstream is []int and we want to extract the first element and map it to the downstream, in this case we use `WithCustomExtractor`:
+When standard field mapping semantics cannot express the intent (e.g., source is `[]int` and you need the first element), use `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) {
+ wf := compose.NewWorkflow[[]int, map[string]int]()
+ wf.End().AddInput(compose.START, compose.ToField("a", compose.WithCustomExtractor(func(input any) (any, error) {
return input.([]int)[0], nil
})))
r, err := wf.Compile(context.Background())
@@ -723,20 +618,18 @@ t.Run("custom extract from array element", func(t *testing.T) {
})
```
-When using WithCustomExtractor, all type alignment validations during Compile time cannot be performed and can only be postponed to runtime validation.
-
-### Some constraints
+With `WithCustomExtractor`, compile-time type alignment checks cannot be performed and are deferred to runtime.
-- Restrictions on Map Key: Only string or string alias (types that can be converted to string) are supported.
-- Unsupported CompileOption:
- - `WithNodeTriggerMode`, because it is fixed to `AllPredecessor`.
- - `WithMaxRunSteps`, because there will be no loops.
-- If the mapping source is Map Key, the Map must contain this key. However, if the mapping source is Stream, Einos cannot determine whether this key appears at least once in all frames of the stream, so verification cannot be performed for Stream.
-- If the source or target field of the mapping belongs to a struct, these fields must be exported because reflection is used internally.
-- Mapping source is nil: Generally supported, only reports an error when the mapping target cannot be nil, such as when the target is a basic type (int, etc.).
+### Constraints
-## Practical Application
+- Map key restrictions: only `string`, or string aliases convertible to `string`
+- Unsupported compile options:
+ - `WithNodeTriggerMode` (fixed `AllPredecessor`)
+ - `WithMaxRunSteps` (no cycles)
+- If the mapping source is a map key, the key must exist. For streams, existence across chunks cannot be verified ahead of time.
+- For struct fields as mapping sources or targets, fields must be exported (reflection is used internally).
+- Nil sources are generally supported; errors when the target cannot be nil (e.g., basic types).
-### Coze-Studio Workflow
+## Real-world Usage
-[Coze-Studio](https://github.com/coze-dev/coze-studio) The workflow engine of the open-source version is based on the Eino Workflow orchestration framework. See: [11. New Workflow Node Type (Backend)](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)
+Coze‑Studio’s open source workflow engine is built on Eino Workflow. See: 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/content/en/docs/eino/core_modules/components/_index.md b/content/en/docs/eino/core_modules/components/_index.md
index 5617987084e..4e93fe49d5b 100644
--- a/content/en/docs/eino/core_modules/components/_index.md
+++ b/content/en/docs/eino/core_modules/components/_index.md
@@ -1,71 +1,72 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Components'
-weight: 0
+weight: 1
---
-The most significant difference between LLM application development and traditional application development lies in the two core capabilities of LLMs:
+LLM application development differs from traditional app development primarily due to two core capabilities:
-- **Semantic Text Processing Capability**: The ability to understand and generate human language, handling the semantic relationships of unstructured content
-- **Intelligent Decision-Making Capability**: The ability to reason and judge based on context, making corresponding behavioral decisions
+- **Semantic text processing**: understanding and generating human language, handling relationships within unstructured content.
+- **Intelligent decision-making**: reasoning from context and making appropriate action decisions.
-These two core capabilities have given rise to three main application models:
+These capabilities lead to three major application patterns:
-1. **Direct Dialogue Model**: Processing user input and generating corresponding responses
-2. **Knowledge Processing Model**: Semantic processing, storage, and retrieval of text documents
-3. **Tool Invocation Model**: Making decisions based on context and invoking corresponding tools
+1. **Direct conversation**: process user input and produce responses.
+2. **Knowledge processing**: semantically process, store, and retrieve textual documents.
+3. **Tool calling**: reason from context and call appropriate tools.
-These models succinctly summarize the current major scenarios of LLM applications, providing us with a foundation for abstraction and standardization. Based on this, Eino abstracts these commonly used capabilities into reusable "Components."
+These patterns summarize common LLM app scenarios and provide a basis for abstraction and standardization. Eino abstracts these into reusable **components**.
-The relationship between component abstraction and these models corresponds as follows:
+Mapping components to patterns:
-**Dialogue Processing Components:**
+**Conversation components:**
-1. The component abstraction for templated processing and LLM interaction parameters: `ChatTemplate`
+1. Template and parameter preparation for LLM interaction: `ChatTemplate`
-> See [Eino: ChatTemplate guide](/docs/eino/core_modules/components/chat_template_guide)
+ - See [Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide)
-1. The component abstraction for direct interaction with LLMs: `ChatModel`
+2. Direct LLM interaction: `ChatModel`
-> See [Eino: ChatModel guide](/docs/eino/core_modules/components/chat_model_guide)
+ - See [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide)
-**Text Semantic Processing Components:**
+**Text semantics components:**
-1. The component abstraction for obtaining and processing text documents: `Document.Loader`, `Document.Transformer`
+1. Document acquisition and processing: `Document.Loader`, `Document.Transformer`
-> See [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide), [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide)
+ - See [Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide) and [Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide)
-1. The component abstraction for semantic processing of text documents: `Embedding`
+2. Semantic embedding of documents: `Embedding`
-> See [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide)
+ - See [Embedding Guide](/docs/eino/core_modules/components/embedding_guide)
-1. The component abstraction for storing the indexed data after embedding: `Indexer`
+3. Indexing and storage of embeddings: `Indexer`
-> See [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide)
+ - See [Indexer Guide](/docs/eino/core_modules/components/indexer_guide)
-1. Component abstraction for indexing and retrieving semantically related text documents: `Retriever`
+4. Retrieval of semantically related documents: `Retriever`
-> See [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
+ - See [Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
-**Decision Execution Components**:
+**Decision and execution components:**
-1. Component abstraction allowing the LLM to make decisions and call tools: `ToolsNode`
+1. Tool-enabled decision making for LLMs: `ToolsNode`
-> See [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)
+ - See [ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide)
-**Custom Components**:
+**Custom logic:**
-1. Component abstraction for user-defined code logic: `Lambda`
+1. User-defined business logic: `Lambda`
-> See [Eino: Lambda guide](/docs/eino/core_modules/components/lambda_guide)
+ - See [Lambda Guide](/docs/eino/core_modules/components/lambda_guide)
-Components are the providers of LLM application capabilities and serve as the building blocks in the construction of LLM applications. The quality of component abstraction determines the complexity of developing LLM applications. Eino's component abstractions adhere to the following design principles:
+Components provide application capabilities — the bricks and mortar of LLM app construction. Eino’s component abstractions follow these principles:
-1. **Modularity and Standardization**: Abstract a series of capabilities with similar functions into unified modules. The components have clear responsibilities and boundaries, supporting flexible composition.
-2. **Extensibility**: The interface design maintains minimal module capability constraints, making it convenient for developers to create custom components.
-3. **Reusability**: Encapsulate the most commonly used capabilities and implementations, providing developers with out-of-the-box tools.
+1. **Modularity and standardization**: unify common capabilities into clear modules with well-defined boundaries for flexible composition.
+2. **Extensibility**: keep interfaces minimally constraining so developers can implement custom components easily.
+3. **Reusability**: package common capabilities and implementations as ready-to-use tooling.
+
+These abstractions establish consistent development paradigms, reduce cognitive load, and improve collaboration efficiency, letting developers focus on business logic instead of reinventing the wheel.
-Component abstraction allows the development of LLM applications to form a relatively fixed paradigm, reducing cognitive complexity, and enhancing collaborative efficiency. By encapsulating components, developers can focus on implementing business logic, avoiding the reinvention of the wheel, and quickly building high-quality LLM applications.
diff --git a/content/en/docs/eino/core_modules/components/chat_model_guide.md b/content/en/docs/eino/core_modules/components/chat_model_guide.md
index dca99474c39..62eb6072f45 100644
--- a/content/en/docs/eino/core_modules/components/chat_model_guide.md
+++ b/content/en/docs/eino/core_modules/components/chat_model_guide.md
@@ -1,26 +1,26 @@
---
Description: ""
-date: "2025-05-07"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino: ChatModel guide'
-weight: 0
+title: 'Eino: ChatModel Guide'
+weight: 1
---
-## **Basic Introduction**
+## Overview
-The Model component is used to interact with large language models (LLM). Its primary function is to send user input messages to the language model and obtain the model's response. This component plays an important role in the following scenarios:
+The `Model` component enables interaction with large language models. It sends user messages to the model and receives responses. It’s essential for:
-- Natural language dialogue
+- Natural-language dialogues
- Text generation and completion
-- Parameter generation for tool invocation
-- Multimodal interaction (text, image, audio, etc.)
+- Generating tool-call parameters
+- Multimodal interactions (text, image, audio, etc.)
-## **Component Definition**
+## Component Definition
-### **Interface Definition**
+### Interfaces
-> Code Location: eino/components/model/interface.go
+> Code: `eino/components/model/interface.go`
```go
type BaseChatModel interface {
@@ -38,109 +38,114 @@ type ToolCallingChatModel interface {
}
```
-#### **Generate Method**
+#### Generate
-- Function: Generates a complete model response
-- Parameters:
- - ctx: Context object for passing request-level information and Callback Manager
- - input: List of input messages
- - opts: Optional parameters to configure model behavior
-- Return values:
- - `*schema.Message`: The response message generated by the model
- - error: Error information during the generation process
+- Purpose: produce a complete model response
+- Params:
+ - `ctx`: context for request-scoped info and callback manager
+ - `input`: list of input messages
+ - `opts`: options to configure model behavior
+- Returns:
+ - `*schema.Message`: the generated response
+ - `error`: if generation fails
-#### **Stream Method**
+#### Stream
-- Function: Generates model response in a streaming manner
-- Parameters: Same as the Generate method
-- Return values:
- - `*schema.StreamReader[*schema.Message]`: Stream reader for model response
- - error: Error information during the generation process
+- Purpose: produce a response as a stream
+- Params: same as `Generate`
+- Returns:
+ - `*schema.StreamReader[*schema.Message]`: stream reader for response chunks
+ - `error`
-#### **WithTools Method**
+#### WithTools
-- Function: Binds available tools to the model
-- Parameters:
- - tools: List of tool information
-- Return values:
- - ToolCallingChatModel: chatmodel that with tools info
- - error: Error information during the binding process
+- Purpose: bind available tools to the model
+- Params:
+ - `tools`: list of tool info definitions
+- Returns:
+ - `ToolCallingChatModel`: a model instance with tools bound
+ - `error`
-### **Message Structure**
+### Message Struct
-> Code Location: eino/schema/message.go
+> Code: `eino/schema/message.go`
```go
type Message struct {
- // Role indicates the role of the message (system/user/assistant/tool)
+ // Role indicates system/user/assistant/tool
Role RoleType
- // Content is the text content of the message
+ // Content is textual content
Content string
- // MultiContent is multimodal content supporting text, image, audio, etc.
- MultiContent []ChatMessagePart
- // Name is the name of the message sender
+ // MultiContent is deprecated; use UserInputMultiContent
+ // Deprecated
+ // MultiContent []ChatMessagePart
+ // UserInputMultiContent holds multimodal user inputs (text, image, audio, video, file)
+ // Use only for user-role messages
+ UserInputMultiContent []MessageInputPart
+ // AssistantGenMultiContent holds multimodal outputs from the model
+ // Use only for assistant-role messages
+ AssistantGenMultiContent []MessageOutputPart
+ // Name of the sender
Name string
- // ToolCalls is the tool invocation information in assistant messages
+ // ToolCalls in assistant messages
ToolCalls []ToolCall
- // ToolCallID is the tool invocation ID in tool messages
+ // ToolCallID for tool messages
ToolCallID string
- // ResponseMeta contains meta-information about the response
+ // ResponseMeta contains metadata
ResponseMeta *ResponseMeta
- // Extra is used to store additional information
+ // Extra for additional information
Extra map[string]any
}
```
-The `Message` structure is the basic structure for model interaction, supporting:
+Message supports:
-- Multiple roles: system, user, assistant (AI), tool
+- Roles: `system`, `user`, `assistant`, `tool`
- Multimodal content: text, image, audio, video, file
-- Tool invocation: supports model calling external tools and functions
-- Meta-information: includes reasons for the response, token usage statistics, etc.
+- Tool calls and function invocation
+- Metadata (reasoning, token usage, etc.)
-### **Common Options**
+### Common Options
-The Model component provides a set of common options to configure model behavior:
-
-> Code Location: eino/components/model/option.go
+> Code: `eino/components/model/option.go`
```go
type Options struct {
- // Temperature controls the randomness of the output
+ // Temperature controls randomness
Temperature *float32
- // MaxTokens controls the maximum number of tokens generated
+ // MaxTokens caps output tokens
MaxTokens *int
- // Model specifies the name of the model used
+ // Model selects a model name
Model *string
- // TopP controls the diversity of the output
+ // TopP controls diversity
TopP *float32
- // Stop specifies the conditions to stop generation
+ // Stop lists stop conditions
Stop []string
}
```
-Options can be set in the following way:
+Use options as:
```go
// Set temperature
WithTemperature(temperature float32) Option
-// Set maximum token count
+// Set max tokens
WithMaxTokens(maxTokens int) Option
// Set model name
WithModel(name string) Option
-// Set top_p value
+// Set top_p
WithTopP(topP float32) Option
// Set stop words
WithStop(stop []string) Option
```
-## **How to Use**
+## Usage
-### **Use Alone**
+### Standalone
```go
import (
@@ -153,48 +158,33 @@ import (
"github.com/cloudwego/eino/schema"
)
-// Initialize the model (using OpenAI as an example)
+// Initialize model (OpenAI example)
cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // Configuration parameters
+ // config
})
-// Prepare input messages
+// Prepare messages
messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful assistant.",
- },
- {
- Role: schema.User,
- Content: "Hello!",
- },
+ { Role: schema.System, Content: "你是一个有帮助的助手。" },
+ { Role: schema.User, Content: "你好!" },
}
// Generate response
response, err := cm.Generate(ctx, messages, model.WithTemperature(0.8))
-
-// Handle response
fmt.Print(response.Content)
-// Stream generation
+// Stream response
streamResult, err := cm.Stream(ctx, messages)
-
defer streamResult.Close()
-
for {
chunk, err := streamResult.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- // Error handling
- }
- // Handle response chunk
+ if err == io.EOF { break }
+ if err != nil { /* handle error */ }
fmt.Print(chunk.Content)
}
```
-### **Use in Orchestration**
+### In Orchestration
```go
import (
@@ -210,20 +200,18 @@ import (
c := compose.NewChain[[]*schema.Message, *schema.Message]()
c.AppendChatModel(cm)
-
// Use in Graph
g := compose.NewGraph[[]*schema.Message, *schema.Message]()
g.AddChatModelNode("model_node", cm)
```
-## **Using Option and Callback**
+## Options and Callbacks
-### **Option Usage Example**
+### Options Example
```go
import "github.com/cloudwego/eino/components/model"
-// Use Option
response, err := cm.Generate(ctx, messages,
model.WithTemperature(0.7),
model.WithMaxTokens(2000),
@@ -231,7 +219,7 @@ response, err := cm.Generate(ctx, messages,
)
```
-### **Callback Usage Example**
+### Callback Example
```go
import (
@@ -245,100 +233,90 @@ import (
callbacksHelper "github.com/cloudwego/eino/utils/callbacks"
)
-// Create callback handler
+// define callback handler
handler := &callbacksHelper.ModelCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *model.CallbackInput) context.Context {
- fmt.Printf("Generation started, number of input messages: %d\n", len(input.Messages))
- return ctx
+ fmt.Printf("start, input messages: %d\n", len(input.Messages))
+ return ctx
},
OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *model.CallbackOutput) context.Context {
- fmt.Printf("Generation completed, Token usage: %+v\n", output.TokenUsage)
- return ctx
+ fmt.Printf("end, token usage: %+v\n", output.TokenUsage)
+ return ctx
},
OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*model.CallbackOutput]) context.Context {
- fmt.Println("Receiving streaming output")
- defer output.Close()
- return ctx
+ fmt.Println("stream started")
+ defer output.Close()
+ for {
+ chunk, err := output.Recv()
+ if errors.Is(err, io.EOF) { break }
+ if err != nil { fmt.Printf("stream read error: %v\n", err); break }
+ if chunk == nil || chunk.Message == nil { continue }
+ if len(chunk.Message.ToolCalls) > 0 {
+ for _, tc := range chunk.Message.ToolCalls {
+ fmt.Printf("ToolCall detected, arguments: %s\n", tc.Function.Arguments)
+ }
+ }
+ }
+ return ctx
},
}
-// Use callback handler
+// use callback handler
helper := callbacksHelper.NewHandlerHelper().
ChatModel(handler).
Handler()
-/*** compose a chain
-* chain := NewChain
-* chain.appendxxx().
-* appendxxx().
-* ...
-*/
-
-// Use at runtime
-runnable, err := chain.Compile()
-if err != nil {
- return err
-}
-result, err := runnable.Invoke(ctx, messages, compose.WithCallbacks(helper))
+chain := compose.NewChain[[]*schema.Message, *schema.Message]()
+chain.AppendChatModel(cm)
+run, _ := chain.Compile(ctx)
+result, _ := run.Invoke(ctx, messages, compose.WithCallbacks(helper))
```
-## **Existing Implementations**
+## Existing Implementations
-1. OpenAI ChatModel: Use OpenAI's GPT series models [ChatModel - OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
-2. Ollama ChatModel: Use Ollama local models [ChatModel - Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
-3. ARK ChatModel: Use ARK platform's model service [ChatModel - ARK](/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
-4. See more implementation at: [Eino ChatModel](https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model/)
+1. OpenAI ChatModel: [ChatModel — OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
+2. Ollama ChatModel: [ChatModel — Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
+3. Ark ChatModel: [ChatModel — Ark](/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+4. More: [Eino ChatModel](/docs/eino/ecosystem_integration/chat_model/)
-## **Self-Implementation Reference**
+## Implementation Notes
-When implementing a custom ChatModel component, you need to pay attention to the following points:
+1. Implement common options and any provider‑specific options
+2. Implement callbacks correctly for both Generate and Stream
+3. Close the stream writer after streaming completes to avoid resource leaks
-1. Make sure to implement common options
-2. Ensure the callback mechanism is implemented
-3. Remember to close the writer after completing the output during streaming
+### Option Mechanism
-### **Option Mechanism**
-
-When customizing ChatModel, if you need options other than the common ones, you can use the component abstraction utility functions to implement custom options, for example:
+Custom ChatModels can define additional options beyond common `model.Options` using helper wrappers:
```go
import (
"time"
-
"github.com/cloudwego/eino/components/model"
)
-// Define the Option struct
type MyChatModelOptions struct {
Options *model.Options
RetryCount int
Timeout time.Duration
}
-// Define Option functions
func WithRetryCount(count int) model.Option {
- return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) {
- o.RetryCount = count
- })
+ return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) { o.RetryCount = count })
}
func WithTimeout(timeout time.Duration) model.Option {
- return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) {
- o.Timeout = timeout
- })
+ return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) { o.Timeout = timeout })
}
```
-### **Callback Handling**
+### Callback Handling
-ChatModel implementation needs to trigger callbacks at appropriate times. The following structures are defined by the ChatModel component:
+ChatModel implementations should trigger callbacks at appropriate times. Structures defined by the component:
```go
-import (
- "github.com/cloudwego/eino/schema"
-)
+import "github.com/cloudwego/eino/schema"
-// Define callback input and output
type CallbackInput struct {
Messages []*schema.Message
Model string
@@ -354,7 +332,7 @@ type CallbackOutput struct {
}
```
-### **Complete Implementation Example**
+### Complete Implementation Example
```go
import (
@@ -377,113 +355,73 @@ type MyChatModel struct {
retryCount int
}
-type MyChatModelConfig struct {
- APIKey string
-}
+type MyChatModelConfig struct { APIKey string }
func NewMyChatModel(config *MyChatModelConfig) (*MyChatModel, error) {
- if config.APIKey == "" {
- return nil, errors.New("api key is required")
- }
-
- return &MyChatModel{
- client: &http.Client{},
- apiKey: config.APIKey,
- }, nil
+ if config.APIKey == "" { return nil, errors.New("api key is required") }
+ return &MyChatModel{ client: &http.Client{}, apiKey: config.APIKey }, nil
}
func (m *MyChatModel) Generate(ctx context.Context, messages []*schema.Message, opts ...model.Option) (*schema.Message, error) {
- // 1. Handle options
options := &MyChatModelOptions{
- Options: &model.Options{
- Model: &m.model,
- },
- RetryCount: m.retryCount,
- Timeout: m.timeout,
+ Options: &model.Options{ Model: &m.model },
+ RetryCount: m.retryCount,
+ Timeout: m.timeout,
}
options.Options = model.GetCommonOptions(options.Options, opts...)
options = model.GetImplSpecificOptions(options, opts...)
- // 2. Callback before starting generation
ctx = callbacks.OnStart(ctx, &model.CallbackInput{
- Messages: messages,
- Config: &model.Config{
- Model: *options.Options.Model,
- },
+ Messages: messages,
+ Config: &model.Config{
+ Model: *options.Options.Model,
+ },
})
- // 3. Execute generation logic
response, err := m.doGenerate(ctx, messages, options)
+ if err != nil { callbacks.OnError(ctx, err); return nil, err }
- // 4. Handle errors and end callback
- if err != nil {
- ctx = callbacks.OnError(ctx, err)
- return nil, err
- }
-
- ctx = callbacks.OnEnd(ctx, &model.CallbackOutput{
- Message: response,
- })
-
+ callbacks.OnEnd(ctx, &model.CallbackOutput{ Message: response })
return response, nil
}
func (m *MyChatModel) Stream(ctx context.Context, messages []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) {
- // 1. Handle options
options := &MyChatModelOptions{
- Options: &model.Options{
- Model: &m.model,
- },
- RetryCount: m.retryCount,
- Timeout: m.timeout,
+ Options: &model.Options{ Model: &m.model },
+ RetryCount: m.retryCount,
+ Timeout: m.timeout,
}
options.Options = model.GetCommonOptions(options.Options, opts...)
options = model.GetImplSpecificOptions(options, opts...)
- // 2. Callback before starting streaming generation
ctx = callbacks.OnStart(ctx, &model.CallbackInput{
- Messages: messages,
- Config: &model.Config{
- Model: *options.Options.Model,
- },
+ Messages: messages,
+ Config: &model.Config{
+ Model: *options.Options.Model,
+ },
})
- // 3. Create Streaming Response
- // Pipe creates a StreamReader and a StreamWriter, writing to the StreamWriter can be read from the StreamReader, and both are thread-safe.
- // Implement asynchronous writing to the StreamWriter with the generated content, returning the StreamReader as the result
- // ***StreamReader is a data stream that can only be read once. When implementing the callback by yourself, it is necessary to pass the data stream to the callback through OnEndWithCallbackOutput and also return the data stream, requiring a copy of the data stream
- // Considering that such scenarios always require copying the data stream, the OnEndWithCallbackOutput function will internally copy and return an unread stream
- // The following code demonstrates a streaming processing method, but it is not the only way
+ // Pipe produces a StreamReader and StreamWriter; writes to the writer are readable from the reader
sr, sw := schema.Pipe[*model.CallbackOutput](1)
- // 4. Start Asynchronous Generation
+ // Asynchronously generate and write to the stream
go func() {
- defer sw.Close()
-
- // Stream writing
- m.doStream(ctx, messages, options, sw)
+ defer sw.Close()
+ m.doStream(ctx, messages, options, sw)
}()
- // 5. Complete Callback
+ // Copy stream for callbacks and return a fresh reader
_, nsr := callbacks.OnEndWithStreamOutput(ctx, sr)
-
- return schema.StreamReaderWithConvert(nsr, func(t *model.CallbackOutput) (*schema.Message, error) {
- return t.Message, nil
- }), nil
+ return schema.StreamReaderWithConvert(nsr, func(t *model.CallbackOutput) (*schema.Message, error) { return t.Message, nil }), nil
}
-func (m *MyChatModel) BindTools(tools []*schema.ToolInfo) error {
- // Implement tool binding logic
- return nil
+func (m *MyChatModel) WithTools(tools []*schema.ToolInfo) (model.ToolCallingChatModel, error) {
+ return nil, nil
}
func (m *MyChatModel) doGenerate(ctx context.Context, messages []*schema.Message, opts *MyChatModelOptions) (*schema.Message, error) {
- // Implement generation logic
return nil, nil
}
-func (m *MyChatModel) doStream(ctx context.Context, messages []*schema.Message, opts *MyChatModelOptions, sr *schema.StreamWriter[*model.CallbackOutput]) {
- // Stream generate text written into sr
- return
-}
+func (m *MyChatModel) doStream(ctx context.Context, messages []*schema.Message, opts *MyChatModelOptions, sw *schema.StreamWriter[*model.CallbackOutput]) {}
```
diff --git a/content/en/docs/eino/core_modules/components/chat_template_guide.md b/content/en/docs/eino/core_modules/components/chat_template_guide.md
index 26f75111b79..5c793b1580b 100644
--- a/content/en/docs/eino/core_modules/components/chat_template_guide.md
+++ b/content/en/docs/eino/core_modules/components/chat_template_guide.md
@@ -1,25 +1,25 @@
---
Description: ""
-date: "2025-04-21"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: 'Eino: ChatTemplate guide'
-weight: 0
+title: 'Eino: ChatTemplate Guide'
+weight: 2
---
-## **Basic Introduction**
+## Introduction
-The Prompt component is a tool for processing and formatting prompt templates. Its primary function is to fill user-provided variable values into predefined message templates, generating standard message formats for interacting with language models. This component can be used in the following scenarios:
+The `Prompt` component formats message templates by filling user-provided variables into predefined message structures. It’s used to generate standardized messages for model interaction and is useful for:
-- Constructing structured system prompts
-- Handling templates for multi-turn conversations (including history)
-- Implementing reusable prompt patterns
+- Structured system prompts
+- Multi-turn dialogue templates (including history)
+- Reusable prompt patterns
-## **Component Definition**
+## Component Definition
-### **Interface Definition**
+### Interface
-> Code Location:eino/components/prompt/interface.go
+> Code: `eino/components/prompt/interface.go`
```go
type ChatTemplate interface {
@@ -27,59 +27,52 @@ type ChatTemplate interface {
}
```
-#### **Format Method**
+#### Format
-- Function: Fills variable values into message templates.
-- Parameters:
- - ctx: Context object used to pass request-level information as well as the Callback Manager
- - vs: Variable value map used to fill placeholders in the template
- - opts: Optional parameters to configure formatting behavior
-- Return Values:
- - `[]*schema.Message`: Formatted message list
- - error: Error information during the formatting process
+- Purpose: fill variables into the message template
+- Params:
+ - `ctx`: request-scoped info and callback manager
+ - `vs`: variables map used to fill placeholders
+ - `opts`: optional formatting controls
+- Returns:
+ - `[]*schema.Message`: formatted messages
+ - `error`
-### **Built-in Template Formats**
+### Built-in Templating
-The Prompt component supports three built-in template formats:
+Prompt supports three built-in templating modes:
-1. FString Format (schema.FString)
- - Uses `{variable}` syntax for variable replacement
- - Simple and intuitive, suitable for basic text replacement scenarios
- - Example: `"You are a {role}, please help me {task}."`
-2. GoTemplate Format (schema.GoTemplate)
- - Uses Go standard library's text/template syntax
- - Supports conditional judgments, loops, and other complex logic
- - Example: `"{{if .expert}}As an expert{{end}}, please {{.action}}"`
-3. Jinja2 Format (schema.Jinja2)
- - Uses Jinja2 template syntax
- - Example: `"{% if level == 'expert' %}From an expert's perspective{% endif %} analyze {{topic}}"`
+1. `FString` (`schema.FString`)
+ - `{variable}` syntax for substitution
+ - Simple and direct for basic text replacement
+ - Example: `"You are a {role}. Please help me {task}."`
+2. `GoTemplate` (`schema.GoTemplate`)
+ - Go `text/template` syntax
+ - Supports conditionals, loops, etc.
+ - Example: `"{{if .expert}}As an expert{{end}} please {{.action}}"`
+3. `Jinja2` (`schema.Jinja2`)
+ - Jinja2 template syntax
+ - Example: `"{% if level == 'expert' %}From an expert perspective{% endif %} analyze {{topic}}"`
-### **Common Options**
+### Options
-The Prompt component uses Options to define optional parameters. ChatTemplate does not have a common option abstraction. Each specific implementation can define its own specific Options, which can be wrapped into a unified Option type via the WrapImplSpecificOptFn function.
+Prompt includes an `Option` mechanism; there’s no global option abstraction. Each implementation may define its own specific options and wrap them via `WrapImplSpecificOptFn`.
-## **Usage**
+## Usage
-ChatTemplate is generally used for context preparation before ChatModel.
+`ChatTemplate` is typically used before `ChatModel` to prepare context.
-### create chat template
+### Creation Methods
-- `prompt.FromMessages()`
- - Used to create a template.
-- `schema.Message{}`
- - schema.Message is a struct that implements the Format interface, so you can directly construct `schema.Message{}` as a template.
-- `schema.SystemMessage()`
- - This method is a shortcut for constructing a message with the role of "system".
-- `schema.AssistantMessage()`
- - This method is a shortcut for constructing a message with the role of "assistant".
-- `schema.UserMessage()`
- - This method is a shortcut for constructing a message with the role of "user".
-- `schema.ToolMessage()`
- - This method is a shortcut for constructing a message with the role of "tool".
-- `schema.MessagesPlaceholder()`
- - Can be used to insert a `[]*schema.Message` into a message list, often used for inserting historical conversations.
+- `prompt.FromMessages()` — compose multiple messages into a template.
+- `schema.Message{}` — since `Message` implements `Format`, you can use it directly as a template.
+- `schema.SystemMessage()` — create a system-role message.
+- `schema.AssistantMessage()` — create an assistant-role message.
+- `schema.UserMessage()` — create a user-role message.
+- `schema.ToolMessage()` — create a tool-role message.
+- `schema.MessagesPlaceholder()` — insert a `[]*schema.Message` (e.g., history) into the message list.
-### **Standalone Usage**
+### Standalone Usage
```go
import (
@@ -89,26 +82,23 @@ import (
// Create template
template := prompt.FromMessages(schema.FString,
- schema.SystemMessage("You are a {role}."),
- schema.MessagesPlaceholder("history_key", false),
- &schema.Message{
- Role: schema.User,
- Content: "Please help me {task}.",
- },
+ schema.SystemMessage("你是一个{role}。"),
+ schema.MessagesPlaceholder("history_key", false),
+ &schema.Message{ Role: schema.User, Content: "请帮我{task}。" },
)
-// Prepare variables
+// Variables
variables := map[string]any{
- "role": "professional assistant",
- "task": "write a poem",
- "history_key": []*schema.Message{{Role: schema.User, Content: "what is Go?"}, {Role: schema.Assistant, Content: "Go is xxx"}},
+ "role": "专业的助手",
+ "task": "写一首诗",
+ "history_key": []*schema.Message{{Role: schema.User, Content: "告诉我油画是什么?"}, {Role: schema.Assistant, Content: "油画是xxx"}},
}
-// Format template
+// Format
messages, err := template.Format(context.Background(), variables)
```
-### **Usage in Orchestration**
+### In Orchestration
```go
import (
@@ -124,7 +114,7 @@ chain.AppendChatTemplate(template)
// Compile and run
runnable, err := chain.Compile()
if err != nil {
- return err
+ return err
}
result, err := runnable.Invoke(ctx, variables)
@@ -133,9 +123,22 @@ graph := compose.NewGraph[map[string]any, []*schema.Message]()
graph.AddChatTemplateNode("template_node", template)
```
-## **Usage of Option and Callback**
+### Pull Data from a Predecessor Node
+
+Use `WithOutputKey` to map a node’s output into a keyed `map[string]any`:
+
+```go
+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"))
+```
+
+Then refer to that key within a downstream `ChatTemplate` node.
+
+## Options and Callbacks
-### **Callback Usage Example**
+### Callback Example
```go
import (
@@ -147,70 +150,55 @@ import (
"github.com/cloudwego/eino/components/prompt"
)
-// Create callback handler
handler := &callbackHelper.PromptCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *prompt.CallbackInput) context.Context {
- fmt.Printf("Starting template formatting, variables: %v\n", input.Variables)
+ fmt.Printf("Formatting template; variables: %v\n", input.Variables)
return ctx
},
OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *prompt.CallbackOutput) context.Context {
- fmt.Printf("Template formatting completed, number of generated messages: %d\n", len(output.Result))
+ fmt.Printf("Template formatted; messages: %d\n", len(output.Result))
return ctx
},
}
-// Use callback handler
-helper := template.NewHandlerHelper().
+helper := callbackHelper.NewHandlerHelper().
Prompt(handler).
Handler()
-// Use at runtime
runnable, err := chain.Compile()
-if err != nil {
- return err
-}
result, err := runnable.Invoke(ctx, variables, compose.WithCallbacks(helper))
```
-## **Reference for Custom Implementation**
+## Implementation Notes
-### **Option Mechanism**
+### Option Mechanism
-If necessary, component implementers can create custom prompt options:
+If needed, define custom prompt options:
```go
import (
"github.com/cloudwego/eino/components/prompt"
)
-// Define Option struct
type MyPromptOptions struct {
StrictMode bool
DefaultValues map[string]string
}
-// Define Option functions
func WithStrictMode(strict bool) prompt.Option {
- return prompt.WrapImplSpecificOptFn(func(o *MyPromptOptions) {
- o.StrictMode = strict
- })
+ return prompt.WrapImplSpecificOptFn(func(o *MyPromptOptions) { o.StrictMode = strict })
}
func WithDefaultValues(values map[string]string) prompt.Option {
- return prompt.WrapImplSpecificOptFn(func(o *MyPromptOptions) {
- o.DefaultValues = values
- })
+ return prompt.WrapImplSpecificOptFn(func(o *MyPromptOptions) { o.DefaultValues = values })
}
```
-### **Callback Handling**
-
-Prompt implementation needs to trigger callbacks at the appropriate times. The following structure is predefined by the component:
+### Callback Structures
-> Code Location: eino/components/prompt/callback_extra.go
+> Code: `eino/components/prompt/callback_extra.go`
```go
-// Define callback input and output
type CallbackInput struct {
Variables map[string]any
Templates []schema.MessagesTemplate
@@ -224,7 +212,7 @@ type CallbackOutput struct {
}
```
-### **Complete Implementation Example**
+### Complete Implementation Example
```go
type MyPrompt struct {
@@ -244,41 +232,30 @@ func NewMyPrompt(config *MyPromptConfig) (*MyPrompt, error) {
}
func (p *MyPrompt) Format(ctx context.Context, vs map[string]any, opts ...prompt.Option) ([]*schema.Message, error) {
- // 1. Handle Option
options := &MyPromptOptions{
StrictMode: p.strictMode,
DefaultValues: p.defaultValues,
}
options = prompt.GetImplSpecificOptions(options, opts...)
-
- // 2. Get callback manager
cm := callbacks.ManagerFromContext(ctx)
-
- // 3. Callback before formatting starts
ctx = cm.OnStart(ctx, info, &prompt.CallbackInput{
Variables: vs,
Templates: p.templates,
})
-
- // 4. Execute formatting logic
messages, err := p.doFormat(ctx, vs, options)
-
- // 5. Handle errors and completion callback
if err != nil {
ctx = cm.OnError(ctx, info, err)
return nil, err
}
-
ctx = cm.OnEnd(ctx, info, &prompt.CallbackOutput{
Result: messages,
Templates: p.templates,
})
-
return messages, nil
}
func (p *MyPrompt) doFormat(ctx context.Context, vs map[string]any, opts *MyPromptOptions) ([]*schema.Message, error) {
- // Implement custom logic
+ // 实现自己定义逻辑
return messages, nil
}
```
diff --git a/content/en/docs/eino/core_modules/components/document_loader_guide/_index.md b/content/en/docs/eino/core_modules/components/document_loader_guide/_index.md
index 436c3788c39..a997debf945 100644
--- a/content/en/docs/eino/core_modules/components/document_loader_guide/_index.md
+++ b/content/en/docs/eino/core_modules/components/document_loader_guide/_index.md
@@ -1,24 +1,24 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: 'Eino: Document Loader guide'
-weight: 0
+title: 'Eino: Document Loader Guide'
+weight: 8
---
-## **Basic Introduction**
+## Introduction
-Document Loader is a component used for loading documents. Its primary function is to load document content from different sources (such as web URLs, local files, etc.) and convert it into a standard document format. This component plays an important role in scenarios where document content needs to be sourced from various origins, such as:
+`Document Loader` loads documents from various sources (e.g., web URLs, local files) and converts them into a standard document format. It’s useful for scenarios such as:
-- Loading web page content from a web URL
-- Reading local documents in formats such as PDF, Word, etc.
+- Loading web content from URLs
+- Reading local documents like PDF or Word
-## **Component Definition**
+## Component Definition
-### **Interface Definition**
+### Interface
-> Code Location: eino/components/document/interface.go
+> Code: `eino/components/document/interface.go`
```go
type Loader interface {
@@ -26,18 +26,18 @@ type Loader interface {
}
```
-#### **Load Method**
+#### Load
-- Function: Loads documents from a specified data source
-- Parameters:
- - ctx: Context object used to pass request-level information, and to pass the Callback Manager as well
- - src: Document source containing the URI information of the document
- - opts: Load options used to configure loading behavior
+- Purpose: load documents from a given source
+- Params:
+ - `ctx`: request context and callback manager
+ - `src`: document source including URI
+ - `opts`: loader options
- Returns:
- - `[]*schema.Document`: List of loaded documents
- - error: Error information during the loading process
+ - `[]*schema.Document`: loaded documents
+ - `error`
-### **Source Struct**
+### Source
```go
type Source struct {
@@ -45,43 +45,35 @@ type Source struct {
}
```
-The Source struct defines the source information of a document:
+Defines source information:
-- URI: The Uniform Resource Identifier of the document, which can be a web URL or a local file path
+- `URI`: a web URL or local file path
-### **Document Struct**
+### Document
```go
type Document struct {
- // ID is the unique identifier of the document
ID string
- // Content is the content of the document
Content string
- // MetaData is used to store metadata information of the document
MetaData map[string]any
}
```
-The Document struct is the standard format of a document, containing the following important fields:
+Standard document fields:
-- ID: The unique identifier of the document, used to uniquely identify a document in the system
-- Content: The actual content of the document
-- MetaData: The metadata of the document, which can store the following information:
- - Source information of the document
- - Vector representation of the document (used for vector retrieval)
- - Score of the document (used for sorting)
- - Sub-index of the document (used for hierarchical retrieval)
- - Other custom metadata
+- `ID`: unique identifier
+- `Content`: document content
+- `MetaData`: source info, embeddings, scores, sub-indexes, and custom metadata
-### **Common Options**
+### Options
-The Loader component uses `LoaderOption` to define loading options. Currently, Loader does not have common Options; each specific implementation can define its own specific options, which are wrapped into a unified `LoaderOption` type through the `WrapLoaderImplSpecificOptFn` function.
+`LoaderOption` represents loader options. There is no global common option; each implementation defines its own and wraps via `WrapLoaderImplSpecificOptFn`.
-## **Usage**
+## Usage
-### **Standalone Use**
+### Standalone
-> Code Location: eino-ext/components/document/loader/file/examples/fileloader
+> Code: `eino-ext/components/document/loader/file/examples/fileloader`
```go
import (
@@ -89,43 +81,32 @@ import (
"github.com/cloudwego/eino-ext/components/document/loader/file"
)
-// Initialize loader (take file loader as an example)
-loader, _ := file.NewFileLoader(ctx, &file.FileLoaderConfig{
- // Configuration parameters
- UseNameAsID: true,
-})
+loader, _ := file.NewFileLoader(ctx, &file.FileLoaderConfig{ UseNameAsID: true })
-// Load document
filePath := "../../testdata/test.md"
-docs, _ := loader.Load(ctx, document.Source{
- URI: filePath,
-})
-
+docs, _ := loader.Load(ctx, document.Source{ URI: filePath })
log.Printf("doc content: %v", docs[0].Content)
```
-### **Use in Orchestration**
+### In Orchestration
```go
-// Use in Chain
-chain := compose.NewChain[document.Source, []*schema.Document]()
+// Chain
+chain := compose.NewChain[string, []*schema.Document]()
chain.AppendLoader(loader)
-
-// Compile and run
runnable, _ := chain.Compile()
-
result, _ := runnable.Invoke(ctx, input)
-// Use in Graph
+// Graph
graph := compose.NewGraph[string, []*schema.Document]()
graph.AddLoaderNode("loader_node", loader)
```
-## **Option and Callback Usage**
+## Options and Callbacks
-### **Callback Usage Example**
+### Callback Example
-> Code location: eino-ext/components/document/loader/file/examples/fileloader
+> Code: `eino-ext/components/document/loader/file/examples/fileloader`
```go
import (
@@ -134,83 +115,60 @@ import (
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
callbacksHelper "github.com/cloudwego/eino/utils/callbacks"
-
"github.com/cloudwego/eino-ext/components/document/loader/file"
)
-// Create callback handler
handler := &callbacksHelper.LoaderCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *document.LoaderCallbackInput) context.Context {
log.Printf("start loading docs...: %s\n", input.Source.URI)
return ctx
},
OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *document.LoaderCallbackOutput) context.Context {
- log.Printf("complete loading docs,total loaded docs: %d\n", len(output.Docs))
+ log.Printf("complete loading docs, total: %d\n", len(output.Docs))
return ctx
},
- // OnError
}
-// Use callback handler
-helper := callbacksHelper.NewHandlerHelper().
- Loader(handler).
- Handler()
+helper := callbacksHelper.NewHandlerHelper().Loader(handler).Handler()
chain := compose.NewChain[document.Source, []*schema.Document]()
chain.AppendLoader(loader)
-// Use at runtime
run, _ := chain.Compile(ctx)
-outDocs, _ := run.Invoke(ctx, document.Source{
- URI: filePath,
-}, compose.WithCallbacks(helper))
-
+outDocs, _ := run.Invoke(ctx, document.Source{ URI: filePath }, compose.WithCallbacks(helper))
log.Printf("doc content: %v", outDocs[0].Content)
```
-## **Existing Implementations**
+## Existing Implementations
-1. File Loader: Used to load documents from the local file system [Loader - local file](/docs/eino/ecosystem_integration/document/loader_local_file)
-2. Web Loader: Used to load documents pointed by web URLs [Loader - web url](/docs/eino/ecosystem_integration/document/loader_web_url)
-3. S3 Loader: Used to load documents stored in S3 compatible storage systems [Loader - amazon s3](/docs/eino/ecosystem_integration/document/loader_amazon_s3)
+1. File Loader — local filesystem: [Loader — local file](/docs/eino/ecosystem_integration/document/loader_local_file)
+2. Web Loader — HTTP/HTTPS: [Loader — web url](/docs/eino/ecosystem_integration/document/loader_web_url)
+3. S3 Loader — S3-compatible storage: [Loader — Amazon S3](/docs/eino/ecosystem_integration/document/loader_amazon_s3)
-## **Reference for Self-Implementation**
+## Implementation Notes
-When self-implementing a loader component, attention must be paid to the option mechanism and callback handling.
-
-### **Option Mechanism**
-
-Custom Loaders need to implement their own Option parameter mechanism:
+### Option Mechanism
```go
-// Define the options struct
type MyLoaderOptions struct {
Timeout time.Duration
RetryCount int
}
-// Define option functions
func WithTimeout(timeout time.Duration) document.LoaderOption {
- return document.WrapLoaderImplSpecificOptFn(func(o *MyLoaderOptions) {
- o.Timeout = timeout
- })
+ return document.WrapLoaderImplSpecificOptFn(func(o *MyLoaderOptions) { o.Timeout = timeout })
}
func WithRetryCount(count int) document.LoaderOption {
- return document.WrapLoaderImplSpecificOptFn(func(o *MyLoaderOptions) {
- o.RetryCount = count
- })
+ return document.WrapLoaderImplSpecificOptFn(func(o *MyLoaderOptions) { o.RetryCount = count })
}
```
-### **Callback Handling**
-
-Loader implementations need to trigger callbacks at appropriate times:
+### Callback Structures
-> Code Location: eino/components/document/callback_extra_loader.go
+> Code: `eino/components/document/callback_extra_loader.go`
```go
-// These are the callback input and output defined by the loader component. Implementations should satisfy the parameter meanings.
type LoaderCallbackInput struct {
Source Source
Extra map[string]any
@@ -223,7 +181,7 @@ type LoaderCallbackOutput struct {
}
```
-### **Complete Implementation Example**
+### Full Implementation Example
```go
import (
@@ -233,46 +191,41 @@ import (
)
func NewCustomLoader(config *Config) (*CustomLoader, error) {
- return & CustomLoader {
- timeout: config.DefaultTimeout,
+ return &CustomLoader{
+ timeout: config.DefaultTimeout,
retryCount: config.DefaultRetryCount,
}, nil
}
type CustomLoader struct {
- timeout time.Duration
+ timeout time.Duration
retryCount int
}
type Config struct {
- DefaultTimeout time.Duration
+ DefaultTimeout time.Duration
DefaultRetryCount int
}
func (l *CustomLoader) Load(ctx context.Context, src document.Source, opts ...document.LoaderOption) ([]*schema.Document, error) {
- // 1. Handle options
options := &customLoaderOptions{
- Timeout: l.timeout,
+ Timeout: l.timeout,
RetryCount: l.retryCount,
}
options = document.GetLoaderImplSpecificOptions(options, opts...)
var err error
- // 2. Handle errors and trigger error callbacks
defer func() {
if err != nil {
callbacks.OnError(ctx, err)
}
}()
- // 3. Trigger pre-load callback
ctx = callbacks.OnStart(ctx, &document.LoaderCallbackInput{
Source: src,
})
- // 4. Execute load logic
docs, err := l.doLoad(ctx, src, options)
-
if err != nil {
return nil, err
}
@@ -286,23 +239,20 @@ func (l *CustomLoader) Load(ctx context.Context, src document.Source, opts ...do
}
func (l *CustomLoader) doLoad(ctx context.Context, src document.Source, opts *customLoaderOptions) ([]*schema.Document, error) {
- // Implement document load logic
- // 1. Load document content
- // 2. Construct Document objects, ensuring to save important information such as document source in MetaData
return []*schema.Document{{
Content: "Hello World",
}}, nil
}
```
-### **Precautions**
+### Notes
-- MetaData is an important part of the document, used to save various metadata of the document
-- Return meaningful error information when document loading fails to facilitate error troubleshooting
+- `MetaData` is critical for storing document source and other metadata
+- Return meaningful errors on load failures for easier debugging
-## **Other Reference Documents**
+## Other References
-- [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide)
-- [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide)
-- [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide)
-- [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
+- [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide)
+- [Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide)
+- [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide)
+- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
diff --git a/content/en/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md b/content/en/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
index 69baa57fc4a..8a9c2b80c41 100644
--- a/content/en/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
+++ b/content/en/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
@@ -1,87 +1,85 @@
---
Description: ""
-date: "2025-04-21"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Document Parser guide'
-weight: 0
+title: 'Eino: Document Parser Interface Guide'
+weight: 1
---
-## **Basic Introduction**
+## Introduction
-Document Parser is a toolkit for parsing document content. It is not a standalone component but an internal tool used by Document Loader to parse raw content of various formats into standard document formats. The Parser supports:
+`Document Parser` is a toolkit for parsing raw content into standard documents. It is not a standalone component; it’s used inside `Document Loader`. Parsers support:
-- Parsing document content of different formats (such as text, PDF, Markdown, etc.)
-- Automatically selecting the appropriate parser based on the file extension (e.g., ExtParser)
-- Adding metadata information to the parsed documents
+- Parsing various formats (text, PDF, Markdown, etc.)
+- Automatically selecting a parser by file extension (`ExtParser`)
+- Adding metadata to parsed documents
-## **Interface Definition**
+## Interfaces
-### **Parser Interface**
+### Parser
-> Code Location: eino/components/document/parser/interface.go
+> Code: `eino/components/document/parser/interface.go`
```go
import (
"github.com/cloudwego/eino/schema"
)
-// Parser is a document parser, can be used to parse a document from a reader.
type Parser interface {
Parse(ctx context.Context, reader io.Reader, opts ...Option) ([]*schema.Document, error)
}
```
-#### **Parse Method**
+#### Parse
-- Function: Parses the document content from a Reader
-- Parameters:
- - ctx: Context object
- - reader: Reader providing the raw content
- - opts: Parsing options
-- Return Values:
- - `[]*schema.Document`: List of parsed documents
- - error: Errors encountered during parsing
+- Purpose: parse from a `Reader`
+- Params:
+ - `ctx`: context
+ - `reader`: raw content
+ - `opts`: parsing options
+- Returns:
+ - `[]*schema.Document`: parsed documents
+ - `error`
-### **Common Option Definitions**
+### Common Options
```go
type Options struct {
- // URI indicates the source of the document
+ // URI of the document source
URI string
- // ExtraMeta will be merged into each parsed document's metadata
+ // ExtraMeta merged into each parsed document’s metadata
ExtraMeta map[string]any
}
```
-Two basic option functions are provided:
+Provided helpers:
-- WithURI: Sets the URI of the document, used in ExtParser to select the parser
-- WithExtraMeta: Sets additional metadata
+- `WithURI`: set document URI (used by `ExtParser` to select parser)
+- `WithExtraMeta`: set additional metadata
-## **Built-in Parsers**
+## Built-in Parsers
-### **TextPars****er**
+### TextParser
-The most basic text parser, which directly uses the input content as document content:
+Basic text parser; uses input as content directly.
-> Code Location: eino-examples/components/document/parser/textparser
+> Code: `eino-examples/components/document/parser/textparser`
```go
import "github.com/cloudwego/eino/components/document/parser"
textParser := parser.TextParser{}
docs, _ := textParser.Parse(ctx, strings.NewReader("hello world"))
-
logs.Infof("text content: %v", docs[0].Content)
```
-### **ExtParser**
+### ExtParser
-File extension-based parser, which can automatically choose the appropriate parser based on the file extension:
+Selects parsers by file extension; falls back to a default parser.
-> Code Location: eino-examples/components/document/parser/extparser
+> Code: `eino-examples/components/document/parser/extparser`
```go
package main
@@ -103,33 +101,19 @@ func main() {
textParser := parser.TextParser{}
- htmlParser, _ := html.NewParser(ctx, &html.Config{
- Selector: gptr.Of("body"),
- })
-
+ htmlParser, _ := html.NewParser(ctx, &html.Config{ Selector: gptr.Of("body") })
pdfParser, _ := pdf.NewPDFParser(ctx, &pdf.Config{})
- // Create extension parser
extParser, _ := parser.NewExtParser(ctx, &parser.ExtParserConfig{
- // Register parsers for specific extensions
- Parsers: map[string]parser.Parser{
- ".html": htmlParser,
- ".pdf": pdfParser,
- },
- // Set default parser for handling unknown formats
+ Parsers: map[string]parser.Parser{ ".html": htmlParser, ".pdf": pdfParser },
FallbackParser: textParser,
})
- // Use the parser
filePath := "./testdata/test.html"
file, _ := os.Open(filePath)
-
docs, _ := extParser.Parse(ctx, file,
- // Must provide URI for ExtParser to choose the correct parser
parser.WithURI(filePath),
- parser.WithExtraMeta(map[string]any{
- "source": "local",
- }),
+ parser.WithExtraMeta(map[string]any{ "source": "local" }),
)
for idx, doc := range docs {
@@ -138,18 +122,18 @@ func main() {
}
```
-### **Other Implementations**
+### Other Implementations
-- pdf parser, used for extracting and parsing PDF formatted files: [Parser - pdf](/docs/eino/ecosystem_integration/document/parser_pdf)
-- html parser, used for extracting and parsing HTML formatted content: [Parser - html](/docs/eino/ecosystem_integration/document/parser_html)
+- PDF parser: [Parser — PDF](/docs/eino/ecosystem_integration/document/parser_pdf)
+- HTML parser: [Parser — HTML](/docs/eino/ecosystem_integration/document/parser_html)
-## **Using ****Document Loader**
+## Using Parsers in Document Loader
-The parser is mainly used in the Document Loader to parse the loaded document content. Here are some typical usage scenarios:
+Parsers are primarily used by `Document Loader` to parse loaded content.
-### **File Loader**
+### File Loader Example
-> Code Location: eino-ext/components/document/loader/file/examples/fileloader
+> Code: `eino-ext/components/document/loader/file/examples/fileloader`
```go
import (
@@ -158,66 +142,41 @@ import (
"github.com/cloudwego/eino-ext/components/document/loader/file"
)
-// Use FileLoader to load local files
ctx := context.Background()
-
-log.Printf("===== call File Loader directly =====")
-// Initialize the loader (using file loader as an example)
loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{
- // Configuration parameters
UseNameAsID: true,
- Parser: &parser.TextParser{}, // use TextParser as default parser
+ Parser: &parser.TextParser{}, // Or parser.NewExtParser()
})
-if err != nil {
- log.Fatalf("file.NewFileLoader failed, err=%v", err)
-}
-// Load the document
filePath := "../../testdata/test.md"
-docs, err := loader.Load(ctx, document.Source{
- URI: filePath,
-})
-if err != nil {
- log.Fatalf("loader.Load failed, err=%v", err)
-}
-
+docs, err := loader.Load(ctx, document.Source{ URI: filePath })
log.Printf("doc content: %v", docs[0].Content)
-log.Printf("Extension: %s\n", docs[0].MetaData[file.MetaKeyExtension]) // Output: Extension: .txt
-log.Printf("Source: %s\n", docs[0].MetaData[file.MetaKeySource]) // Output: Source: ./document.txt
+log.Printf("Extension: %s\n", docs[0].MetaData[file._MetaKeyExtension_])
+log.Printf("Source: %s\n", docs[0].MetaData[file._MetaKeySource_])
```
-## **Custom Parser Implementation**
-
-### **option Mechanism**
+## Custom Parser Implementation
-Custom parsers can define their own options:
+### Options
```go
-// options
-// Customize the option structure independently
type options struct {
Encoding string
MaxSize int64
}
-// WithEncoding
-// Customize the Option method independently
func WithEncoding(encoding string) parser.Option {
- return parser.WrapImplSpecificOptFn(func(o *options) {
- o.Encoding = encoding
- })
+ return parser.WrapImplSpecificOptFn(func(o *options) { o.Encoding = encoding })
}
func WithMaxSize(size int64) parser.Option {
- return parser.WrapImplSpecificOptFn(func(o *options) {
- o.MaxSize = size
- })
+ return parser.WrapImplSpecificOptFn(func(o *options) { o.MaxSize = size })
}
```
-### **Complete Implementation Example**
+### Example
-> Code Location: eino-examples/components/document/parser/customparser/custom_parser.go
+> Code: `eino-examples/components/document/parser/customparser/custom_parser.go`
```go
import (
@@ -243,18 +202,15 @@ func NewCustomParser(config *Config) (*CustomParser, error) {
}
func (p *CustomParser) Parse(ctx context.Context, reader io.Reader, opts ...parser.Option) ([]*schema.Document, error) {
- // 1. Handle common options
commonOpts := parser.GetCommonOptions(&parser.Options{}, opts...)
_ = commonOpts
- // 2. Handle specific options
myOpts := &options{
Encoding: p.defaultEncoding,
MaxSize: p.defaultMaxSize,
}
myOpts = parser.GetImplSpecificOptions(myOpts, opts...)
_ = myOpts
- // 3. Implement parsing logic
return []*schema.Document{{
Content: "Hello World",
@@ -262,7 +218,7 @@ func (p *CustomParser) Parse(ctx context.Context, reader io.Reader, opts ...pars
}
```
-### **Notes**
+### Notes
-1. Pay attention to handling abstract common options
-2. Pay attention to the setting and passing of metadata
+1. Handle common options consistently via the shared abstraction
+2. Set and propagate metadata appropriately
diff --git a/content/en/docs/eino/core_modules/components/document_transformer_guide.md b/content/en/docs/eino/core_modules/components/document_transformer_guide.md
index 903eaf367ff..fd9a61d7a68 100644
--- a/content/en/docs/eino/core_modules/components/document_transformer_guide.md
+++ b/content/en/docs/eino/core_modules/components/document_transformer_guide.md
@@ -1,26 +1,26 @@
---
Description: ""
-date: "2025-05-07"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Document Transformer guide'
-weight: 0
+title: 'Eino: Document Transformer Guide'
+weight: 9
---
-## **Introduction**
+## **Overview**
-Document Transformer is a component used for document conversion and processing. Its main function is to perform various transformation operations on input documents, such as splitting, filtering, merging, etc., to obtain documents that meet specific needs. This component can be used in the following scenarios:
+Document Transformer is a component for transforming and processing documents. It performs operations such as splitting, filtering, merging, and more to produce documents tailored to specific needs. Typical scenarios include:
-- Splitting long documents into smaller paragraphs for easier processing
-- Filtering document content based on specific rules
-- Performing structured transformations on document content
-- Extracting specific parts from documents
+- Split long documents into smaller chunks for processing
+- Filter document content by rules
+- Convert document structure
+- Extract specific parts of a document
## **Component Definition**
-### **Interface Definition**
+### **Interface**
-> Code Location: eino/components/document/interface.go
+> Code: `eino/components/document/interface.go`
```go
type Transformer interface {
@@ -30,48 +30,48 @@ type Transformer interface {
#### **Transform Method**
-- Function: Performs transformation processing on the input documents
-- Parameters:
- - ctx: Context object used to pass request-level information, and for passing the Callback Manager
- - src: List of documents to be processed
- - opts: Optional parameters to configure transformation behavior
-- Return Values:
- - `[]*schema.Document`: List of transformed documents
- - error: Error information encountered during the transformation process
+- Purpose: transform input documents
+- Params:
+ - `ctx`: request context, also carries the Callback Manager
+ - `src`: documents to process
+ - `opts`: options to configure behavior
+- Returns:
+ - `[]*schema.Document`: transformed documents
+ - `error`: error during transformation
-### **Document Structure**
+### **Document Struct**
```go
type Document struct {
- // ID is the unique identifier of the document
- ID string
- // Content is the content of the document
+ // ID is the unique identifier
+ ID string
+ // Content is the document text
Content string
- // MetaData is used to store metadata information of the document
+ // MetaData stores metadata
MetaData map[string]any
}
```
-The Document structure is the standard format of the document and includes the following important fields:
+Key fields:
-- ID: The unique identifier of the document, used to uniquely identify a document in the system
-- Content: The actual content of the document
-- MetaData: Metadata of the document, which can store information like:
- - The source information of the document
- - Vector representation of the document (for vector retrieval)
- - Score of the document (for sorting)
- - Sub-index of the document (for hierarchical retrieval)
- - Other custom metadata
+- ID: unique identifier
+- Content: actual text
+- MetaData: metadata such as:
+ - source info
+ - vector representation (for retrieval)
+ - score (for ranking)
+ - sub-index (for hierarchical retrieval)
+ - other custom metadata
-### **Common Option**
+### **Common Options**
-The Transformer component uses TransformerOption to define optional parameters, and currently, there are no common options. Each specific implementation can define its own specific Option, which can be wrapped into a unified TransformerOption type via the WrapTransformerImplSpecificOptFn function.
+Transformer uses `TransformerOption` for optional parameters. There are currently no global/common options; each implementation defines its own specific options, wrapped via `WrapTransformerImplSpecificOptFn` into `TransformerOption`.
## **Usage**
-### **Use Individually**
+### **Standalone**
-> Code Location: eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter
+> Code: `eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter`
```go
import (
@@ -79,9 +79,8 @@ import (
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
)
-// Initialize the transformer (using markdown as an example)
+// init transformer (markdown example)
transformer, _ := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{
- // Configuration parameters
Headers: map[string]string{
"##": "",
},
@@ -90,7 +89,7 @@ transformer, _ := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{
markdownDoc := &schema.Document{
Content: "## Title 1\nHello Word\n## Title 2\nWord Hello",
}
-// Transform the document
+// transform
transformedDocs, _ := transformer.Transform(ctx, []*schema.Document{markdownDoc})
for idx, doc := range transformedDocs {
@@ -98,23 +97,23 @@ for idx, doc := range transformedDocs {
}
```
-### **Use in Orchestration**
+### **In Orchestration**
```go
-// Use in a Chain
+// in Chain
chain := compose.NewChain[[]*schema.Document, []*schema.Document]()
chain.AppendDocumentTransformer(transformer)
-// Use in a Graph
+// in Graph
graph := compose.NewGraph[[]*schema.Document, []*schema.Document]()
graph.AddDocumentTransformerNode("transformer_node", transformer)
```
-## **Usage of Option and Callback**
+## **Options and Callbacks**
-### **Callback Usage Example**
+### **Callback Example**
-> Code location: eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter
+> Code: `eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter`
```go
import (
@@ -127,7 +126,6 @@ import (
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
)
-// Create callback handler
handler := &callbacksHelper.TransformerCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *document.TransformerCallbackInput) context.Context {
log.Printf("input access, len: %v, content: %s\n", len(input.Input), input.Input[0].Content)
@@ -137,10 +135,8 @@ handler := &callbacksHelper.TransformerCallbackHandler{
log.Printf("output finished, len: %v\n", len(output.Output))
return ctx
},
- // OnError
}
-// Use callback handler
helper := callbacksHelper.NewHandlerHelper().
Transformer(handler).
Handler()
@@ -148,7 +144,6 @@ helper := callbacksHelper.NewHandlerHelper().
chain := compose.NewChain[[]*schema.Document, []*schema.Document]()
chain.AppendDocumentTransformer(transformer)
-// Use at runtime
run, _ := chain.Compile(ctx)
outDocs, _ := run.Invoke(ctx, []*schema.Document{markdownDoc}, compose.WithCallbacks(helper))
@@ -160,30 +155,26 @@ for idx, doc := range outDocs {
## **Existing Implementations**
-1. Markdown Header Splitter: Document splitting based on Markdown headers [Splitter - markdown](/docs/eino/ecosystem_integration/document/splitter_markdown)
-2. Text Splitter: Document splitting based on text length or delimiters [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
-3. Document Filter: Filtering document content based on rules [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive)
+1. Markdown Header Splitter: split by markdown headers — [Splitter - markdown](/docs/eino/ecosystem_integration/document/splitter_markdown)
+2. Text Splitter: split by length or separators — [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
+3. Document Filter: filter by rules — [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive)
-## **Reference Implementation**
+## **Implement Your Own**
-When implementing a custom Transformer component, please pay attention to the following points:
+Consider the following when implementing a custom Transformer:
-1. Handling of options
-2. Handling of callbacks
+1. Option handling
+2. Callback handling
### **Option Mechanism**
-A custom Transformer needs to implement its own option mechanism:
-
```go
-// Define the Option struct
type MyTransformerOptions struct {
ChunkSize int
Overlap int
MinChunkLength int
}
-// Define the Option functions
func WithChunkSize(size int) document.TransformerOption {
return document.WrapTransformerImplSpecificOptFn(func(o *MyTransformerOptions) {
o.ChunkSize = size
@@ -197,12 +188,9 @@ func WithOverlap(overlap int) document.TransformerOption {
}
```
-### **Handling Callbacks**
-
-The Transformer implementation needs to trigger callbacks at appropriate times:
+### **Callback Handling**
```go
-// These are the callback input and output defined by the transformer. Custom components need to comply with the structure definitions when implementing.
type TransformerCallbackInput struct {
Input []*schema.Document
Extra map[string]any
@@ -214,7 +202,7 @@ type TransformerCallbackOutput struct {
}
```
-### **Complete Implementation Example**
+### **Full Implementation Example**
```go
type MyTransformer struct {
@@ -232,7 +220,7 @@ func NewMyTransformer(config *MyTransformerConfig) (*MyTransformer, error) {
}
func (t *MyTransformer) Transform(ctx context.Context, src []*schema.Document, opts ...document.TransformerOption) ([]*schema.Document, error) {
- // 1. Handle options
+ // 1. handle Option
options := &MyTransformerOptions{
ChunkSize: t.chunkSize,
Overlap: t.overlap,
@@ -240,15 +228,15 @@ func (t *MyTransformer) Transform(ctx context.Context, src []*schema.Document, o
}
options = document.GetTransformerImplSpecificOptions(options, opts...)
- // 2. Trigger the pre-transformation callback
+ // 2. before-transform callback
ctx = callbacks.OnStart(ctx, info, &document.TransformerCallbackInput{
Input: src,
})
- // 3. Execute the transformation logic
+ // 3. perform transform
docs, err := t.doTransform(ctx, src, options)
- // 5. Handle errors and trigger the completion callback
+ // 4. handle error and finish callback
if err != nil {
ctx = callbacks.OnError(ctx, info, err)
return nil, err
@@ -260,20 +248,22 @@ func (t *MyTransformer) Transform(ctx context.Context, src []*schema.Document, o
return docs, nil
}
+```
+```go
func (t *MyTransformer) doTransform(ctx context.Context, src []*schema.Document, opts *MyTransformerOptions) ([]*schema.Document, error) {
- // Implement the document transformation logic
+ // implement document transform logic
return docs, nil
}
```
### **Notes**
-- It's important to manage the metadata of transformed documents carefully, ensuring that original metadata is retained and custom metadata is properly added.
+- Preserve and manage metadata when transforming documents; keep original metadata and add custom metadata as needed.
-## **Other Reference Documents**
+## **References**
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide)
-- [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide)
-- [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
+- [Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide)
+- [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide)
+- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
diff --git a/content/en/docs/eino/core_modules/components/embedding_guide.md b/content/en/docs/eino/core_modules/components/embedding_guide.md
index 027c19cc2f4..1a921998341 100644
--- a/content/en/docs/eino/core_modules/components/embedding_guide.md
+++ b/content/en/docs/eino/core_modules/components/embedding_guide.md
@@ -1,23 +1,23 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Embedding guide'
-weight: 0
+title: 'Eino: Embedding Guide'
+weight: 7
---
-## **Basic Introduction**
+## Introduction
-The Embedding component is used to convert text into vector representations. Its main function is to map text content into a high-dimensional vector space, so that semantically similar texts are closer in the vector space. This component plays an important role in the following scenarios:
+The `Embedding` component converts text into vector representations, mapping content into a vector space where semantically similar texts are closer. It’s useful for:
-- Text similarity calculation
+- Text similarity
- Semantic search
-- Text clustering analysis
+- Clustering analysis
-## **Component Definition**
+## Component Definition
-### **Interface Definition**
+### Interface
```go
type Embedder interface {
@@ -25,40 +25,38 @@ type Embedder interface {
}
```
-#### **EmbedStrings Method**
+#### EmbedStrings
-- Function: Convert a set of texts into vector representations
-- Parameters:
- - ctx: Context object, used to pass request-level information, and also for passing Callback Manager
- - texts: List of texts to be converted
- - opts: Conversion options, used to configure the conversion behavior
-- Return values:
- - `[][]float64`: List of vector representations corresponding to the texts, the dimension of each vector is determined by the specific implementation
- - error: Error information during the conversion process
+- Purpose: convert a list of texts into vectors
+- Params:
+ - `ctx`: request context and callback manager
+ - `texts`: list of texts
+ - `opts`: embedding options
+- Returns:
+ - `[][]float64`: vectors (dimensions depend on implementation)
+ - `error`
-### **Common Option**
+### Common Options
-The Embedding component uses EmbeddingOption to define optional parameters. Below are the abstract common options. Each specific implementation can define its specific options, which can be wrapped into a unified EmbeddingOption type through the WrapEmbeddingImplSpecificOptFn function.
+Embedding uses `EmbeddingOption`. Implementations may define specific options and wrap via `WrapEmbeddingImplSpecificOptFn`.
```go
type Options struct {
- // Model is the name of the model used to generate vectors
Model *string
}
```
-Options can be set as follows:
+Set options:
```go
-// Set model name
WithModel(model string) Option
```
-## **Usage**
+## Usage
-### **Standalone Usage**
+### Standalone
-> Code location: eino-ext/components/embedding/openai/examples/embedding
+> Code: `eino-ext/components/embedding/openai/examples/embedding`
```go
import "github.com/cloudwego/eino-ext/components/embedding/openai"
@@ -73,34 +71,33 @@ embedder, _ := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{
vectorIDs, _ := embedder.EmbedStrings(ctx, []string{"hello", "how are you"})
```
-### **Usage in Orchestration**
+### In Orchestration
-> Code location: eino-ext/components/embedding/openai/examples/embedding
+> Code: `eino-ext/components/embedding/openai/examples/embedding`
```go
-// Use in Chain
+// Chain
chain := compose.NewChain[[]string, [][]float64]()
chain.AppendEmbedding(embedder)
-// Use in Graph
+// Graph
graph := compose.NewGraph[[]string, [][]float64]()
graph.AddEmbeddingNode("embedding_node", embedder)
```
-## **Option and Callback Usage**
+## Options and Callbacks
-### **Option Usage Example**
+### Options Example
```go
-// Use options (example for standalone usage)
vectors, err := embedder.EmbedStrings(ctx, texts,
embedding.WithModel("text-embedding-3-small"),
)
```
-### **Callback Usage Example**
+### Callback Example
-> Code location: eino-ext/components/embedding/openai/examples/embedding
+> Code: `eino-ext/components/embedding/openai/examples/embedding`
```go
import (
@@ -126,86 +123,59 @@ callbackHandler := callbacksHelper.NewHandlerHelper().Embedding(handler).Handler
chain := compose.NewChain[[]string, [][]float64]()
chain.AppendEmbedding(embedder)
-
-// Compile and run
runnable, _ := chain.Compile(ctx)
-vectors, _ = runnable.Invoke(ctx, []string{"hello", "how are you"},
- compose.WithCallbacks(callbackHandler))
-
+vectors, _ = runnable.Invoke(ctx, []string{"hello", "how are you"}, compose.WithCallbacks(callbackHandler))
log.Printf("vectors in chain: %v", vectors)
```
-## **Existing Implementations**
-
-1. OpenAI Embedding: Generate vectors using OpenAI's text embedding model [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai)
-2. ARK Embedding: Generate vectors using the ARK platform's model [Embedding - ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark)
+## Existing Implementations
-## **Custom Implementation Reference**
+1. OpenAI Embedding: [Embedding — OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai)
+2. ARK Embedding: [Embedding — ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark)
-When implementing a custom Embedding component, the following points need to be noted:
+## Implementation Notes
-1. Pay attention to handling common options
-2. Implement the callback mechanism properly
+1. Handle common options
+2. Implement callback mechanisms
-### **Option Mechanism**
-
-Custom Embedding needs to implement its own Option mechanism:
+### Options
```go
-// Define the Option struct
type MyEmbeddingOptions struct {
BatchSize int
MaxRetries int
Timeout time.Duration
}
-// Define the Option function
func WithBatchSize(size int) embedding.Option {
- return embedding.WrapEmbeddingImplSpecificOptFn(func(o *MyEmbeddingOptions) {
- o.BatchSize = size
- })
+ return embedding.WrapEmbeddingImplSpecificOptFn(func(o *MyEmbeddingOptions) { o.BatchSize = size })
}
```
-### **Callback Handling**
-
-The Embedder implementation needs to trigger callbacks at appropriate times. The framework has defined standard callback input and output structs:
+### Callback Structures
```go
-// CallbackInput is the input for the embedding callback
type CallbackInput struct {
- // Texts are the list of texts to be converted
Texts []string
- // Config is the configuration information for generating vectors
Config *Config
- // Extra is additional information for the callback
Extra map[string]any
}
-// CallbackOutput is the output for the embedding callback
type CallbackOutput struct {
- // Embeddings are the list of generated vectors
Embeddings [][]float64
- // Config is the configuration information for generating vectors
Config *Config
- // TokenUsage is the token usage information
TokenUsage *TokenUsage
- // Extra is additional information for the callback
Extra map[string]any
}
-// TokenUsage is the token usage information
type TokenUsage struct {
- // PromptTokens is the number of tokens in the prompt
PromptTokens int
- // CompletionTokens is the number of tokens in the completion
CompletionTokens int
- // TotalTokens is the total number of tokens
TotalTokens int
}
```
-### **Complete Implementation Example**
+### Full Implementation Example
```go
type MyEmbedder struct {
@@ -221,18 +191,18 @@ func NewMyEmbedder(config *MyEmbedderConfig) (*MyEmbedder, error) {
}
func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...embedding.Option) ([][]float64, error) {
- // 1. Handle the options
+ // 1. handle options
options := &MyEmbeddingOptions{
Options: &embedding.Options{},
BatchSize: e.batchSize,
}
options.Options = embedding.GetCommonOptions(options.Options, opts...)
- options = embedding.GetImplSpecificOptions(options, opts...)
+ options = embedding.GetImplSpecificOptions(options.Options, opts...)
- // 2. Get the callback manager
+ // 2. get callback manager
cm := callbacks.ManagerFromContext(ctx)
- // 3. Trigger the pre-generation callback
+ // 3. before-embed callback
ctx = cm.OnStart(ctx, info, &embedding.CallbackInput{
Texts: texts,
Config: &embedding.Config{
@@ -240,10 +210,10 @@ func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...e
},
})
- // 4. Execute the vector generation logic
+ // 4. perform embedding
vectors, tokenUsage, err := e.doEmbed(ctx, texts, options)
- // 5. Handle errors and trigger the completion callback
+ // 5. handle error and finish callback
if err != nil {
ctx = cm.OnError(ctx, info, err)
return nil, err
@@ -261,13 +231,13 @@ func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...e
}
func (e *MyEmbedder) doEmbed(ctx context.Context, texts []string, opts *MyEmbeddingOptions) ([][]float64, *TokenUsage, error) {
- // Implementation logic
+ // implement logic
return vectors, tokenUsage, nil
}
```
-## **Other Reference Documents**
+## References
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide)
-- [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
+- [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide)
+- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
diff --git a/content/en/docs/eino/core_modules/components/indexer_guide.md b/content/en/docs/eino/core_modules/components/indexer_guide.md
index 83e5044496a..f4cf86dec5c 100644
--- a/content/en/docs/eino/core_modules/components/indexer_guide.md
+++ b/content/en/docs/eino/core_modules/components/indexer_guide.md
@@ -1,23 +1,21 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Indexer guide'
-weight: 0
+title: 'Eino: Indexer Guide'
+weight: 6
---
-## **Basic Introduction**
+## Introduction
-The Indexer component is used for storing and indexing documents. Its primary function is to store documents and their vector representations into a backend storage system and provide efficient retrieval capabilities. This component plays a significant role in the following scenarios:
+The `Indexer` component stores documents (and vectors) into backend systems and provides efficient retrieval. It’s useful for building vector databases for semantic search.
-- Building a vector database for semantic association search
+## Component Definition
-## **Component Definition**
+### Interface
-### **Interface Definition**
-
-> Code Location: eino/components/indexer/interface.go
+> Code: `eino/components/indexer/interface.go`
```go
type Indexer interface {
@@ -25,42 +23,38 @@ type Indexer interface {
}
```
-#### **Store Method**
+#### Store
-- Function: Store documents and build an index
-- Parameters:
- - ctx: Context object used to pass request-level information and the Callback Manager
- - docs: List of documents to be stored
- - opts: Storage options used to configure storage behavior
+- Purpose: store documents and build indexes
+- Params:
+ - `ctx`: context and callback manager
+ - `docs`: documents to store
+ - `opts`: options for storage
- Returns:
- - ids: List of successfully stored document IDs
- - error: Error information during the storage process
+ - `ids`: stored document IDs
+ - `error`
-### **Common Options**
+### Common Options
-The Indexer component uses IndexerOption to define optional parameters. Indexer defines the following common options. Additionally, each specific implementation can define its specific options, wrapped as a unified IndexerOption type through the WrapIndexerImplSpecificOptFn function.
+`IndexerOption` defines options. Implementations may add specific options via `WrapIndexerImplSpecificOptFn`.
```go
type Options struct {
- // SubIndexes is the list of sub-indexes to be created
- SubIndexes []string
- // Embedding is the component used to generate document vectors
+ SubIndexes []string
Embedding embedding.Embedder
}
```
-Options can be set in the following ways:
+Set options:
```go
-// Set sub-indexes
WithSubIndexes(subIndexes []string) Option
-// Set vector generation component
WithEmbedding(emb embedding.Embedder) Option
```
-## **Usage**
+## Usage
-### **Standalone Use**
+### Standalone
```go
import (
@@ -71,24 +65,24 @@ import (
collectionName := "eino_test"
/*
- * In the following example, a dataset named eino_test is prebuilt with the field configuration as:
- * Field Name Field Type Vector Dimension
+ * In the following example, a dataset (collection) named "eino_test" is pre-created with fields:
+ * Field Name Field Type Vector Dim
* ID string
- * vector vector 1024
+ * vector vector 1024
* sparse_vector sparse_vector
* content string
* extra_field_1 string
*
- * When using the component, note:
- * 1. The field names and types for ID / vector / sparse_vector / content should be consistent with the above configuration
- * 2. The vector dimension must match the vector dimension output by the model corresponding to ModelName
- * 3. Some models do not output sparse vectors. In this case, UseSparse needs to be set to false, and the collection can omit the sparse_vector field
+ * Component usage notes:
+ * 1. Field names and types for ID / vector / sparse_vector / content must match the above configuration
+ * 2. The vector dimension must match the output dimension of the model indicated by ModelName
+ * 3. Some models do not output sparse vectors; set UseSparse=false and the collection may omit sparse_vector
*/
cfg := &volc_vikingdb.IndexerConfig{
// https://api-vikingdb.volces.com (North China)
// https://api-vikingdb.mlp.cn-shanghai.volces.com (East China)
- // https://api-vikingdb.mlp.ap-mya.byteplus.com (Overseas-Johor)
+ // https://api-vikingdb.mlp.ap-mya.byteplus.com (Overseas - Johor)
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
AK: ak,
@@ -106,62 +100,48 @@ cfg := &volc_vikingdb.IndexerConfig{
volcIndexer, _ := volc_vikingdb.NewIndexer(ctx, cfg)
-doc := &schema.Document{
- ID: "mock_id_1",
- Content: "A ReAct prompt consists of few-shot task-solving trajectories, with human-written text reasoning traces and actions, as well as environment observations in response to actions",
-}
+doc := &schema.Document{ ID: "mock_id_1", Content: "A ReAct prompt consists of..." }
volc_vikingdb.SetExtraDataFields(doc, map[string]interface{}{"extra_field_1": "mock_ext_abc"})
volc_vikingdb.SetExtraDataTTL(doc, 1000)
docs := []*schema.Document{doc}
resp, _ := volcIndexer.Store(ctx, docs)
-
fmt.Printf("vikingDB store success, docs=%v, resp ids=%v\n", docs, resp)
```
-### **Use in Orchestration**
+### In Orchestration
```go
-// Use in Chain
+// Chain
chain := compose.NewChain[[]*schema.Document, []string]()
chain.AppendIndexer(indexer)
-// Use in Graph
+// Graph
graph := compose.NewGraph[[]*schema.Document, []string]()
graph.AddIndexerNode("indexer_node", indexer)
```
-## **Option and Callback Usage**
+## Options and Callbacks
-### **Option Usage Example**
+### Options Example
```go
-// Using options (when used individually)
ids, err := indexer.Store(ctx, docs,
- // Set subindex
indexer.WithSubIndexes([]string{"kb_1", "kb_2"}),
- // Set embedding component
indexer.WithEmbedding(embedder),
)
```
-### **Callback Usage Example**
+### Callback Example
-> Code location: eino-ext/components/indexer/volc_vikingdb/examples/builtin_embedding
+> Code: `eino-ext/components/indexer/volc_vikingdb/examples/builtin_embedding`
```go
import (
- "context"
- "fmt"
- "log"
- "os"
-
"github.com/cloudwego/eino/callbacks"
"github.com/cloudwego/eino/components/indexer"
"github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
callbacksHelper "github.com/cloudwego/eino/utils/callbacks"
-
"github.com/cloudwego/eino-ext/components/indexer/volc_vikingdb"
)
@@ -177,75 +157,47 @@ handler := &callbacksHelper.IndexerCallbackHandler{
// OnError
}
-// Using callback handler
-helper := callbacksHelper.NewHandlerHelper().
- Indexer(handler).
- Handler()
+helper := callbacksHelper.NewHandlerHelper().Indexer(handler).Handler()
chain := compose.NewChain[[]*schema.Document, []string]()
chain.AppendIndexer(volcIndexer)
-
-// At runtime
run, _ := chain.Compile(ctx)
-
outIDs, _ := run.Invoke(ctx, docs, compose.WithCallbacks(helper))
-
-fmt.Printf("vikingDB store success, docs=%v, resp ids=%v\n", docs, outIDs)
```
-## **Existing Implementation**
-
-1. Volc VikingDB Indexer: Vector database indexer based on Volcano Engine VikingDB [Indexer - volc VikingDB](/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb)
+## Existing Implementations
-## **Custom Implementation Reference**
+1. Volc VikingDB Indexer: [Indexer — VikingDB](/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb)
-When implementing a custom Indexer component, please pay attention to the following points:
+## Implementation Notes
-1. Proper handling of common options and component-specific options
-2. Proper handling of callbacks
+1. Handle common options and implementation-specific options.
+2. Implement callbacks correctly.
-### **Option Mechanism**
-
-Custom Indexers can implement their own Options as needed:
+### Options
```go
-// Define Option struct
-type MyIndexerOptions struct {
- BatchSize int
- MaxRetries int
-}
-
-// Define Option function
+type MyIndexerOptions struct { BatchSize int; MaxRetries int }
func WithBatchSize(size int) indexer.Option {
- return indexer.WrapIndexerImplSpecificOptFn(func(o *MyIndexerOptions) {
- o.BatchSize = size
- })
+ return indexer.WrapIndexerImplSpecificOptFn(func(o *MyIndexerOptions) { o.BatchSize = size })
}
```
-### **Callback Handling**
-
-The Indexer implementation needs to trigger callbacks at appropriate times. The framework has already defined standard callback input and output structs:
+### Callback Structures
```go
-// CallbackInput is the input for the indexer callback
type CallbackInput struct {
- // Docs is the list of documents to be indexed
Docs []*schema.Document
- // Extra is the additional information for the callback
Extra map[string]any
}
-// CallbackOutput is the output for the indexer callback
type CallbackOutput struct {
- // IDs is the list of document IDs returned by the indexer
IDs []string
- // Extra is the additional information for the callback
Extra map[string]any
}
```
-### **Complete Implementation Example**
+### Full Implementation Example
```go
type MyIndexer struct {
@@ -261,23 +213,23 @@ func NewMyIndexer(config *MyIndexerConfig) (*MyIndexer, error) {
}
func (i *MyIndexer) Store(ctx context.Context, docs []*schema.Document, opts ...indexer.Option) ([]string, error) {
- // 1. Handle options
+ // 1. handle options
options := &indexer.Options{}
options = indexer.GetCommonOptions(options, opts...)
- // 2. Get callback manager
+ // 2. get callback manager
cm := callbacks.ManagerFromContext(ctx)
- // 3. Callback before starting storage
+ // 3. before-store callback
ctx = cm.OnStart(ctx, info, &indexer.CallbackInput{
Docs: docs,
})
- // 4. Execute storage logic
+ // 4. perform storage
ids, err := i.doStore(ctx, docs, options)
- // 5. Handle errors and complete the callback
- if (err != nil) {
+ // 5. handle error and finish callback
+ if err != nil {
ctx = cm.OnError(ctx, info, err)
return nil, err
}
@@ -290,26 +242,23 @@ func (i *MyIndexer) Store(ctx context.Context, docs []*schema.Document, opts ...
}
func (i *MyIndexer) doStore(ctx context.Context, docs []*schema.Document, opts *indexer.Options) ([]string, error) {
- // Implement document storage logic (make sure to handle common option parameters)
- // 1. If an Embedding component is set, generate vector representations for the documents
+ // implement storage logic (handle common options)
+ // 1. If Embedding is set, generate vectors for documents
if opts.Embedding != nil {
- // Extract document content
texts := make([]string, len(docs))
for j, doc := range docs {
texts[j] = doc.Content
}
- // Generate vectors
vectors, err := opts.Embedding.EmbedStrings(ctx, texts)
if err != nil {
return nil, err
}
- // Store vectors in the documents' Metadata
- for j, doc := range docs {
- doc.WithVector(vectors[j])
+ for j := range docs {
+ docs[j].WithVector(vectors[j])
}
}
- // 2. Additional custom logic
+ // 2. other custom logic
return ids, nil
}
```
diff --git a/content/en/docs/eino/core_modules/components/lambda_guide.md b/content/en/docs/eino/core_modules/components/lambda_guide.md
index 547e5a8cccb..da5dd9fb077 100644
--- a/content/en/docs/eino/core_modules/components/lambda_guide.md
+++ b/content/en/docs/eino/core_modules/components/lambda_guide.md
@@ -1,145 +1,124 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: 'Eino: Lambda guide'
-weight: 0
+title: 'Eino: Lambda Guide'
+weight: 5
---
-## **Introduction**
+## Introduction
-Lambda is the most fundamental component type in Eino, allowing users to embed custom function logic in the workflow. The underlying Lambda component consists of 4 types of running functions, corresponding to 4 interaction modes: Invoke, Stream, Collect, and Transform.
+`Lambda` is the simplest component type, allowing you to embed custom function logic in a workflow. Lambdas can implement one or more of the four paradigms formed by streaming/non-streaming input/output: `Invoke`, `Stream`, `Collect`, `Transform`.
-When users build Lambda, they can only implement one of them or more, and the framework will perform conversion according to certain rules. For detailed introduction, please refer to: [Eino: Overview](/docs/eino/overview) (see at Runnable)
+The framework converts among paradigms under defined rules. See [Overview](/docs/eino/overview) for details (Runnable section).
-## **Component Definition and Implementation**
+## Definition and Construction
-The core of the Lambda component is the `Lambda` structure, which wraps the lambda function provided by the user. Users can create a Lambda component through the construction method:
+> Code: `eino/compose/types_lambda.go`
```go
-// definition at: https://github.com/cloudwego/eino/blob/main/compose/types_lambda.go
-type Lambda struct {
- executor *composableRunnable
-}
+type Lambda struct { executor *composableRunnable }
```
-The four function types supported by Lambda are defined as follows. That is, the lambda function provided by the user needs to satisfy these function signatures:
+Lambda function signatures:
```go
type Invoke[I, O, TOption any] func(ctx context.Context, input I, opts ...TOption) (output O, err error)
-
type Stream[I, O, TOption any] func(ctx context.Context, input I, opts ...TOption) (output *schema.StreamReader[O], err error)
-
type Collect[I, O, TOption any] func(ctx context.Context, input *schema.StreamReader[I], opts ...TOption) (output O, err error)
-
type Transform[I, O, TOption any] func(ctx context.Context, input *schema.StreamReader[I], opts ...TOption) (output *schema.StreamReader[O], err error)
```
-## **Usage**
+## Usage
+
+> Examples: [https://github.com/cloudwego/eino-examples/blob/main/components/lambda](https://github.com/cloudwego/eino-examples/blob/main/components/lambda)
-### **Construction method**
+### Constructors
-From the perspective of the unified specification of Eino's component interface, a callable method of a component needs to have 3 parameters and 2 return values: func (ctx, input,... option) (output, error). However, in scenarios where Lambda is used, it is often desirable to provide only a simple function. Therefore, the construction methods are divided into 3 categories:
+Eino components generally accept `func(ctx, input, ...option) (output, error)`. For lambdas, simpler constructors are provided:
-- Functions that provide only one interaction method
- - Without Option
- - Handling custom Option
-- Functions that fully customize each interaction method: AnyLambda
+- Provide exactly one paradigm function
+ - Without custom options
+ - With custom options
+- Provide any subset of the four paradigms via `AnyLambda`
-#### Without custom options
+#### Without Custom Options
-- InvokableLambda
+- `InvokableLambda`
```go
-// The types of input and output types can be any types.
-lambda := compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+// input and output can be any custom types
+lambda := compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
// some logic
})
```
-- StreamableLambda
+- `StreamableLambda`
```go
-// The type of input can be any custom type and the type of output must be *schema.StreamReader[O],O can be any type.
-lambda := compose.StreamableLambda(func(ctx context.Context, input string) (output *schema.StreamReader[string], err error) {
+// input is any type; output must be *schema.StreamReader[O]
+lambda := compose.StreamableLambda(func(ctx context.Context, input string) (*schema.StreamReader[string], error) {
// some logic
})
```
-- CollectableLambda
+- `CollectableLambda`
```go
-// The type of input must be *schema.StreamReader[I],I can be any type and the type of output can be any custom type.
-lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output string, err error) {
+// input must be *schema.StreamReader[I]; output can be any type
+lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (string, error) {
// some logic
})
```
-- TransformableLambda
+- `TransformableLambda`
```go
-// The type of input must be *schema.StreamReader[I],I can be any type and the type of output must be *schema.StreamReader[O],O can be any type.
-lambda := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output *schema.StreamReader[string], err error) {
+// input and output must be *schema.StreamReader[I]
+lambda := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (*schema.StreamReader[string], error) {
// some logic
})
```
-#### **Using custom options**
+Shared options:
+
+- `compose.WithLambdaType()` — change component type (default: Lambda)
+- `compose.WithLambdaCallbackEnable()` — disable default node callbacks in Graph
-Each interaction method corresponds to a construction method. The following takes Invoke as an example:
+#### With Custom Options
```go
-type Options struct {
- Field1 string
-}
+type Options struct { Field1 string }
type MyOption func(*Options)
lambda := compose.InvokableLambdaWithOption(
- func(ctx context.Context, input string, opts ...MyOption) (output string, err error) {
+ func(ctx context.Context, input string, opts ...MyOption) (string, error) {
// handle opts
// some logic
- }
+ },
)
```
-#### **AnyLambda**
+#### AnyLambda
-AnyLambda allows for the implementation of multiple types of Lambda functions with different interaction patterns simultaneously.
+Implement multiple paradigms at once:
```go
-type Options struct {
- Field1 string
-}
-
+type Options struct { Field1 string }
type MyOption func(*Options)
-// The types of 'input' and 'output' are any custom types.
lambda, err := compose.AnyLambda(
- // Invoke Function
- func(ctx context.Context, input string, opts ...MyOption) (output string, err error) {
- // some logic
- },
- // Stream Function
- func(ctx context.Context, input string, opts ...MyOption) (output *schema.StreamReader[string], err error) {
- // some logic
- },
- // Collect Function
- func(ctx context.Context, input *schema.StreamReader[string], opts ...MyOption) (output string, err error) {
- // some logic
- },
- // Transform Function
- func(ctx context.Context, input *schema.StreamReader[string], opts ...MyOption) (output *schema.StreamReader[string], err error) {
- // some logic
- },
+ func(ctx context.Context, input string, opts ...MyOption) (string, error) { /* ... */ },
+ func(ctx context.Context, input string, opts ...MyOption) (*schema.StreamReader[string], error) { /* ... */ },
+ func(ctx context.Context, input *schema.StreamReader[string], opts ...MyOption) (string, error) { /* ... */ },
+ func(ctx context.Context, input *schema.StreamReader[string], opts ...MyOption) (*schema.StreamReader[string], error) { /* ... */ },
)
```
-### **Used in orchestration**
-
-#### In Graph
+### In Orchestration
-In Graph, a Lambda node can be added through AddLambdaNode:
+#### Graph
```go
graph := compose.NewGraph[string, *MyStruct]()
@@ -151,9 +130,7 @@ graph.AddLambdaNode(
)
```
-#### In Chain
-
-In Chain, Lambda nodes can be added through AppendLambda:
+#### Chain
```go
chain := compose.NewChain[string, string]()
@@ -162,46 +139,43 @@ chain.AppendLambda(compose.InvokableLambda(func(ctx context.Context, input strin
}))
```
-### Two built-in Lambda nodes
+### Built-in Lambdas
#### ToList
-`ToList` is a built-in lambda that is used to convert a single input into a list:
+Convert a single element into a one-item slice:
```go
-// Create a ToList Lambda
lambda := compose.ToList[*schema.Message]()
-
-// add to Chain
chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
-chain.AppendChatModel(chatModel) // chatModel returns *schema.Message
-chain.AppendLambda(lambda) // convert *schema.Message to []*schema.Message
+chain.AppendChatModel(chatModel)
+chain.AppendLambda(lambda)
```
#### MessageParser
-MessageParser is a built-in Lambda used to parse JSON messages into the specified structure (mostly generate by LLM).
+Parse a JSON message (often from an LLM) into a struct:
```go
-// Define the parsing target structure body
+// define target struct
type MyStruct struct {
ID int `json:"id"`
}
-// Create a parser
+// create parser
parser := schema.NewMessageJSONParser[*MyStruct](&schema.MessageJSONParseConfig{
ParseFrom: schema.MessageParseFromContent,
- ParseKeyPath: "", // If you only need to parse the sub-field, you can use "key.sub.grandsub"
+ ParseKeyPath: "", // use "key.sub.grandsub" to parse subfields
})
-// Create a parsing Lambda
+// create parser lambda
parserLambda := compose.MessageParser(parser)
-// Used in Chain
+// use in Chain
chain := compose.NewChain[*schema.Message, *MyStruct]()
chain.AppendLambda(parserLambda)
-// Invoke example
+// example
runner, err := chain.Compile(context.Background())
parsed, err := runner.Invoke(context.Background(), &schema.Message{
Content: `{"id": 1}`,
@@ -209,10 +183,10 @@ parsed, err := runner.Invoke(context.Background(), &schema.Message{
// parsed.ID == 1
```
-MessageParser supports parsing data from the message content (Content) or the result of tool invocation (ToolCall), which is commonly used in scenarios such as intent recognition:
+Parsing from tool call results is also supported:
```go
-// Parsing from the result of the tool invocation
+// parse from tool call results
parser := schema.NewMessageJSONParser[*MyStruct](&schema.MessageJSONParseConfig{
ParseFrom: schema.MessageParseFromToolCall,
})
diff --git a/content/en/docs/eino/core_modules/components/retriever_guide.md b/content/en/docs/eino/core_modules/components/retriever_guide.md
index a2c8918a2ce..f4c1167b8cf 100644
--- a/content/en/docs/eino/core_modules/components/retriever_guide.md
+++ b/content/en/docs/eino/core_modules/components/retriever_guide.md
@@ -1,25 +1,25 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: 'Eino: Retriever guide'
-weight: 0
+title: 'Eino: Retriever Guide'
+weight: 4
---
-## **Basic Introduction**
+## Introduction
-The Retriever component is used to retrieve documents from various data sources. Its main function is to retrieve the most relevant documents from a document library based on a user's query. This component is particularly useful in the following scenarios:
+The `Retriever` component fetches relevant documents based on a query from underlying indexes or stores. It’s useful for:
-- Document retrieval based on vector similarity
-- Document search based on keywords
-- Knowledge base Q&A system (rag)
+- Vector-similarity retrieval
+- Keyword-based search
+- Knowledge-base QA (RAG)
-## **Component Definition**
+## Component Definition
-### **Interface Definition**
+### Interface
-> Code Location: eino/components/retriever/interface.go
+> Code: `eino/components/retriever/interface.go`
```go
type Retriever interface {
@@ -27,81 +27,58 @@ type Retriever interface {
}
```
-#### **Retrieve Method**
+#### Retrieve
-- Function: Retrieve relevant documents based on the query
-- Parameters:
- - ctx: Context object used to transfer request-level information and to pass the Callback Manager
- - query: Query string
- - opts: Retrieval options used to configure retrieval behavior
-- Return Values:
- - `[]*schema.Document`: List of retrieved documents
- - error: Error information during the retrieval process
+- Purpose: retrieve documents for a query
+- Params:
+ - `ctx`: context and callback manager
+ - `query`: the query string
+ - `opts`: retriever options
+- Returns:
+ - `[]*schema.Document`: matching documents
+ - `error`
-### **Document Struct**
+### Document
```go
type Document struct {
- // ID is the unique identifier of the document
ID string
- // Content is the content of the document
Content string
- // MetaData is used to store metadata information of the document
MetaData map[string]any
}
```
-### **Common Options**
+### Common Options
-The Retriever component uses RetrieverOption to define optional parameters. The following are the common options that the Retriever component needs to implement. Additionally, each specific implementation can define its own specific options, which can be wrapped into a unified RetrieverOption type through the WrapRetrieverImplSpecificOptFn function.
+Implementations should handle these common options, plus any impl-specific ones via `WrapRetrieverImplSpecificOptFn`:
```go
type Options struct {
- // Index is the index used by the retriever. The meaning of the index may vary across different retrievers.
Index *string
-
- // SubIndex is the sub-index used by the retriever. The meaning of the sub-index may vary across different retrievers.
SubIndex *string
-
- // TopK is the upper limit on the number of documents retrieved
TopK *int
-
- // ScoreThreshold is the threshold for document similarity, e.g., 0.5 means the document's similarity score must be greater than 0.5
ScoreThreshold *float64
-
- // Embedding is the component used for generating query vectors
Embedding embedding.Embedder
-
- // DSLInfo is the DSL information used for retrieval, only used in Viking-type retrievers
DSLInfo map[string]interface{}
}
```
-The options can be set as follows:
+Helpers:
```go
-// Set the index
WithIndex(index string) Option
-
-// Set the sub-index
WithSubIndex(subIndex string) Option
-
-// Set the upper limit on the number of retrieved documents
WithTopK(topK int) Option
-
-// Set the similarity threshold
WithScoreThreshold(threshold float64) Option
-
-// Set the vector generation component
WithEmbedding(emb embedding.Embedder) Option
-
-// Set DSL information (only for Viking-type retrievers)
WithDSLInfo(dsl map[string]any) Option
-```## **Usage**
+```
+
+## Usage
-### **Standalone Usage**
+### Standalone
-> Code location: eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding
+> Code: `eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding`
```go
import (
@@ -116,19 +93,20 @@ collectionName := "eino_test"
indexName := "test_index_1"
/*
- * In the following example, a dataset called eino_test has been pre-built, and an hnsw-hybrid index called test_index_1 has been built on this dataset.
- * The dataset fields are configured as:
- * Field Name Field Type Vector Dimension
- * ID string
- * vector vector 1024
- * sparse_vector sparse_vector
- * content string
- * extra_field_1 string
+ * In the following example, a dataset (collection) named "eino_test" is pre-created,
+ * and an hnsw-hybrid index named "test_index_1" is built on this dataset.
+ * Dataset field configuration:
+ * Field Name Field Type Vector Dim
+ * ID string
+ * vector vector 1024
+ * sparse_vector sparse_vector
+ * content string
+ * extra_field_1 string
*
- * Note when using the component:
- * 1. The field names and types of ID / vector / sparse_vector / content should be consistent with the above configuration.
- * 2. The vector dimension should be consistent with the vector dimension output by the model corresponding to ModelName.
- * 3. Some models do not output sparse vectors. In this case, set UseSparse to false, and the collection does not need to set the sparse_vector field.
+ * Component usage notes:
+ * 1. The field names and types for ID / vector / sparse_vector / content must match the above configuration
+ * 2. The vector dimension must match the output dimension of the model for ModelName
+ * 3. Some models do not output sparse vectors; in that case set UseSparse=false and the collection may omit sparse_vector
*/
cfg := &volc_vikingdb.RetrieverConfig{
@@ -137,8 +115,8 @@ cfg := &volc_vikingdb.RetrieverConfig{
// https://api-vikingdb.mlp.ap-mya.byteplus.com (Overseas - Johor)
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
- AK: ak,
- SK: sk,
+ AK: ak,
+ SK: sk,
Scheme: "https",
ConnectionTimeout: 0,
Collection: collectionName,
@@ -149,50 +127,61 @@ cfg := &volc_vikingdb.RetrieverConfig{
UseSparse: true,
DenseWeight: 0.4,
},
- Partition: "", // Corresponding to the partition field in the index; when not set, leave it blank
+ Partition: "", // corresponds to the index's sub-index partition field; leave empty if not set
TopK: of(10),
ScoreThreshold: of(0.1),
- FilterDSL: nil, // Corresponding to the scalar filtering field in the index; when not set, leave it blank; see https://www.volcengine.com/docs/84313/1254609 for the expression
+ FilterDSL: nil, // corresponds to the index's scalar filter field; leave nil if not set; see https://www.volcengine.com/docs/84313/1254609
}
volcRetriever, _ := volc_vikingdb.NewRetriever(ctx, cfg)
-
-
query := "tourist attraction"
docs, _ := volcRetriever.Retrieve(ctx, query)
-
log.Printf("vikingDB retrieve success, query=%v, docs=%v", query, docs)
```
-### **Usage in Composition**
+### In Orchestration
```go
-// Using in Chain
+// Chain
chain := compose.NewChain[string, []*schema.Document]()
chain.AppendRetriever(retriever)
-// Using in Graph
+// Graph
graph := compose.NewGraph[string, []*schema.Document]()
graph.AddRetrieverNode("retriever_node", retriever)
```
-## **Option and Cal****lback Usage**
+## Options and Callbacks
-### **Callback Example**
+### Option Mechanism
-> Code location: eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding
+```go
+// Use GetCommonOptions to handle common options
+func (r *MyRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) ([]*schema.Document, error) {
+ // 1. init and read options
+ options := &retriever.Options{ // set defaults as needed
+ Index: &r.index,
+ TopK: &r.topK,
+ Embedding: r.embedder,
+ }
+ options = retriever.GetCommonOptions(options, opts...)
+
+ // ...
+}
+```
+
+### Callback Example
+
+> Code: `eino-ext/components/retriever/volc_vikingdb/examples/builtin_embedding`
```go
import (
"github.com/cloudwego/eino/callbacks"
"github.com/cloudwego/eino/components/retriever"
"github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
callbacksHelper "github.com/cloudwego/eino/utils/callbacks"
- "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
)
-// Create callback handler
handler := &callbacksHelper.RetrieverCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *retriever.CallbackInput) context.Context {
log.Printf("input access, content: %s\n", input.Query)
@@ -202,62 +191,20 @@ handler := &callbacksHelper.RetrieverCallbackHandler{
log.Printf("output finished, len: %v\n", len(output.Docs))
return ctx
},
- // OnError
}
-// Use callback handler
-helper := callbacksHelper.NewHandlerHelper().
- Retriever(handler).
- Handler()
+helper := callbacksHelper.NewHandlerHelper().Retriever(handler).Handler()
chain := compose.NewChain[string, []*schema.Document]()
chain.AppendRetriever(volcRetriever)
-
-// Use at runtime
run, _ := chain.Compile(ctx)
-
outDocs, _ := run.Invoke(ctx, query, compose.WithCallbacks(helper))
-
-log.Printf("vikingDB retrieve success, query=%v, docs=%v", query, outDocs)
```
-## **Existing Implementations**
-
-- Volc VikingDB Retriever: Retrieval implementation based on Volcano Engine VikingDB [Retriever - volc VikingDB](/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb)
-
-## **Reference for Custom Implementation**
-
-When implementing a custom Retriever component, pay attention to the following points:
-
-1. Handle the option mechanism properly and manage common options.
-2. Handle callbacks appropriately.
-3. Inject specific metadata for use by subsequent nodes.
-
-### **Option Mechanism**
-
-The Retriever component provides a set of common options that need to be correctly handled during implementation:
+### Callback Structures
```go
-// Use GetCommonOptions to handle common options
-func (r *MyRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) ([]*schema.Document, error) {
- // 1. Initialize and read options
- options := &retriever.Options{ // You can set default values
- Index: &r.index,
- TopK: &r.topK,
- Embedding: r.embedder,
- }
- options = retriever.GetCommonOptions(options, opts...)
-
- // ...
-}
-```
-
-### **Callback Handling**
-
-Retriever implementations need to trigger callbacks at appropriate times. The following structures are defined by the retriever component:
-
-```go
-// Define callback input and output
+// Callback input/output definitions
type CallbackInput struct {
Query string
TopK int
@@ -272,69 +219,11 @@ type CallbackOutput struct {
}
```
-### **Complete Implementation Example**
-
-```go
-type MyRetriever struct {
- embedder embedding.Embedder
- index string
- topK int
-}
+## Existing Implementations
-func NewMyRetriever(config *MyRetrieverConfig) (*MyRetriever, error) {
- return &MyRetriever{
- embedder: config.Embedder,
- index: config.Index,
- topK: config.DefaultTopK,
- }, nil
-}
+- Volc VikingDB Retriever: [Retriever — VikingDB](/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb)
-func (r *MyRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) ([]*schema.Document, error) {
- // 1. Handle options
- options := &retriever.Options{
- Index: &r.index,
- TopK: &r.topK,
- Embedding: r.embedder,
- }
- options = retriever.GetCommonOptions(options, opts...)
-
- // 2. Get callback manager
- cm := callbacks.ManagerFromContext(ctx)
-
- // 3. Callback before starting retrieval
- ctx = cm.OnStart(ctx, info, &retriever.CallbackInput{
- Query: query,
- TopK: *options.TopK,
- })
-
- // 4. Execute retrieval logic
- docs, err := r.doRetrieve(ctx, query, options)
-
- // 5. Handle errors and complete callback
- if err != nil {
- ctx = cm.OnError(ctx, info, err)
- return nil, err
- }
-
- ctx = cm.OnEnd(ctx, info, &retriever.CallbackOutput{
- Docs: docs,
- })
-
- return docs, nil
-}
+## Implementation Notes
-func (r *MyRetriever) doRetrieve(ctx context.Context, query string, opts *retriever.Options) ([]*schema.Document, error) {
- // 1. If Embedding is set, generate vector representation of the query (pay attention to handling common options)
- var queryVector []float64
- if opts.Embedding != nil {
- vectors, err := opts.Embedding.EmbedStrings(ctx, []string{query})
- if err != nil {
- return nil, err
- }
- queryVector = vectors[0]
- }
-
- // 2. Other logic
- return docs, nil
-}
-```
+1. Handle common options and callbacks.
+2. Inject metadata required for downstream nodes.
diff --git a/content/en/docs/eino/core_modules/components/tools_node_guide/_index.md b/content/en/docs/eino/core_modules/components/tools_node_guide/_index.md
index 614bcfbeeee..74e7aeee613 100644
--- a/content/en/docs/eino/core_modules/components/tools_node_guide/_index.md
+++ b/content/en/docs/eino/core_modules/components/tools_node_guide/_index.md
@@ -1,28 +1,91 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: 'Eino: ToolsNode guide'
-weight: 0
+title: 'Eino: ToolsNode & Tool Guide'
+weight: 3
---
-## **Basic Introduction**
+## Introduction
-The ToolsNode component is a module designed to extend the capabilities of a model, allowing the model to call external tools to accomplish specific tasks. This component can be used in the following scenarios:
+`Tool` in Eino is an external capability that a `ChatModel` may choose to call, including local functions, MCP server tools, etc.
-- Enabling the model to obtain real-time information (such as search engines, weather queries, etc.)
-- Allowing the model to perform specific operations (such as database operations, API calls, etc.)
-- Extending the model's range of capabilities (such as mathematical calculations, code execution, etc.)
-- Integrating with external systems (such as knowledge base queries, plugin systems, etc.)
+`ToolsNode` is the designated tool executor in Eino. Whether inside a Graph or in an Agent, tool execution is performed via `ToolsNode`:
-## **Component Definition**
+```go
+// compose/tool_node.go
+
+// run tools using Invoke
+func (tn *ToolsNode) Invoke(ctx context.Context, input *schema.Message,
+ opts ...ToolsNodeOption) ([]*schema.Message, error)
+
+// run tools using Stream
+func (tn *ToolsNode) Stream(ctx context.Context, input *schema.Message,
+ opts ...ToolsNodeOption) (*schema.StreamReader[[]*schema.Message], error)
+```
+
+Configure `ToolsNode` with a list of tools and supporting policies:
+
+```go
+// compose/tool_node.go
+
+type ToolsNodeConfig struct {
+ Tools []tool.BaseTool
+
+ UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)
+
+ ExecuteSequentially bool
+
+ ToolArgumentsHandler func(ctx context.Context, name, arguments string) (string, error)
+
+ ToolCallMiddlewares []ToolMiddleware
+}
+```
+
+With this, `ToolsNode` can execute configured tools and gain extensibility such as execution ordering, error handling, argument processing, and middleware.
+
+How does `ToolsNode` decide which tool to run? It does not decide; it executes according to the incoming `*schema.Message`:
+
+```go
+// schema/message.go
+
+type Message struct {
+ // role should be 'assistant' for tool call message
+ Role RoleType `json:"role"`
+
+ // each ToolCall is generated by ChatModel and to be executed by ToolsNode
+ ToolCalls []ToolCall `json:"tool_calls,omitempty"`
+ // other fields...
+}
+
+// ToolCall in a message (used in Assistant when tool calls should be made)
+type ToolCall struct {
+ // Index helps identify the chunk when multiple calls exist (for merging in stream mode)
+ Index *int `json:"index,omitempty"`
+ // ID identifies the specific tool call
+ ID string `json:"id"`
+ // Type of the tool call, default "function"
+ Type string `json:"type"`
+ // Function payload
+ Function FunctionCall `json:"function"`
+ // Extra information
+ Extra map[string]any `json:"extra,omitempty"`
+}
+
+type FunctionCall struct {
+ Name string `json:"name,omitempty"`
+ Arguments string `json:"arguments,omitempty"`
+}
+```
+
+`ChatModel` generates a list of `ToolCall`s (name, arguments, etc.) and places them in a `*schema.Message` for `ToolsNode`. `ToolsNode` executes each `ToolCall` in turn. If `ExecuteSequentially` is set, tools are executed in the order of the `ToolCalls`. Each result is wrapped into a `*schema.Message` and returned as part of `ToolsNode` output.
-### **Interface Definition**
+## Interfaces
-The Tool component provides three levels of interfaces:
+Tool interfaces have three levels:
-> Code Location: eino/compose/tool/interface.go
+> Code: `eino/compose/tool/interface.go`
```go
// Basic tool interface, provides tool information
@@ -30,74 +93,80 @@ type BaseTool interface {
Info(ctx context.Context) (*schema.ToolInfo, error)
}
-// Callable tool interface, supports synchronous calls
+// Invokable tool, supports synchronous calls
type InvokableTool interface {
BaseTool
InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
}
-// Tool interface supporting streaming output
+// Streamable tool, supports streaming output
type StreamableTool interface {
BaseTool
StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)
}
```
-#### **Info Method**
+### Info
-- Function: Retrieve the description information of the tool
-- Parameters:
- - ctx: Context object
-- Return Values:
- - `*schema.ToolInfo`: Description information of the tool
- - error: Errors encountered during the information retrieval process
+- Purpose: return tool description for the model
+- Params:
+ - `ctx`: context
+- Returns:
+ - `*schema.ToolInfo`: tool metadata (name/desc/params)
+ - `error`
-#### **InvokableRun Method**
+### InvokableRun
-- Function: Execute the tool synchronously
-- Parameters:
- - ctx: Context object, used to pass request-level information and also to pass the Callback Manager
- - `argumentsInJSON`: JSON formatted parameter string
- - opts: Options for tool execution
-- Return Values:
- - string: Execution result
- - error: Errors encountered during the execution process
+- Purpose: synchronous tool execution
+- Params:
+ - `ctx`: context (request-scoped; carries callback manager)
+ - `argumentsInJSON`: JSON string of arguments
+ - `opts`: tool options
+- Returns:
+ - `string`: execution result
+ - `error`
-#### **StreamableRun Method**
+### StreamableRun
-- Function: Execute the tool in a streaming manner
-- Parameters:
- - ctx: Context object, used to pass request-level information and also to pass the Callback Manager
- - `argumentsInJSON`: JSON formatted parameter string
- - opts: Options for tool execution
-- Return Values:
- - `*schema.StreamReader[string]`: Result of the streaming execution
- - error: Errors encountered during the execution process
+- Purpose: streaming tool execution
+- Params:
+ - `ctx`: context (request-scoped; carries callback manager)
+ - `argumentsInJSON`: JSON string of arguments
+ - `opts`: tool options
+- Returns:
+ - `*schema.StreamReader[string]`: streaming result
+ - `error`
-### **ToolInfo Struct**
+## ToolInfo
-> Code Location:eino/schema/tool.go
+> Code: `eino/schema/tool.go`
```go
type ToolInfo struct {
- // Unique name of the tool, used to clearly express its purpose
+ // Unique tool name, clearly expressing its purpose
Name string
- // Describes how/when/why to use this tool
- // Can include a small number of examples in the description
+ // Guidance for the model on how/when/why to use the tool
+ // You can include brief examples in the description
Desc string
- // Definition of the parameters accepted by the tool
- // Can be described in two ways:
- // 1. Using ParameterInfo: schema.NewParamsOneOfByParams(params)
- // 2. Using OpenAPIV3: schema.NewParamsOneOfByOpenAPIV3(openAPIV3)
+ // Definition of accepted parameters
+ // Two ways to describe:
+ // 1. ParameterInfo: schema.NewParamsOneOfByParams(params)
+ // 2. OpenAPI v3: schema.NewParamsOneOfByOpenAPIV3(openAPIV3)
*ParamsOneOf
}
```
-### **Common Option**
+- Name: unique tool name
+- Desc: guidance for when/how/why to use the tool (can include brief examples)
+- ParamsOneOf: define accepted parameters in one of two ways:
+ - ParameterInfo: `schema.NewParamsOneOfByParams(params)`
+ - OpenAPI v3: `schema.NewParamsOneOfByOpenAPIV3(openAPIV3)`
-The Tool component uses ToolOption to define optional parameters. ToolsNode does not abstract a common option. Each specific implementation can define its own specific Option, wrapped into a unified ToolOption type using the WrapToolImplSpecificOptFn function.
+## Options
-## **Usage**
+`ToolOption` configures tool behavior. ToolsNode has no global options; implementations define specific options via `WrapToolImplSpecificOptFn`.
+
+## Usage
```go
import (
@@ -106,21 +175,19 @@ import (
"github.com/cloudwego/eino/schema"
)
-// Create tools node
toolsNode := compose.NewToolsNode([]tool.Tool{
searchTool, // Search tool
weatherTool, // Weather query tool
calculatorTool, // Calculator tool
})
-// Mock LLM output as input
input := &schema.Message{
Role: schema.Assistant,
ToolCalls: []schema.ToolCall{
{
Function: schema.FunctionCall{
Name: "weather",
- Arguments: `{"city": "Shenzhen", "date": "tomorrow"}`,
+ Arguments: `{"city": "深圳", "date": "tomorrow"}`,
},
},
},
@@ -129,9 +196,9 @@ input := &schema.Message{
toolMessages, err := toolsNode.Invoke(ctx, input)
```
-ToolsNode is typically not used alone and is generally used in orchestration, following ChatModel.
+ToolsNode is typically used after a `ChatModel` inside orchestration.
-### **Using in Orchestration**
+### In Orchestration
```go
import (
@@ -140,37 +207,26 @@ import (
"github.com/cloudwego/eino/schema"
)
-// Create tools node
-toolsNode := compose.NewToolsNode([]tool.Tool{
- searchTool, // Search tool
- weatherTool, // Weather query tool
- calculatorTool, // Calculator tool
-})
-
-// Use in Chain
+// Chain
chain := compose.NewChain[*schema.Message, []*schema.Message]()
chain.AppendToolsNode(toolsNode)
-// In graph
+// Graph
graph := compose.NewGraph[*schema.Message, []*schema.Message]()
-chain.AddToolsNode(toolsNode)
+graph.AddToolsNode(toolsNode)
```
-## **Option Mechanism**
-
-Custom Tools can implement specific Options according to their needs:
+## Option Mechanism
```go
import "github.com/cloudwego/eino/components/tool"
-// Define Option struct
type MyToolOptions struct {
Timeout time.Duration
MaxRetries int
RetryInterval time.Duration
}
-// Define Option function
func WithTimeout(timeout time.Duration) tool.Option {
return tool.WrapImplSpecificOptFn(func(o *MyToolOptions) {
o.Timeout = timeout
@@ -178,9 +234,7 @@ func WithTimeout(timeout time.Duration) tool.Option {
}
```
-## **Using Options and Callbacks**
-
-### **Callback Usage Example**
+## Callbacks
```go
import (
@@ -192,18 +246,18 @@ import (
"github.com/cloudwego/eino/components/tool"
)
-// Create callback handler
+// 创建 callback handler
handler := &callbackHelper.ToolCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context {
- fmt.Printf("Starting tool execution, parameters: %s\n", input.ArgumentsInJSON)
+ fmt.Printf("开始执行工具,参数: %s\n", input.ArgumentsInJSON)
return ctx
},
OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context {
- fmt.Printf("Tool execution completed, result: %s\n", output.Response)
+ fmt.Printf("工具执行完成,结果: %s\n", output.Response)
return ctx
},
OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*tool.CallbackOutput]) context.Context {
- fmt.Println("Tool started streaming output")
+ fmt.Println("工具开始流式输出")
go func() {
defer output.Close()
@@ -215,26 +269,26 @@ handler := &callbackHelper.ToolCallbackHandler{
if err != nil {
return
}
- fmt.Printf("Received streaming output: %s\n", chunk.Response)
+ fmt.Printf("收到流式输出: %s\n", chunk.Response)
}
}()
return ctx
},
}
-// Use callback handler
+// 使用 callback handler
helper := callbackHelper.NewHandlerHelper().
Tool(handler).
Handler()
-/*** Compose a chain
+/*** compose a chain
* chain := NewChain
* chain.appendxxx().
* appendxxx().
* ...
*/
-// Use at runtime
+// 在运行时使用
runnable, err := chain.Compile()
if err != nil {
return err
@@ -242,7 +296,21 @@ if err != nil {
result, err := runnable.Invoke(ctx, input, compose.WithCallbacks(helper))
```
-## **Existing Implementations**
+## Implementations
+
+1. Google Search: [Tool — GoogleSearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch)
+2. DuckDuckGo: [Tool — DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
+3. MCP server as tool: [Tool — MCP](/docs/eino/ecosystem_integration/tool/tool_mcp)
+
+### v0.5.x → v0.6.x Migration
+
+Eino removed all OpenAPI 3.0 related definitions and methods and switched to JSONSchema 2020-12 because:
+
+- Major model vendors and MCP tool protocols specify input/output schemas via JSONSchema
+- `getkin/kin-openapi@v0.118.0` has security issues, and later secure versions introduced breaking changes
+
+See details: https://github.com/cloudwego/eino/discussions/397
+
+After upgrading, some `eino-ext` modules may error with `undefined: schema.NewParamsOneOfByOpenAPIV3`; upgrade those modules to the latest versions.
-1. Google Search Tool: Tool implementation based on Google search [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch)
-2. duckduckgo search tool: Tool implementation based on duckduckgo search [Tool - DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
+If schema migration is complex, use the helper tooling linked in the discussion above.
diff --git a/content/en/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md b/content/en/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
index 88745073a0e..2dde341bc6f 100644
--- a/content/en/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
+++ b/content/en/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
@@ -1,22 +1,20 @@
---
Description: ""
-date: "2025-04-09"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: How to create a tool?
-weight: 0
+title: How to Create a Tool
+weight: 1
---
-## The basic structure of Tool
+## Tool Structure Basics
-An agent needs to take two steps to invoke a tool: ① The large model constructs invocation parameters based on the tool's functions and parameter requirements. ② Actually invokes the tool
+An agent calling a tool involves two steps: (1) the LLM constructs parameters according to the tool definition; (2) the tool executes with those parameters. A tool therefore needs:
-These two basic steps also require the tool to include two parts:
+- Tool metadata and parameter constraints
+- An execution interface
-- Introduction to the functions of the tool and the parameter information needed to invoke this tool.
-- Call the interface of this tool
-
-In Eino, the BaseTool interface requires any tool to have a ` Info()` interface that returns tool information, as follows:
+In Eino, any tool must implement `Info()` to return tool metadata:
```go
type BaseTool interface {
@@ -24,7 +22,7 @@ type BaseTool interface {
}
```
-And according to whether the return structure of a tool is streamable after it is invoked, it can be divided into InvokableTool and StreamableTool, which are also defined in the form of interfaces:
+Execution interfaces depend on whether the result is streaming:
```go
type InvokableTool interface {
@@ -41,16 +39,16 @@ type StreamableTool interface {
}
```
-## The representation of ToolInfo
+## ToolInfo Representations
-In the process of function call in a large model, the large model generates the parameters needed for the function call, which requires the large model to understand whether the generated parameters meet the constraints. In Eino, based on the developers' usage habits and domain standards, it provides ` params map[string]*ParameterInfo` and ` *openapi3.Schema` two ways to express parameter constraints.
+In LLM function-call flows, the model must understand whether generated parameters satisfy constraints. Eino supports two representations: `params map[string]*ParameterInfo` and `*openapi3.Schema`.
-### Method 1 - map[string]*ParameterInfo
+### 1) `map[string]*ParameterInfo`
-In the intuitive habits of many developers, the description of parameters can be represented by a map, where the key is the parameter name and the value is the detailed constraint of this parameter. In Eino, ParameterInfo is defined to represent the description of a parameter, as follows:
+Intuitive map-based parameter descriptions:
```go
-// watch at: https://github.com/cloudwego/eino/blob/main/schema/tool.go
+// Full definition: https://github.com/cloudwego/eino/blob/main/schema/tool.go
type ParameterInfo struct {
Type DataType // The type of the parameter.
ElemInfo *ParameterInfo // The element type of the parameter, only for array.
@@ -61,7 +59,7 @@ type ParameterInfo struct {
}
```
-For example, a parameter representing "User" can be expressed as:
+Example:
```go
map[string]*schema.ParameterInfo{
@@ -73,48 +71,46 @@ map[string]*schema.ParameterInfo{
Type: schema.Integer,
},
"gender": &schema.ParameterInfo{
- Type: schema.String,
+ Type: schema.String,
Enum: []string{"male", "female"},
},
}
```
-This representation method is very simple and intuitive, and it is often used when parameters are manually maintained by developers through coding.
-
-### Method 2 - openapi3.Schema
-
-Another common way to represent parameter constraints is ` JSON schema` , which is defined by OAI. [ OpenAPI](https://github.com/OAI/OpenAPI-Specification) is the most commonly used standard, and Eino also supports the use of openapi3.Schema to represent parameter constraints.
+### 2) JSON Schema (2020-12)
-The OpenAPI 3 standard offers a wide range of constraints for parameters, and a detailed description can be found in [ OpenAPI 3.03](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object) . In actual use, developers typically do not build this structure themselves, but use some methods to generate it.
+JSON Schema’s constraint system is rich. In practice, you usually generate it from struct tags or helper functions.
-#### Generate using GoStruct2ParamsOneOf
+#### `GoStruct2ParamsOneOf`
-Eino provides a way to describe parameter constraints in structures using go tags, and it also offers the GoStruct2ParamsOneOf method to generate parameter constraints for a struct. Its function signature is as follows:
+Describe constraints via Go tags on a struct and generate `ParamsOneOf`:
```go
func GoStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error)
```
-Extract the field name and description of the parameters from T, and the Tag used for extraction is as follows:
+Supported tags:
-- jsonschema: "description=xxx"
-- jsonschema: "enum=xxx,enum=yyy,enum=zzz"
-- jsonschema: "required"
-- json: "xxx,omitempty" => The "omitempty" in json tag represents that it is not required
-- Implement a custom parsing method using utils.WithSchemaCustomizer
+- `jsonschema_description:"xxx"` [recommended] or `jsonschema:"description=xxx"`
+- Note: descriptions often include commas; tag commas separate fields and cannot be escaped. Prefer `jsonschema_description`.
+- `jsonschema:"enum=xxx,enum=yyy,enum=zzz"`
+- `jsonschema:"required"`
+- `json:"xxx,omitempty"` → `omitempty` implies not required
+- Customize via `utils.WithSchemaModifier`
-You can refer to the following examples:
+Example:
```go
package main
import (
+ "context"
"github.com/cloudwego/eino/components/tool/utils"
)
type User struct {
- Name string `json:"name" jsonschema:"required,description=the name of the user"`
- Age int `json:"age" jsonschema:"description=the age of the user"`
+ Name string `json:"name" jsonschema_description=the name of the user jsonschema:"required"`
+ Age int `json:"age" jsonschema_description:"the age of the user"`
Gender string `json:"gender" jsonschema:"enum=male,enum=female"`
}
@@ -123,17 +119,13 @@ func main() {
}
```
-This method is generally not invoked by developers, and is often directly used ` utils.GoStruct2ToolInfo()` to build ToolInfo, or directly use ` utils.InferTool()` to directly build a tool. You can refer to the "Convert local functions into tools" section below for more details.
-
-#### Generated through the openapi.json file
+You usually won’t call this directly; prefer `utils.GoStruct2ToolInfo()` or `utils.InferTool()`.
-Since OpenAPI is a very universal standard, many tools or platforms can export OpenAPI.json files, especially in some HTTP interface management tools. If the tool is a wrapper for some OpenAPI, you can use this method.
+
-See the usage examples in [ eino-examples](https://github.com/cloudwego/eino-examples/blob/main/components/tool/openapi3/main.go#L33) 。
+## Approach 1 — Implement Interfaces Directly
-## Method 1 - Directly implement the interface
-
-Since the definition of a tool is an interface, the most direct way to implement a tool is to implement the interface. Take InvokableTool as an example:
+Implement `InvokableTool`:
```go
type AddUser struct{}
@@ -143,49 +135,44 @@ func (t *AddUser) Info(_ context.Context) (*schema.ToolInfo, error) {
Name: "add_user",
Desc: "add user",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
- // omit. Refer to the way of constructing params constraints in the above text.
+ // omitted; see earlier for building params constraints
}),
}, nil
}
func (t *AddUser) InvokableRun(_ context.Context, argumentsInJSON string, _ ...tool.Option) (string, error) {
- // 1. Deserialize argumentsInJSON and handle options, etc.
+ // 1. Deserialize argumentsInJSON and handle options
user, _ := json.Unmarshal([]byte(argumentsInJSON))
// 2. Handle business logic
- // 3. Serialize the result to a string and return it
+ // 3. Serialize the result to string and return
return `{"msg": "ok"}`, nil
}
```
-Since the function call parameters given by the large model are always a string, in the Eino framework, the imported parameter of the tool is also a json serialized into a string. Therefore, this approach requires developers to handle the deserialization of parameters themselves, and the result of the call is also returned as a string.
-
-## Method 2 - Convert local functions into tools
+Because the LLM always supplies a JSON string, the tool receives `argumentsInJSON`; you deserialize it and return a JSON string.
-During development, we often need to encapsulate a local function into a tool of Eino, for example, we already have an ` AddUser` method in our code, but to allow the large model to independently decide how to call this method, we need to turn this method into a tool and bind it to the large model.
+## Approach 2 — Wrap a Local Function
-Eino provides ` NewTool` methods to convert a function into a tool. Additionally, it offers InferTool methods for scenarios where parameter constraints are represented through the tag of a struct, making the build process simpler.
+Often you have an existing function (e.g., `AddUser`) and want the LLM to decide when/how to call it. Eino provides `NewTool` for this, and `InferTool` for tag-based parameter constraints.
-> You can refer to the examples of the following methods:[ cloudwego/eino/components/tool/utils/invokable_func_test.go](https://github.com/cloudwego/eino/blob/main/components/tool/utils/invokable_func_test.go) and [ cloudwego/eino/components/tool/utils/streamable_func_test.go](https://github.com/cloudwego/eino/blob/main/components/tool/utils/streamable_func_test.go) in the unit test. Here, we only take InvokableTool as an example, and StreamableTool also has corresponding construction methods
+> See tests in `cloudwego/eino/components/tool/utils/invokable_func_test.go` and `streamable_func_test.go`.
-### Use the NewTool method
+### `NewTool`
-When a function satisfies the following function signature, you can use NewTool to turn it into an InvokableTool:
+For functions of signature:
```go
type InvokeFunc[T, D any] func(ctx context.Context, input T) (output D, err error)
```
-The method of NewTool is as follows:
+Use:
```go
-// See the code at: github.com/cloudwego/eino/components/tool/utils/invokable_func.go
func NewTool[T, D any](desc *schema.ToolInfo, i InvokeFunc[T, D], opts ...Option) tool.InvokableTool
```
-> Similarly, NewStreamTool can create StreamableTool
-
-Take AddUser as an example, you can build it in the following way:
+Example:
```go
import (
@@ -222,7 +209,7 @@ func createTool() tool.InvokableTool {
Type: schema.Integer,
},
"gender": &schema.ParameterInfo{
- Type: schema.String,
+ Type: schema.String,
Enum: []string{"male", "female"},
},
},
@@ -233,19 +220,15 @@ func createTool() tool.InvokableTool {
}
```
-### Use the InferTool method
+### `InferTool`
-As we can see from NewTool, the process of building a tool requires us to pass in ToolInfo and InvokeFunc separately. Among them, ToolInfo contains the part of ParamsOneOf, which represents the constraint of the imported parameter of the function. At the same time, the function signature of InvokeFunc also has input parameters, which means: The part of ParamsOneOf and the input parameters of InvokeFunc need to be consistent.
-
-When a function is fully implemented by developers themselves, they need to manually maintain the input parameters and ParamsOneOf to keep them consistent. A more elegant solution is to "directly maintain parameter constraints in the input parameter type definition", which can be referred to the introduction of ` GoStruct2ParamsOneOf` above.
-
-When the parameter constraint information is included in the input parameter type definition, you can use InferTool to implement it. The function signature is as follows:
+When parameter constraints live in the input struct tags, use `InferTool`:
```go
func InferTool[T, D any](toolName, toolDesc string, i InvokeFunc[T, D], opts ...Option) (tool.InvokableTool, error)
```
-Take AddUser as an example:
+Example:
```go
import (
@@ -273,17 +256,17 @@ func createTool() (tool.InvokableTool, error) {
}
```
-### Use the InferOptionableTool method
+### `InferOptionableTool`
-The Option mechanism is a feature provided by Eino for passing dynamic parameters at runtime. For more details, you can refer to [Eino: CallOption capabilities and specification](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities) . This mechanism is also applicable in custom tools.
+Eino’s Option mechanism passes dynamic runtime parameters. Details: `Eino: CallOption capabilities and conventions` at `/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities`. The same mechanism applies to custom tools.
-When developers want to implement a function that requires custom option parameters, they can use the InferOptionableTool method. Compared to the requirements for function signatures in InferTool, this method's signature adds an option parameter, as follows:
+When you need custom option parameters, use `InferOptionableTool`:
```go
func InferOptionableTool[T, D any](toolName, toolDesc string, i OptionableInvokeFunc[T, D], opts ...Option) (tool.InvokableTool, error)
```
-Here is an example (adapted from[ cloudwego/eino/components/tool/utils/invokable_func_test.go](https://github.com/cloudwego/eino/blob/main/components/tool/utils/invokable_func_test.go) ):
+Example (adapted from `cloudwego/eino/components/tool/utils/invokable_func_test.go`):
```go
import (
@@ -325,20 +308,24 @@ func useInInvoke() {
}
```
-## Method 3 - Use the tool provided in eino-ext
+## Approach 3 — Use tools from eino-ext
-In addition to the various custom tools that need to be implemented by yourself, there are also many general tools implemented in the eino-ext project that can be used out of the box, such [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch) 、[Tool - DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search) 、wikipedia、httprequest , etc. You can refer to [https://github.com/cloudwego/eino-ext/tree/main/components/tool](https://github.com/cloudwego/eino-ext/tree/main/components/tool) for various implementations.
+Beyond custom tools, the `eino-ext` project provides many ready-to-use implementations: `Googlesearch`, `DuckDuckGoSearch`, `wikipedia`, `httprequest`, etc. See implementations at https://github.com/cloudwego/eino-ext/tree/main/components/tool and docs:
-## Method 4 - Use the MCP protocol
+- Tool — Googlesearch: `/docs/eino/ecosystem_integration/tool/tool_googlesearch`
+- Tool — DuckDuckGoSearch: `/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search`
-MCP (Model Context Protocol) is an open model context protocol. Now more and more tools and platforms are exposing their own capabilities to large models based on this protocol. eino can use the callable tools provided by MCP as tools, which will greatly expand the variety of tools.
+## Approach 4 — Use MCP protocol
-It's very convenient to use the tools provided by MCP in Eino:
+MCP (Model Context Protocol) is an open protocol for exposing tool capabilities to LLMs. Eino can treat tools provided via MCP as regular tools, greatly expanding available capabilities.
+
+Using MCP tools in Eino is straightforward:
```go
import (
"fmt"
"log"
+ "context"
"github.com/mark3labs/mcp-go/client"
mcpp "github.com/cloudwego/eino-ext/components/tool/mcp"
)
@@ -374,4 +361,4 @@ func getMCPTool(ctx context.Context) []tool.BaseTool {
}
```
-> Code reference: [https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go](https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go)
+Code reference: https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go
diff --git a/content/en/docs/eino/core_modules/devops/_index.md b/content/en/docs/eino/core_modules/devops/_index.md
index e7971573bb2..0da65ed6cd9 100644
--- a/content/en/docs/eino/core_modules/devops/_index.md
+++ b/content/en/docs/eino/core_modules/devops/_index.md
@@ -1,10 +1,11 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'EinoDev: Devops tools'
-weight: 0
+title: 'Eino Dev: Application Tooling'
+weight: 5
---
-🚀 Eino is a development framework for Go AI integration components, providing common components related to AI applications as well as integration component orchestration capabilities. To better assist developers in using Eino, we offer the "GoLand Eino IDE Plugin". Install the plugin now ([EinoDev Plugin Installation Guide](/docs/eino/core_modules/devops/ide_plugin_guide)) to help you develop efficiently. 🚀
+🚀 Eino is a Go framework for AI component integration and orchestration. To help developers use Eino efficiently, we provide the “Eino Dev” plugin. Install it now ([Eino Dev Plugin Installation Guide](/docs/eino/core_modules/devops/ide_plugin_guide)) to accelerate development. 🚀
+
diff --git a/content/en/docs/eino/core_modules/devops/ide_plugin_guide.md b/content/en/docs/eino/core_modules/devops/ide_plugin_guide.md
index 8e7dbaca8fd..a155ba7d022 100644
--- a/content/en/docs/eino/core_modules/devops/ide_plugin_guide.md
+++ b/content/en/docs/eino/core_modules/devops/ide_plugin_guide.md
@@ -1,74 +1,70 @@
---
Description: ""
-date: "2025-03-13"
+date: "2025-01-20"
lastmod: ""
tags: []
-title: EinoDev IDE Plugin Introduction
+title: Eino Dev Plugin Installation Guide
weight: 1
---
-## **Background**
+## Background & Overview
-> [Eino: Overview](/docs/eino/overview)
->
-> **🚀 Eino is a Go AI component development framework that provides commonly used AI application components and integration component orchestration capabilities. To better assist developers in using Eino, we offer a GoLand Eino IDE plugin to help you develop efficiently 🚀**
+> See [Eino: Overview](/docs/eino/overview)
-## **Introduction**
+Eino is a Go framework for AI component integration and orchestration. The “Eino Dev” plugin helps you build AI applications more efficiently. 🚀
-
+
-## **Installation Version Requirements**
+## Installation
+
+### Version Compatibility
-| Plugin Version | Goland IDE Version | VS Code Version | Eino-Ext/devops Version |
+| 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.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 |
-**Plugin Version :** Plugin version information
-
-**Goland IDE Version** : The minimum version supported by the GoLand IDE
-
-**VS Code Version** : The minimum version supported by the VS Code
-
-**Eino-Ext/devops Version :** The comfortable version for debug module of [eino-ext/devops](https://github.com/cloudwego/eino-ext/tree/main/devops)
-
-## **Install**
+- Plugin Version: plugin release
+- GoLand IDE Version: minimum compatible version
+- VS Code Version: minimum compatible version
+- eino-ext/devops Version: matching version of [eino-ext/devops](https://github.com/cloudwego/eino-ext/tree/main/devops)
-### Goland
+### GoLand
-| Enter GoLand, click on Settings, and select Plugin.
+ | Open GoLand → Settings → Plugins
- | Through Marketplace, search for Eino Dev plugin and follow
+ | Search for Eino Dev in Marketplace and install
-
|
### VS Code
-- In VS Code, click on the "Extensions icon" to enter the extension marketplace. Search for EinoDev and install it.
+- Open Extensions, search for “Eino Dev”, and install.
+
+
-
+## Features
-> 💡
-> **After the plugin installation is completed, you can find the EinoDev debug plugin icon in the plugin list on the right side of the IDE. Now, you can experience the debugging and orchestration capabilities provided by the plugin.**
+> 💡 After installation, you can use the debugging and orchestration features immediately.
-Goland
-Find the 「 EinoDev 」 icon in the right sidebar and click it.:
-
+ | GoLand
+Find the “Eino Dev” icon in the right sidebar and click it:
+
| VS Code
-Find 「EinoDev」 at the bottom and click it.:
-
+Click “Eino Dev” at the bottom:
+
+
|
-## Function Introduction
+### Graph Orchestration
-### EinoDev Graph Orchestration
+Details: [Eino Dev Visual Orchestration Guide](/docs/eino/core_modules/devops/visual_orchestration_plugin_guide)
@@ -77,7 +73,9 @@ Find 「EinoDev」 at the bottom and click it.:
|
-### EinoDev Graph Debugging
+### Graph Debugging
+
+Details: [Eino Dev Visual Debugging Guide](/docs/eino/core_modules/devops/visual_debug_plugin_guide)
|
@@ -85,3 +83,4 @@ Find 「EinoDev」 at the bottom and click it.:
|
|
+
diff --git a/content/en/docs/eino/core_modules/devops/visual_debug_plugin_guide.md b/content/en/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
index 8a67e81f34d..65ecbba27e5 100644
--- a/content/en/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
+++ b/content/en/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
@@ -1,122 +1,116 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: EinoDev visual debug plugin guide
+title: Eino Dev Visual Debugging Guide
weight: 3
---
-## **Introduction**
+## Overview
> 💡
-> Using this plugin, you can visually debug the orchestration products (Graph, Chain) written using the Eino framework:
+> Use this plugin to visually debug orchestration artifacts built with Eino (Graph, Chain):
>
-> 1. Visualization rendering of orchestration products;
-> 2. Debug by mocking inputs starting from any operational node.
+> 1. Visual rendering of orchestration
+> 2. Start from any operable node and debug with mock input
-## **Quick Start**
+## Quick Start
-### **Download** eino-example
+### Download eino-examples
-> github repo:_[https://github.com/cloudwego/eino-examples](https://github.com/cloudwego/eino-examples)_
+Repo: [https://github.com/cloudwego/eino-examples](https://github.com/cloudwego/eino-examples)
```bash
-# HTTPS
-git clone https://github.com/cloudwego/eino-examples.git
-
-# SSH
+git clone https://github.com/cloudwego/eino-examples.git
+# or
git clone git@github.com:cloudwego/eino-examples.git
```
-### **Install Dependencies**
+### Install Dependencies
-Execute the following commands in the project directory
+In the project directory, run the following in order:
```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
```
-### Run Demo
+### Run the Demo
-Go to `eino-examples/devops/debug/main.go`, and run `main.go`. Since the plugin will also start a local HTTP service to connect the user service process, a network connection warning will pop up. Click Allow.
+Open `eino-examples/devops/debug/main.go` and run `main.go`. The plugin launches a local HTTP service to connect to your process; allow network access if prompted.
-### **Configure Debug Address**
+### Configure Debug Address
-1.Click the debugging feature on the left or in the middle to enter the debugging configuration
+1) Click the debug feature entry on the left or center to open configuration
|
-2.Click to configure the debug address
+2) Click “Configure Address”
|
-3.Enter 127.0.0.1:52538
+3) Enter 127.0.0.1:52538
|
-4.Click Confirm to enter the debug interface, selecting the Graph to debug
+4) Confirm to enter the debug view, then select the Graph to debug
|
-### **Start Debugging**
+### Start Debugging
-1.Click "Test Run" to execute from the start node
+1) Click “Test Run” to start from START
|
-2.Enter "hello eino", and click Confirm
+2) Enter "hello eino" and confirm
|
-
-3.The debugging area displays the input and output of each node
-
- |
-4.Click Input and Output to switch and view node information
-
- |
+3) Inspect per-node inputs/outputs | 4) Switch Input/Output views |
+
+
+ |
+ |
+
-## **Features Overview**
+## Feature Overview
-### **Local or Remote Debugging**
+### Local or Remote Debugging
-Regardless of whether the target debugging orchestration output is on a local computer or a remote server, you can actively connect to the server where the target debugging object is located by configuring IP:Port.
+Configure `IP:Port` to connect to the target process, whether local or remote.
-### **Orchestration Topology Visualization**
+### Orchestration Visualization
-Supports Graph and Chain orchestration topology visualization.
+Supports Graph and Chain topology visualization.
-### **Debug From Any Node**
+### Start from Any Node
-### **View Node Execution Results**
+### Inspect Node Results
-The execution results of each node are displayed in the debugging area in the order of execution, including: input, output, execution time
+Each node’s input, output, and execution time are shown in order.
-## **Debugging From Scratch**
+## Debugging from Scratch
-### **Orchestrating with Eino**
+### Orchestrate with Eino
-The plugin supports debugging the orchestration outputs of Graph and Chain. Assuming you already have the following orchestration code:
+The plugin supports debugging Graph and Chain artifacts. Example registration:
```go
func RegisterSimpleGraph(ctx context.Context) {
@@ -144,48 +138,41 @@ func RegisterSimpleGraph(ctx context.Context) {
}
```
-### **Install Dependencies**
-
-In the project directory, execute the following commands in sequence:
+### Install Dependencies
```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
```
-### **Call Debug Initialization Function**
+### Initialize Debugging
-Because debugging requires starting an HTTP service in the user's main process to interact with local debugging plugins, the user needs to proactively call `Init()` from _github.com/cloudwego/eino-ext/devops_ to start the debugging service.
+Because debugging starts an HTTP service in your main process to interact with the local plugin, you must call `Init()` from `github.com/cloudwego/eino-ext/devops` to start the debug service.
> 💡
-> Notice:
+> Notes
>
-> 1. Make sure that the orchestration product of the target debugging has executed `Compile()` at least once.
-> 2. The execution of `devops.Init()` must be carried out before calling `Compile()`.
-> 3. Users need to ensure that the main process does not exit after the execution of `devops.Init()`.
-
-For example, adding the debugging service startup code in the `main()` function:
+> 1. Ensure the target orchestration has run `Compile()` at least once.
+> 2. `devops.Init()` must run before calling `Compile()`.
+> 3. Make sure the main process stays alive after `devops.Init()`.
```go
-// 1. Call the debug service initialization function
+// 1. Initialize debug service
err := devops.Init(ctx)
if err != nil {
logs.Errorf("[eino dev] init failed, err=%v", err)
return
}
-// 2. Compile the target debug orchestration artifact
+// 2. Compile the target orchestration artifact to debug
RegisterSimpleGraph(ctx)
```
-### **Run User Process**
+### Run Your Process
-Run your process on a local computer or remote environment and ensure the main process does not exit.
+Run your process locally or remotely, and ensure the main process does not exit.
-In github.com/cloudwego/eino-examples/devops/debug/main.go, the `main()` code is as follows:
+In `github.com/cloudwego/eino-examples/devops/debug/main.go`, `main()` looks like:
```go
func main() {
@@ -212,125 +199,159 @@ func main() {
}
```
-### **Configure Debug Address**
-
-- **IP**:The IP address of the server where the user process is running.
- - If the user process is running on a local computer, enter `127.0.0.1`.
- - If the user process is running on a remote server, enter the remote server's IP address, compatible with both IPv4 and IPv6.
-- **Port**:The port on which the debugging service is listening, default is `52538`, and can be modified using the `WithDevServerPort` option method.
+### Configure Address
-> 💡
-> Notes
->
-> - Local computer debugging: The system might pop up a network access warning, just allow the access.
-> - Remote server debugging: You need to ensure the port is accessible.
+- IP: `127.0.0.1` for local; remote server IP for remote (IPv4/IPv6).
+- Port: default `52538`, configurable via `WithDevServerPort`.
-After configuring the IP and Port, click confirm, and the debugging plugin will automatically connect to the target debugging server. If successfully connected, the connection status indicator will turn green.
+Allow network prompts locally; ensure remote ports are reachable. Once connected, the status indicator turns green.
-### Select Target Debugging Orchestration
+### Select an Artifact
-Ensure the orchestration product you want to debug has been executed at least once using `Compile()`. Since debugging is designed for instances of orchestration product, multiple executions of `Compile()` will register multiple products with the debugging service, resulting in multiple selectable debugging targets in the list.
+Ensure your target orchestration has been compiled at least once. Multiple `Compile()` runs register multiple artifacts; you’ll see them in the selection list.
-### **Start Debugging**
+### Start Debugging
-Debugging can start from any node, including the start node and other intermediate nodes.
-
-- Start debugging from the START node: Click "Test Run", enter the mock input (if the input is complex, the input structure will be inferred automatically), and then click Confirm to execute your graph. The result of each node will be displayed below.
+- From START: click “Test Run”, enter mock input (complex types are inferred), and confirm.
-- Start debugging from any operable node: For example, execute starting from the second node.
+- From a specific node: click the run button on that node.
-
-
-### **View Execution Results**
-
-Start debugging from the START node, click Test Run, and view the debugging results below the plugin.
-
-
-
-Debug from any operable node and view the debugging results below the plugin.
-
-
+## Advanced
-## Advanced Features
+### Specify Implementation Type for Interface Fields
-### **Specifying Implementation Types for Interface Fields**
+Interface-typed fields render as `{}` by default. Type a space inside `{}` to select an implementation type. The plugin uses a special JSON structure:
-For fields of the interface type, they are rendered as `{}` by default. By entering a space within `{}`, a list of interface implementation types will appear. After selecting a type, the system will generate a special structure to represent the interface information; this special structure is defined as follows:
-
-```go
+```json
{
- "_value": {} // JSON value generated according to the specific type
- "_eino_go_type": "*model.MyConcreteType" // Go type name
+ "_value": {}, // JSON value of the concrete type
+ "_eino_go_type": "*model.MyConcreteType" // Go type name
}
```
> 💡
-> The system has some common interface types built-in, such as `string`, `schema.Message`, etc., which can be used directly. If you need to customize the interface implementation type, you can register it using the `AppendType` method provided by `devops`.
+> Common interface types like `string`, `schema.Message` are built-in. To register custom types, use `devops.AppendType` during `Init()`.
+
+1) Suppose you have orchestration code where the graph input is `any`, and `node_1` takes `*NodeInfo`:
+
+```go
+type NodeInfo struct {
+ Message string
+}
-1. Suppose you already have the following orchestration code, where the input of the graph is defined as `any`, and the input of `node_1` is defined as `*NodeInfo`:
+func RegisterGraphOfInterfaceType(ctx context.Context) {
+ // Define a graph that input parameter is any.
+ g := compose.NewGraph[any, string]()
- ```go
- type NodeInfo struct {
- Message 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
+ }))
- func RegisterGraphOfInterfaceType(ctx context.Context) {
- // Define a graph that input parameter is any.
- g := compose.NewGraph[any, string]()
+ _ = 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_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_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
+ return input + " process by node_3,", 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.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {
- return input + " process by node_3,", nil
- }))
+ _ = g.AddEdge("node_1", "node_2")
- _ = g.AddEdge(compose._START_, "node_1")
+ _ = g.AddEdge("node_2", "node_3")
- _ = g.AddEdge("node_1", "node_2")
+ _ = g.AddEdge("node_3", compose._END_)
- _ = g.AddEdge("node_2", "node_3")
+ r, err := g.Compile(ctx)
+ if err != nil {
+ logs.Errorf("compile graph failed, err=%v", err)
+ return
+ }
+}
+```
- _ = g.AddEdge("node_3", compose._END_)
+2) Before debugging, register the custom `*NodeInfo` type with `AppendType` at `Init()`:
- r, err := g.Compile(ctx)
- if err != nil {
- logs.Errorf("compile graph failed, err=%v", err)
- return
- }
- }
- ```
-2. Before debugging, register the custom `*NodeInfo` type by using the `AppendType` method:
+```go
+err := devops.Init(ctx, devops.AppendType(&graph.NodeInfo{}))
+```
- ```go
- err := devops.Init(ctx, devops.AppendType(&graph.NodeInfo{}))
- ```
-3. During debugging, in the Json input box of the Test Run, fields of the interface type will be presented as `{}` by default. You can view all built-in and custom registered data types by typing a space within `{}` and selecting the specific implementation type of the interface.
+3) During Test Run, interface fields show `{}` by default. Type a space inside `{}` to view all built-in and custom types, select the concrete implementation, then fill `_value`.
-
+### Debugging `map[string]any`
-1. Complete the debug node input in the `_value` field.
+If a node input is `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)
+}
+```
-1. Click confirm to view the debugging results.
+During debugging, in the Test Run JSON input box, use the following format to specify concrete types for values:
-
+```json
+{
+ "name": {
+ "_value": "alice",
+ "_eino_go_type": "string"
+ },
+ "score": {
+ "_value": "99",
+ "_eino_go_type": "int"
+ }
+}
+```
diff --git a/content/en/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md b/content/en/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
index d3ab4e69f32..4d44d957928 100644
--- a/content/en/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
+++ b/content/en/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
@@ -1,117 +1,96 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: EinoDev visual orchestration plugin guide
+title: Eino Dev Visual Orchestration Guide
weight: 2
---
-## **Introduction**
+## Overview
-> 💡
-> The Eino visual orchestration plugin provided by Goland allows for the orchestration of Graphs through component drag-and-drop in GoLand, and supports import and export functionalities.
+> The GoLand Eino visual orchestration plugin lets you compose Graphs by dragging components and generates code. Import/export are supported.
-## Initial Plugin Overview
-
-### Plugin Features Introduction
+## First Look
-## Introduction to Orchestration Components
+## Orchestration Concepts
### Graph
-- Consistent with the concept of Graph in Eino, it refers to the Graph ultimately generated by the plugin side. You can add a Graph on the following interface.
-- Click on Add Plugin, and a creation dialog box will pop up. Fill in the configuration information according to the field instructions to generate a Graph orchestration object.
-
+- Matches Eino’s Graph concept. Add a Graph from the plugin UI.
+- Fill the creation dialog to generate a Graph object.
+
+
### Node
-- Consistent with Node in Eino, after creating the Graph, different types of Nodes can be added to the canvas through the Add Nodes option at the top right corner of the interface.
-- Nodes added to the Graph will automatically fill the NodeKey by default. Additionally, you can expand More Config to configure optional settings for the Nodes.
-
+- Matches Eino’s Node concept. After creating a Graph, click “AddNodes” to add different node types onto the canvas.
+- Node keys are auto-filled; expand “More config” for optional settings.
+
+
### Component
-- Component is essential information that constitutes a Node. Different Components correspond to different Node types and offer built-in Official Components and Custom Components.
-- After completing the Add Node operation, you can configure the Component's Runtime Config information as needed.
-
-
-
- |
-
- |
+- Components define node behavior. Official components and custom components are supported.
+- Configure runtime settings after adding a node.
+
+
+ |
+ |
+
### Slot
-- The generation of different types of Components will rely on other components, using them as part of their own configuration dependencies. This part of the dependency is called Slot.
-- For example, the official volc_vikingDB component relies on the Embedding Component as a Slot. Similarly, the official ToolsNode component relies on multiple Tool Components.
-
-
-
- |
-
- |
+- Some components depend on other components — these dependencies are called slots.
+- For example, `volc_vikingDB` depends on an `Embedding` component; `ToolsNode` depends on multiple `Tool` components.
-## **Start Orchestrating**
+
+ |
+ |
+
-### **Initialize Plugin**
+## Start Orchestrating
-Click to enter the Eino Dev plugin, the following interface will be displayed. Click the circled box in the image to enter the orchestration.
+### Initialize the Plugin
-
+Open Eino Dev and click into orchestration.
-### **Create and Orchestrate Graph**
+
-- A new Graph is added at the bottom left corner of the interface. Fill in the relevant Graph configuration in the pop-up dialog box to generate the Graph canvas.
-- Select suitable Node components from AddNodes as needed and add them to the canvas.
-- Connect the Node components according to the business orchestration logic to complete the Graph business orchestration logic.
+### Create and Orchestrate a Graph
-
-
-
- |
-
- |
+- Add a Graph via the bottom-left control; fill the dialog to create the canvas.
+- Use “AddNodes” to add nodes as needed.
+- Connect nodes according to business logic.
-
-
- |
-
- |
+ |
+ |
+ |
+ |
+
-- Click "Generate as code" and select the appropriate folder to generate the orchestrated Graph code and save it to the specified path.
+- Click “Generate as code”, choose a folder, and save the generated Graph code.
-
-
- |
-
- |
+ |
+ |
+
-- Especially when the added Component is of Graph type, the nested Graph can be expanded to configure each Node component. After configuration, return to the home interface through the top breadcrumb path.
+- When adding a Graph component as a node, expand the nested Graph to configure its nodes; use the breadcrumb to return to the top-level after configuration.
-
-
- |
-
- |
+ |
+ |
+
-##
diff --git a/content/en/docs/eino/core_modules/eino_adk/_index.md b/content/en/docs/eino/core_modules/eino_adk/_index.md
new file mode 100644
index 00000000000..8affbe750b5
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/_index.md
@@ -0,0 +1,31 @@
+---
+Description: ""
+date: "2025-12-03"
+lastmod: ""
+tags: []
+title: 'Eino: ADK - Agent Development Kit'
+weight: 4
+---
+
+Eino ADK is a Go-first Agent and Multi-Agent development kit. It provides:
+
+- Unified Agent abstractions with event-driven outputs
+- Collaboration primitives (Sequential, Parallel, Loop, Supervisor, Plan-Execute)
+- Runner-based execution with callbacks, interrupts, and checkpoints
+- Tooling to integrate with Eino components and Graph/Chain orchestration
+
+Start here:
+
+- Overview: [Eino ADK: Overview](/docs/eino/core_modules/eino_adk/agent_preview)
+- Interface: [Eino ADK: Agent Interface](/docs/eino/core_modules/eino_adk/agent_interface)
+- Quickstart: [Eino ADK: Quickstart](/docs/eino/core_modules/eino_adk/agent_quickstart)
+- Collaboration: [Eino ADK: Agent Collaboration](/docs/eino/core_modules/eino_adk/agent_collaboration)
+- Runner & Extensions: [Eino ADK: Agent Runner & Extensions](/docs/eino/core_modules/eino_adk/agent_extension)
+- HITL: [Eino ADK: Human-in-the-Loop](/docs/eino/core_modules/eino_adk/agent_hitl)
+- Implementations:
+ - [ChatModelAgent](/docs/eino/core_modules/eino_adk/agent_implementation/chat_model)
+ - [Plan-Execute](/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute)
+ - [Supervisor](/docs/eino/core_modules/eino_adk/agent_implementation/supervisor)
+ - [DeepAgents](/docs/eino/core_modules/eino_adk/agent_implementation/deepagents)
+ - [Workflow](/docs/eino/core_modules/eino_adk/agent_implementation/workflow)
+
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_collaboration.md b/content/en/docs/eino/core_modules/eino_adk/agent_collaboration.md
new file mode 100644
index 00000000000..6ab904943ea
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_collaboration.md
@@ -0,0 +1,451 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Agent Collaboration'
+weight: 4
+---
+
+# Collaboration Overview
+
+ADK defines collaboration and composition primitives to build multi‑agent systems. This page introduces collaboration modes, input context strategies, decision autonomy, and composition types with code and diagrams.
+
+## Collaboration Primitives
+
+- Collaboration mode
+
+
+ | Mode | Description |
+ | Transfer | Hand off the task to another agent. The current agent exits and does not wait for the child agent’s completion. |
+ | ToolCall (AgentAsTool) | Treat an agent as a tool, wait for its response, and use the output for subsequent processing. |
+
+
+- Input context strategy
+
+
+ | Strategy | Description |
+ | Upstream full dialogue | Receive complete conversation history from upstream agents. |
+ | New task description | Ignore upstream dialogue and generate a fresh summary as input for the child agent. |
+
+
+- Decision autonomy
+
+
+ | Autonomy | Description |
+ | Autonomous | Agent internally selects downstream agents (often via LLM) when needed. Even preset logic is considered autonomous from the outside. |
+ | Preset | Agent execution order is predetermined and predictable. |
+
+
+## Composition Types
+
+
+| Type | Description | Diagram | Collab | Context | Decision |
+| SubAgents | Build a parent agent with a list of named subagents, forming a tree. Agent names must be unique within the tree. |  | Transfer | Upstream full dialogue | Autonomous |
+| Sequential | Run subagents in order once, then finish. |  | Transfer | Upstream full dialogue | Preset |
+| Parallel | Run subagents concurrently over shared input; finish after all complete. |  | Transfer | Upstream full dialogue | Preset |
+| Loop | Run subagents in sequence repeatedly until termination. |  | Transfer | Upstream full dialogue | Preset |
+| AgentAsTool | Convert an agent into a tool callable by others. `ChatModelAgent` supports this directly. |  | ToolCall | New task description | Autonomous |
+
+
+## Context Passing
+
+ADK provides two core mechanisms: History and SessionValues.
+
+### History
+
+Concept
+
+- History corresponds to the “Upstream full dialogue” strategy. Every `AgentEvent` produced by upstream agents is saved into History.
+- When invoking a new Agent (Workflow/Transfer), History events are converted and appended to that Agent’s `AgentInput`.
+
+By default, assistant/tool messages from other agents are converted into user messages. This tells the current LLM: “Agent_A called some_tool with some_result. Now it’s your turn to decide.” It treats other agents’ behavior as external information rather than the current agent’s own actions, avoiding role confusion.
+
+
+
+Filtering by RunPath
+
+- When building `AgentInput` for an Agent, only include History events whose `RunPath` belong to the current Agent’s `RunPath` (equal or prefix).
+
+Definition: RunPathA “belongs to” RunPathB when RunPathA equals RunPathB or RunPathA is a prefix of RunPathB.
+
+Examples of RunPath in different orchestrations:
+
+
+| Example | RunPath |
+ | Agent: [Agent]SubAgent: [Agent, SubAgent] |
+ | Agent: [Agent]Agent (after function call): [Agent] |
+ | Agent1: [SequentialAgent, LoopAgent, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2]Agent1: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1, Agent2]Agent3: [SequentialAgent, LoopAgent, Agent3]Agent4: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent4]Agent5: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent5]Agent6: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent6] |
+ | Agent: [Agent]SubAgent: [Agent, SubAgent]Agent: [Agent, SubAgent, Agent] |
+
+
+Customize via `WithHistoryRewriter`:
+
+```go
+type HistoryRewriter func(ctx context.Context, entries []*HistoryEntry) ([]Message, error)
+func WithHistoryRewriter(h HistoryRewriter) AgentOption
+```
+
+### SessionValues
+
+- Global KV store per run; thread‑safe helpers:
+
+```go
+func GetSessionValues(ctx context.Context) map[string]any
+func AddSessionValues(ctx context.Context, kvs map[string]any)
+func GetSessionValue(ctx context.Context, key string) (any, bool)
+func AddSessionValue(ctx context.Context, key string, value any)
+```
+
+Note: SessionValues are implemented via Context. Runner re‑initializes Context when starting a run, so calling `AddSessionValues` or `AddSessionValue` outside of `Runner.Run` will not take effect.
+
+Inject initial values on run:
+
+```go
+runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent})
+iterator := runner.Run(ctx, []adk.Message{schema.UserMessage("xxx")},
+ adk.WithSessionValues(map[string]any{
+ PlanSessionKey: 123,
+ UserInputSessionKey: []adk.Message{schema.UserMessage("yyy")},
+ }),
+)
+```
+
+## Transfer SubAgents
+
+- Emit a `Transfer` action to hand off control:
+
+```go
+event := adk.NewTransferToAgentAction("dest agent name")
+```
+
+- Register subagents before running:
+
+```go
+func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error)
+```
+
+- Notes:
+ - Transfer hands off control; parent does not summarize after child ends
+ - Child receives original input; parent output is context only
+
+-- Agents can implement `OnSubAgents` to handle registration callbacks.
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+type OnSubAgents interface {
+ OnSetSubAgents(ctx context.Context, subAgents []Agent) error
+ OnSetAsSubAgent(ctx context.Context, parent Agent) error
+ OnDisallowTransferToParent(ctx context.Context) error
+}
+```
+
+## Example: Weather + Chat via Transfer
+
+Three `ChatModelAgent`s: a router, a weather agent (tool‑enabled), and a general chat agent. The router uses Transfer to hand off requests based on intent.
+
+```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.",
+ 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.",
+ 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
+}
+```
+
+Wire them together and run:
+
+```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)
+ }
+ }
+}
+```
+
+Sample output:
+
+```yaml
+>>>>>>>>>query weather<<<<<<<<<
+Agent[RouterAgent]:
+assistant:
+tool_calls:
+{Index: ID:call_SKNsPwKCTdp1oHxSlAFt8sO6 Type:function Function:{Name:transfer_to_agent Arguments:{"agent_name":"WeatherAgent"}} Extra:map[]}
+
+finish_reason: tool_calls
+usage: &{201 17 218}
+======
+Agent[RouterAgent]: transfer to WeatherAgent
+======
+Agent[WeatherAgent]:
+assistant:
+tool_calls:
+{Index: ID:call_QMBdUwKj84hKDAwMMX1gOiES Type:function Function:{Name:get_weather Arguments:{"city":"Beijing"}} Extra:map[]}
+
+finish_reason: tool_calls
+usage: &{255 15 270}
+======
+Agent[WeatherAgent]:
+tool: the temperature in Beijing is 25°C
+tool_call_id: call_QMBdUwKj84hKDAwMMX1gOiES
+tool_call_name: get_weather
+======
+Agent[WeatherAgent]:
+assistant: The current temperature in Beijing is 25°C.
+finish_reason: stop
+usage: &{286 11 297}
+======
+
+>>>>>>>>>failed to route<<<<<<<<<
+Agent[RouterAgent]:
+assistant: I'm unable to assist with booking flights. Please use a relevant travel service or booking platform to make your reservation.
+finish_reason: stop
+usage: &{206 23 229}
+======
+```
+
+Other `OnSubAgents` callbacks when registering child agents:
+
+- `OnSetAsSubAgent`: register parent info to the agent
+- `OnDisallowTransferToParent`: when `WithDisallowTransferToParent` is set, inform the agent not to transfer back to its parent
+
+```go
+adk.SetSubAgents(
+ ctx,
+ Agent1,
+ []adk.Agent{
+ adk.AgentWithOptions(ctx, Agent2, adk.WithDisallowTransferToParent()),
+ },
+)
+```
+
+### Deterministic Transfer
+
+Wrap a subagent so it automatically transfers back to a target (e.g., supervisor) after finishing:
+
+```go
+type DeterministicTransferConfig struct {
+ Agent Agent
+ ToAgentNames []string
+}
+
+func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent
+```
+
+Used by Supervisor to ensure subagents return control deterministically:
+
+
+
+```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 runs agents according to a preset flow. ADK provides three base Workflow agents: Sequential, Parallel, and Loop. They can be nested to build more complex tasks.
+
+By default, each agent’s input in a Workflow is generated via the History mechanism described earlier; you can customize `AgentInput` generation with `WithHistoryRewriter`.
+
+When an agent emits an `ExitAction` event, the Workflow agent exits immediately, regardless of remaining agents.
+
+See details and examples: `/docs/eino/core_modules/eino_adk/agent_implementation/workflow`
+
+### SequentialAgent
+
+Run a series of agents in the provided order:
+
+
+
+```go
+type SequentialAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+}
+
+func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
+```
+
+### LoopAgent
+
+Based on SequentialAgent; after completing one run, it starts from the beginning again:
+
+
+
+```go
+type LoopAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+
+ MaxIterations int
+}
+
+func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
+```
+
+### ParallelAgent
+
+Run agents concurrently:
+
+
+
+```go
+type ParallelAgentConfig struct {
+ Name string
+ Description string
+ SubAgents []Agent
+}
+
+func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
+```
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_extension.md b/content/en/docs/eino/core_modules/eino_adk/agent_extension.md
new file mode 100644
index 00000000000..a2cb5b62ee4
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_extension.md
@@ -0,0 +1,113 @@
+---
+Description: ""
+date: "2025-11-20"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Agent Runner & Extensions'
+weight: 3
+---
+
+# Agent Runner
+
+## Definition
+
+Runner is the core engine that executes Agents in Eino ADK. It manages the full lifecycle: multi‑agent collaboration, context passing, and cross‑cutting aspects like interrupt and callback. Any Agent should run via Runner.
+
+## Interrupt & Resume
+
+Runner provides runtime interrupt and resume. It allows a running Agent to actively interrupt execution and persist current state, then resume from the breakpoint. Commonly used when external input is needed, long waits occur, or workflows are pausable.
+
+Three key points in one interrupt→resume process:
+
+1. Interrupted Action: emitted by Agent, intercepted by Runner
+2. Checkpoint: after intercepting, Runner persists current state
+3. Resume: when conditions are ready, Runner resumes from the breakpoint
+
+### Interrupted Action
+
+During execution, an Agent can actively interrupt Runner by emitting an AgentEvent containing Interrupted Action. Runner treats an event as interrupted when `Interrupted` is non‑nil:
+
+```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
+}
+```
+
+When interruption occurs, use `InterruptInfo` to attach custom data. This data:
+
+1. Is returned to the caller to explain the interruption reason
+2. Will be passed back to the interrupted Agent on resume, so the Agent can recover based on it
+
+```go
+// e.g., ChatModelAgent emits the following AgentEvent on interrupt:
+h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{
+ Interrupted: &InterruptInfo{
+ Data: &ChatModelAgentInterruptInfo{Data: data, Info: info},
+ },
+}})
+```
+
+### State Persistence (Checkpoint)
+
+Runner terminates the current run after capturing an event with Interrupted Action. If:
+
+1. CheckPointStore is set in Runner
+
+```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)
+}
+```
+
+2. WithCheckPointID is passed when calling Runner
+
+```go
+// github.com/cloudwego/eino/adk/interrupt.go
+func WithCheckPointID(id string) _AgentRunOption_
+```
+
+Runner persists current state (original input, history, etc.) and the Agent’s InterruptInfo into CheckPointStore under CheckPointID.
+
+> To preserve interface concrete types, Eino ADK uses gob (https://pkg.go.dev/encoding/gob) to serialize runtime state. For custom types, register via gob.Register or gob.RegisterName (prefer the latter; the former uses path+type name as default identifiers, so both location and name must remain unchanged). Eino auto‑registers framework built‑in types.
+
+### Resume
+
+```go
+// github.com/cloudwego/eino/adk/runner.go
+func (r *Runner) Resume(ctx context.Context, checkPointID string, opts ...AgentRunOption) (*AsyncIterator[*AgentEvent], error)
+```
+
+Resume requires the interrupted Agent to implement `ResumableAgent`. Runner reads state from CheckPointStore and resumes; `InterruptInfo` and last run’s `EnableStreaming` are provided as inputs:
+
+```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_
+}
+```
+
+If Resume needs to pass new inputs to the Agent, define AgentRunOption and supply it when calling Runner.Resume.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_hitl.md b/content/en/docs/eino/core_modules/eino_adk/agent_hitl.md
new file mode 100644
index 00000000000..3ccd7d8604c
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_hitl.md
@@ -0,0 +1,697 @@
+---
+Description: ""
+date: "2025-12-11"
+lastmod: ""
+tags: []
+title: 'Eino Human-in-the-Loop: Architecture Guide'
+weight: 6
+---
+
+## Overview
+
+Eino’s HITL framework provides robust interrupt/resume and an addressing system to route user approvals or inputs back to the exact interruption point.
+
+Covers:
+
+- Developer: when to interrupt, what to persist, what to expose
+- Framework: where interruption happens, how to persist context/state
+- User: where interruption occurred, whether/how to resume, what data to provide
+
+## Quick Start
+
+Example: a ticket‑booking `ChatModelAgent` that pauses for user approval before calling a booking tool.
+
+1) Create a `ChatModelAgent` with an approvable tool (decorator adds approval interrupt):
+
+```go
+getWeather := &tool2.InvokableApprovableTool{InvokableTool: baseBookTool}
+a, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ /* model + getWeather tool */ })
+```
+
+2) Create a `Runner` with `CheckPointStore`, run with `WithCheckPointID("1")`:
+
+```go
+runner := adk.NewRunner(ctx, adk.RunnerConfig{ Agent: a, EnableStreaming: true, CheckPointStore: store.NewInMemoryStore() })
+iter := runner.Query(ctx, "book a ticket for Martin...", adk.WithCheckPointID("1"))
+```
+
+3) Read `event.Action.Interrupted`; capture `interruptID` and show user info; collect approval result.
+
+```go
+interruptID := lastEvent.Action.Interrupted.InterruptContexts[0].ID
+```
+
+4) Resume with parameters mapping `interruptID` → approval result:
+
+```go
+iter, err := runner.ResumeWithParams(ctx, "1", &adk.ResumeParams{ Targets: map[string]any{ interruptID: approvalResult } })
+```
+
+For production, use a distributed store (e.g., Redis) and expose interrupt context via your API/UI.
+
+## APIs
+
+- Create interrupts: `adk.Interrupt`, `adk.StatefulInterrupt`, `adk.CompositeInterrupt`
+- Access interrupt info: `InterruptInfo` with a flat list of `InterruptCtx{ID, Address, Info, IsRootCause, Parent}`
+- Resume: `(*Runner).ResumeWithParams(ctx, checkPointID, params)` and `ResumeInfo` for agents
+- Compose‑level helpers: `compose.Interrupt`, `compose.StatefulInterrupt`, `compose.CompositeInterrupt`, `compose.ExtractInterruptInfo`, and resume context helpers
+
+Examples repository: https://github.com/cloudwego/eino-examples/pull/125 and the HITL series under `adk/human-in-the-loop`.
+
+## HITL Needs
+
+The following diagram illustrates the key questions each party must answer during interrupt/resume. Understanding these needs explains the architecture design choices.
+
+```mermaid
+graph TD
+ subgraph P1 [Interrupt Phase]
+ direction LR
+ subgraph Dev1 [Developer]
+ direction TB
+ D1[Should I interrupt now?
Was I interrupted before?]
+ D2[What information should users
see about this interrupt?]
+ D3[What state should I persist
for later resume?]
+ D1 --> D2 --> D3
+ end
+
+ subgraph Fw1 [Framework]
+ direction TB
+ F1[Where in the execution
+ did the interrupt occur?]
+ F2[How to associate state with
+ the interrupt location?]
+ F3[How to persist interrupt
+ context and state?]
+ F4[What information does the user
+ need to understand the interrupt?]
+ F1 --> F2 --> F3 --> F4
+ end
+
+ Dev1 --> Fw1
+ end
+
+ subgraph P2 [User Decision Phase]
+ direction TB
+ subgraph "End User"
+ direction TB
+ U1[Where in the flow did
+ the interrupt occur?]
+ U2[What information did the
+ developer provide?]
+ U3[Should I resume this
+ interrupt?]
+ U4[Should I provide data
+ for resume?]
+ U5[What type of resume
+ data should I provide?]
+ U1 --> U2 --> U3 --> U4 --> U5
+ end
+ end
+
+ subgraph P3 [Resume Phase]
+ direction LR
+ subgraph Fw2 [Framework]
+ direction TB
+ FR1[Which entity is interrupted
+ and how to re-run it?]
+ FR2[How to restore context
+ for the interrupted entity?]
+ FR3[How to route user data
+ to the interrupted entity?]
+ FR1 --> FR2 --> FR3
+ end
+
+ subgraph Dev2 [Developer]
+ direction TB
+ DR1[Am I the explicit
+ resume target?]
+ DR2[If not, should I
+ re-interrupt to continue?]
+ DR3[What state did I
+ persist on interrupt?]
+ DR4[If resume data is provided,
+ how should I process it?]
+ DR1 --> DR2 --> DR3 --> DR4
+ end
+
+ Fw2 --> Dev2
+ end
+
+ P1 --> P2 --> P3
+
+ classDef dev fill:#e1f5fe
+ classDef fw fill:#f3e5f5
+ classDef user fill:#e8f5e8
+
+ class D1,D2,D3,DR1,DR2,DR3,DR4 dev
+ class F1,F2,F3,F4,FR1,FR2,FR3 fw
+ class U1,U2,U3,U4,U5 user
+```
+
+Goals:
+
+1. Help developers answer these questions easily
+2. Help end users answer these questions easily
+3. Enable the framework to answer these questions automatically and out‑of‑the‑box
+
+## Quickstart (Full)
+
+We demonstrate a ticket‑booking agent that pauses for user approval before booking. Full code: https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/1_approval
+
+1) Create a `ChatModelAgent` and configure a booking tool wrapped with approval interrupt:
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/compose"
+
+ "github.com/cloudwego/eino-examples/adk/common/model"
+ tool2 "github.com/cloudwego/eino-examples/adk/common/tool"
+)
+
+func NewTicketBookingAgent() adk.Agent {
+ ctx := context.Background()
+
+ type bookInput struct {
+ Location string `json:"location"`
+ PassengerName string `json:"passenger_name"`
+ PassengerPhoneNumber string `json:"passenger_phone_number"`
+ }
+
+ getWeather, err := utils.InferTool(
+ "BookTicket",
+ "this tool can book ticket of the specific location",
+ func(ctx context.Context, input bookInput) (output string, err error) {
+ return "success", nil
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "TicketBooker",
+ Description: "An agent that can book tickets",
+ Instruction: `You are an expert ticket booker.
+Based on the user's request, use the "BookTicket" tool to book tickets.`,
+ Model: model.NewChatModel(),
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{
+ // InvokableApprovableTool is a decorator from eino-examples
+ // that adds approval interrupt to any InvokableTool
+ &tool2.InvokableApprovableTool{InvokableTool: getWeather},
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatal(fmt.Errorf("failed to create chatmodel: %w", err))
+ }
+
+ return a
+}
+```
+
+2) Create a Runner, configure `CheckPointStore`, run with a `CheckPointID`:
+
+```go
+a := NewTicketBookingAgent()
+runner := adk.NewRunner(ctx, adk.RunnerConfig{
+ EnableStreaming: true, // you can disable streaming here
+ Agent: a,
+
+ // provide a CheckPointStore for eino to persist the execution state of the agent for later resumption.
+ // Here we use an in-memory store for simplicity.
+ // In the real world, you can use a distributed store like Redis to persist the checkpoints.
+ CheckPointStore: store.NewInMemoryStore(),
+})
+iter := runner.Query(ctx, "book a ticket for Martin, to Beijing, on 2025-12-01, the phone number is 1234567. directly call tool.", adk.WithCheckPointID("1"))
+```
+
+3) Capture interrupt info and `interruptID`:
+
+```go
+var lastEvent *adk.AgentEvent
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+
+ prints.Event(event)
+
+ lastEvent = event
+}
+
+// this interruptID is crucial 'locator' for Eino to know where the interrupt happens,
+// so when resuming later, you have to provide this same `interruptID` along with the approval result back to Eino
+interruptID := lastEvent.Action.Interrupted.InterruptContexts[0].ID
+```
+
+4) Show interrupt info to the user and collect approval:
+
+```go
+var apResult *tool.ApprovalResult
+for {
+ scanner := bufio.NewScanner(os.Stdin)
+ fmt.Print("your input here: ")
+ scanner.Scan()
+ fmt.Println()
+ nInput := scanner.Text()
+ if strings.ToUpper(nInput) == "Y" {
+ apResult = &tool.ApprovalResult{Approved: true}
+ break
+ } else if strings.ToUpper(nInput) == "N" {
+ // Prompt for reason when denying
+ fmt.Print("Please provide a reason for denial: ")
+ scanner.Scan()
+ reason := scanner.Text()
+ fmt.Println()
+ apResult = &tool.ApprovalResult{Approved: false, DisapproveReason: &reason}
+ break
+ }
+
+ fmt.Println("invalid input, please input Y or N")
+}
+```
+
+Sample output:
+
+```json
+name: TicketBooker
+path: [{TicketBooker}]
+tool name: BookTicket
+arguments: {"location":"Beijing","passenger_name":"Martin","passenger_phone_number":"1234567"}
+
+name: TicketBooker
+path: [{TicketBooker}]
+tool 'BookTicket' interrupted with arguments '{"location":"Beijing","passenger_name":"Martin","passenger_phone_number":"1234567"}', waiting for your approval, please answer with Y/N
+
+your input here: Y
+```
+
+5) Resume with `Runner.ResumeWithParams`, mapping `interruptID` to approval result:
+
+```go
+// here we directly resumes right in the same instance where the original `Runner.Query` happened.
+// In the real world, the original `Runner.Run/Query` and the subsequent `Runner.ResumeWithParams`
+// can happen in different processes or machines, as long as you use the same `CheckPointID`,
+// and you provided a distributed `CheckPointStore` when creating the `Runner` instance.
+iter, err := runner.ResumeWithParams(ctx, "1", &adk.ResumeParams{
+ Targets: map[string]any{
+ interruptID: apResult,
+ },
+})
+if err != nil {
+ log.Fatal(err)
+}
+for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Fatal(event.Err)
+ }
+
+ prints.Event(event)
+}
+```
+
+Full sample output:
+
+```yaml
+name: TicketBooker
+path: [{TicketBooker}]
+tool name: BookTicket
+arguments: {"location":"Beijing","passenger_name":"Martin","passenger_phone_number":"1234567"}
+
+name: TicketBooker
+path: [{TicketBooker}]
+tool 'BookTicket' interrupted with arguments '{"location":"Beijing","passenger_name":"Martin","passenger_phone_number":"1234567"}', waiting for your approval, please answer with Y/N
+
+your input here: Y
+
+name: TicketBooker
+path: [{TicketBooker}]
+tool response: success
+
+name: TicketBooker
+path: [{TicketBooker}]
+answer: The ticket for Martin to Beijing on 2025-12-01 has been successfully booked. If you need any more assistance, feel free
+ to ask!
+```
+
+## Architecture Overview
+
+High‑level interrupt/resume flow:
+
+```mermaid
+flowchart TD
+ U[End User]
+
+ subgraph R [Runner]
+ Run
+ Resume
+ end
+
+ U -->|Initial Input| Run
+ U -->|Resume Data| Resume
+
+ subgraph E [(nested) Entities]
+ Agent
+ Tool
+ ...
+ end
+
+ subgraph C [Run Context]
+ Address
+ InterruptState
+ ResumeData
+ end
+
+ Run -->|any number of transfer / call| E
+ R <-->|persist/restore| C
+ Resume -->|replay transfer / call| E
+ C -->|auto assigned to| E
+```
+
+Time‑ordered interactions:
+
+```mermaid
+sequenceDiagram
+ participant D as Developer
+ participant F as Framework
+ participant U as End User
+
+
+ Note over D,F: 1. Interrupt Phase
+ D->>F: call StatefulInterrupt()
specify info and state
+ F->>F: persist InterruptID->{address, state}
+
+
+ Note over F,U: 2. User Decision Phase
+ F->>U: emit InterruptID->{address, info}
+ U->>U: decide InterruptID->{resume data}
+ U->>F: call TargetedResume()
provide InterruptID->{resume data}
+
+
+ Note over D,F: 3. Resume Phase
+ F->>F: route to interrupted entity
+ F->>D: provide state and resume data
+ D->>D: process resume
+```
+
+## ADK Package APIs
+
+### 1) Interrupt APIs
+
+#### `Interrupt`
+
+```go
+func Interrupt(ctx context.Context, info any) *AgentEvent
+```
+
+#### `StatefulInterrupt`
+
+```go
+func StatefulInterrupt(ctx context.Context, info any, state any) *AgentEvent
+```
+
+#### `CompositeInterrupt`
+
+```go
+func CompositeInterrupt(ctx context.Context, info any, state any,
+ subInterruptSignals ...*InterruptSignal) *AgentEvent
+```
+
+### 2) Accessing Interrupt Info
+
+`InterruptInfo` contains a list of `InterruptCtx` entries (one per interrupt point):
+
+```go
+type InterruptCtx struct {
+ ID string // fully qualified address for targeted resume
+ Address Address // structured address segments
+ Info any // user-facing information
+ IsRootCause bool
+ Parent *InterruptCtx
+}
+```
+
+### 3) User‑Directed Resume
+
+```go
+func (r *Runner) ResumeWithParams(ctx context.Context, checkPointID string,
+ params *ResumeParams, opts ...AgentRunOption) (*AsyncIterator[*AgentEvent], error)
+```
+
+### 4) Developer‑Side Resume
+
+`ResumeInfo` holds all necessary information:
+
+```go
+type ResumeInfo struct {
+ WasInterrupted bool
+ InterruptState any
+ IsResumeTarget bool
+ ResumeData any
+ // ... other fields
+}
+```
+
+## Compose Package APIs
+
+### Interrupt Helpers
+
+```go
+func Interrupt(ctx context.Context, info any) error
+func StatefulInterrupt(ctx context.Context, info any, state any) error
+func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error) error
+```
+
+### Extracting Interrupt Info
+
+```go
+composeInfo, ok := compose.ExtractInterruptInfo(err)
+if ok {
+ interruptContexts := composeInfo.InterruptContexts
+}
+```
+
+### Resume Context Helpers
+
+```go
+func Resume(ctx context.Context, interruptIDs ...string) context.Context
+func ResumeWithData(ctx context.Context, interruptID string, data any) context.Context
+func BatchResumeWithData(ctx context.Context, resumeData map[string]any) context.Context
+```
+
+### Developer‑Side Helpers
+
+```go
+func GetInterruptState[T any](ctx context.Context) (wasInterrupted bool, hasState bool, state T)
+func GetResumeContext[T any](ctx context.Context) (isResumeFlow bool, hasData bool, data T)
+```
+
+## Underlying Architecture: Addressing System
+
+### Address Needs
+
+1) Attach local state to interruption points (stable, unique locator)
+2) Targeted resume (precise identification)
+3) Interrupt localization (explain to end users)
+
+### Address Structure
+
+```go
+type Address struct {
+ Segments []AddressSegment
+}
+
+type AddressSegment struct {
+ Type AddressSegmentType
+ ID string
+ SubID string
+}
+```
+
+ADK‑level view (agent‑centric simplified):
+
+```mermaid
+graph TD
+ A[Address] --> B[AddressSegment 1]
+ A --> C[AddressSegment 2]
+ A --> D[AddressSegment 3]
+
+ B --> B1[Type: Agent]
+ B --> B2[ID: A]
+
+ C --> C1[Type: Agent]
+ C --> C2[ID: B]
+
+ D --> D1[Type: Tool]
+ D --> D2[ID: search_tool]
+ D --> D3[SubID: 1]
+```
+
+Compose‑level view (full hierarchy):
+
+```mermaid
+graph TD
+ A[Address] --> B[AddressSegment 1]
+ A --> C[AddressSegment 2]
+ A --> D[AddressSegment 3]
+ A --> E[AddressSegment 4]
+
+ B --> B1[Type: Runnable]
+ B --> B2[ID: my_graph]
+
+ C --> C1[Type: Node]
+ C --> C2[ID: sub_graph]
+
+ D --> D1[Type: Node]
+ D --> D2[ID: tools_node]
+
+ E --> E1[Type: Tool]
+ E --> E2[ID: mcp_tool]
+ E --> E3[SubID: 1]
+```
+
+ADK segment types:
+
+```go
+type AddressSegmentType = core.AddressSegmentType
+
+const (
+ AddressSegmentAgent AddressSegmentType = "agent"
+ AddressSegmentTool AddressSegmentType = "tool"
+)
+```
+
+Compose segment types:
+
+```go
+type AddressSegmentType = core.AddressSegmentType
+
+const (
+ AddressSegmentRunnable AddressSegmentType = "runnable"
+ AddressSegmentNode AddressSegmentType = "node"
+ AddressSegmentTool AddressSegmentType = "tool"
+)
+```
+
+## Backward Compatibility
+
+### Graph Interrupt Compatibility
+
+Legacy `NewInterruptAndRerunErr` / `InterruptAndRerun` continue to work with error wrapping to attach addresses:
+
+```go
+// 1) legacy tool using deprecated interrupt
+func myLegacyTool(ctx context.Context, input string) (string, error) {
+ return "", compose.NewInterruptAndRerunErr("requires user approval")
+}
+
+// 2) composite node calling legacy tool
+var legacyToolNode = compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
+ out, err := myLegacyTool(ctx, input)
+ if err != nil {
+ segment := compose.AddressSegment{Type: "tool", ID: "legacy_tool"}
+ return "", compose.WrapInterruptAndRerunIfNeeded(ctx, segment, err)
+ }
+ return out, nil
+})
+
+// 3) end user can now see full address
+_, err := graph.Invoke(ctx, input)
+if err != nil {
+ interruptInfo, exists := compose.ExtractInterruptInfo(err)
+ if exists {
+ fmt.Printf("Interrupt Address: %s\n", interruptInfo.InterruptContexts[0].Address.String())
+ }
+}
+```
+
+### Static Interrupts at Compile
+
+`WithInterruptBeforeNodes`/`WithInterruptAfterNodes` remain valid with improved state exposure via `InterruptCtx.Info`:
+
+```go
+type MyGraphState struct { SomeValue string }
+
+g := compose.NewGraph[string, string](compose.WithGenLocalState(func(ctx context.Context) *MyGraphState {
+ return &MyGraphState{SomeValue: "initial"}
+}))
+// ... add nodes ...
+
+graph, err := g.Compile(ctx, compose.WithInterruptAfterNodes([]string{"node_1"}))
+_, err = graph.Invoke(ctx, "start")
+
+interruptInfo, isInterrupt := compose.ExtractInterruptInfo(err)
+if isInterrupt {
+ interruptCtx := interruptInfo.InterruptContexts[0]
+ graphState, ok := interruptCtx.Info.(*MyGraphState)
+ if ok {
+ graphState.SomeValue = "a-new-value-from-user"
+ resumeCtx := compose.ResumeWithData(context.Background(), interruptCtx.ID, graphState)
+ result, err := graph.Invoke(resumeCtx, "start")
+ _ = result; _ = err
+ }
+}
+```
+
+### Agent Interrupt Compatibility
+
+End user view and agent developer view remain compatible:
+
+```go
+// end user view
+if event.Action != nil && event.Action.Interrupted != nil {
+ interruptInfo := event.Action.Interrupted
+ if len(interruptInfo.InterruptContexts) > 0 {
+ fmt.Printf("New structured context available: %+v\n", interruptInfo.InterruptContexts[0])
+ }
+ if chatInterrupt, ok := interruptInfo.Data.(*adk.ChatModelAgentInterruptInfo); ok {
+ fmt.Printf("Legacy ChatModelAgentInterruptInfo still accessible.\n")
+ }
+}
+
+// agent developer view
+func (a *myLegacyAgent) Resume(ctx context.Context, info *adk.ResumeInfo) *adk.AsyncIterator[*adk.AgentEvent] {
+ if info.InterruptInfo != nil {
+ if chatInterrupt, ok := info.InterruptInfo.Data.(*adk.ChatModelAgentInterruptInfo); ok {
+ fmt.Println("Resuming based on legacy InterruptInfo.Data field.")
+ }
+ }
+ // ... continue
+}
+```
+
+### Migration Advantages
+
+- Preserve legacy behavior while enabling address‑aware features when desired
+- Gradual adoption via `WrapInterruptAndRerunIfNeeded`
+- Richer `InterruptCtx` contexts with legacy `Data` still populated
+- Flexible state management for static graph interrupts
+
+## Implementation Examples
+
+See the eino‑examples repository: https://github.com/cloudwego/eino-examples/pull/125
+
+Examples include:
+
+1) Approval — explicit approval before critical tool calls
+2) Review & Edit — human review and in‑place edit of tool call params
+3) Feedback Loop — iterative improvement via human feedback
+4) Ask for Clarification — proactively request clarification or next step
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/_index.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/_index.md
new file mode 100644
index 00000000000..9c9bdd0ccbc
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/_index.md
@@ -0,0 +1,13 @@
+---
+Description: ""
+date: "2025-11-20"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Agent Implementations'
+weight: 5
+---
+
+You can implement custom agents by conforming to the Agent interface. Follow the design rules for predictable composition, iteration, and collaboration.
+
+Quick custom example: [myagent.go](https://github.com/cloudwego/eino-examples/blob/main/adk/intro/custom/myagent.go)
+
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md
new file mode 100644
index 00000000000..c7bcaea40ea
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md
@@ -0,0 +1,303 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino ADK: ChatModelAgent'
+weight: 1
+---
+
+# ChatModelAgent Overview
+
+## Import Path
+
+`import "github.com/cloudwego/eino/adk"`
+
+## What is ChatModelAgent
+
+`ChatModelAgent` is a core prebuilt Agent in Eino ADK. It encapsulates interaction with LLMs and supports tools to accomplish complex tasks.
+
+## ChatModelAgent ReAct Mode
+
+`ChatModelAgent` uses the [ReAct](https://react-lm.github.io/) pattern, designed to solve complex problems by letting the ChatModel perform explicit, step‑by‑step “thinking”. When tools are configured, its internal execution follows ReAct:
+
+- Call ChatModel (Reason)
+- LLM returns a tool‑call request (Action)
+- ChatModelAgent executes the tool (Act)
+- It sends the tool result back to ChatModel (Observation), then starts a new loop until the model decides no tool is needed and stops
+
+When no tools are configured, `ChatModelAgent` falls back to a single ChatModel call.
+
+
+
+Configure tools via `ToolsConfig`:
+
+```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
+}
+```
+
+ToolsConfig reuses Eino Graph `ToolsNodeConfig`. See: [Eino: ToolsNode & Tool Guide](/docs/eino/core_modules/components/tools_node_guide)
+
+## Configuration Fields
+
+```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
+}
+
+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
+}
+
+type GenModelInput func(ctx context.Context, instruction string, input *AgentInput) ([]Message, error)
+```
+
+- `Name`: agent name
+- `Description`: agent description
+- `Instruction`: system prompt when calling ChatModel; supports f‑string rendering
+- `Model`: ToolCallingChatModel is required to support tool calls
+- `ToolsConfig`: tool configuration
+ - Reuses Eino Graph `ToolsNodeConfig` (see: [Eino: ToolsNode & Tool Guide](/docs/eino/core_modules/components/tools_node_guide)).
+ - `ReturnDirectly`: after calling a listed tool, ChatModelAgent exits immediately with the result; if multiple tools are listed, only the first match returns. Map key is the tool name.
+- `GenModelInput`: transforms `Instruction` and `AgentInput` to ChatModel messages; default:
+ 1. Prepend `Instruction` as a System Message to `AgentInput.Messages`
+ 2. Render `SessionValues` variables into the message list
+- `OutputKey`: stores the final message’s content into `SessionValues` under `OutputKey`
+- `MaxIterations`: upper bound on the ReAct loop; default 20
+- `Exit`: special tool that causes immediate agent termination when invoked; ADK provides a default `ExitTool` implementation:
+
+```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
+}
+```
+
+## ChatModelAgent Transfer
+
+`ChatModelAgent` implements `OnSubAgents`. After `SetSubAgents`, it adds a Transfer Tool and instructs the model to call it with the target agent name when transfer is needed:
+
+```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`
+```
+
+– `Transfer Tool` sets a Transfer Event to jump to the target Agent; then ChatModelAgent exits.
+– Agent Runner receives the Transfer Event and switches execution to the target Agent.
+
+## ChatModelAgent AgentAsTool
+
+Convert an agent to a tool when clear input suffices:
+
+```go
+// github.com/cloudwego/eino/adk/agent_tool.go
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+Register the converted Agent tool in `ToolsConfig` so `ChatModelAgent` can decide when to call it.
+
+# ChatModelAgent Usage Example
+
+## Scenario
+
+Create a book recommendation Agent that can recommend relevant books based on user input.
+
+## Implementation
+
+### Step 1: Define the tool
+
+```go
+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
+}
+```
+
+### Step 2: Create ChatModel
+
+```go
+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
+}
+```
+
+### Step 3: Create ChatModelAgent
+
+```go
+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
+}
+```
+
+### Step 4: Run via Runner
+
+```go
+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)
+}
+```
+
+## Run Result
+
+```yaml
+message:
+assistant:
+tool_calls:
+{Index: ID:call_o2It087hoqj8L7atzr70EnfG Type:function Function:{Name:search_book Arguments:{"genre":"fiction","max_pages":0,"min_rating":0}} Extra:map[]}
+
+finish_reason: tool_calls
+usage: &{140 24 164}
+======
+
+
+message:
+tool: {"Books":["God's blessing on this wonderful world!"]}
+tool_call_id: call_o2It087hoqj8L7atzr70EnfG
+tool_call_name: search_book
+======
+
+
+message:
+assistant: I recommend the fiction book "God's blessing on this wonderful world!". It's a great choice for readers looking for an exciting story. Enjoy your reading!
+finish_reason: stop
+usage: &{185 31 216}
+======
+```
+
+# ChatModelAgent Interrupt & Resume
+
+## Introduction
+
+`ChatModelAgent` uses Eino Graph internally, so the agent reuses Graph’s Interrupt & Resume capability.
+
+– On interrupt, a tool returns a special error to trigger Graph interrupt and emit custom info; on resume, Graph reruns this tool:
+
+```go
+// github.com/cloudwego/eino/adk/interrupt.go
+
+func NewInterruptAndRerunErr(extra any) error
+```
+
+– On resume, pass custom `tool.Option`s to supply extra info to tools:
+
+```go
+import (
+ "github.com/cloudwego/eino/components/tool"
+)
+
+type askForClarificationOptions struct { NewInput *string }
+func WithNewInput(input string) tool.Option {
+ return tool.WrapImplSpecificOptFn(func(t *askForClarificationOptions) { t.NewInput = &input })
+}
+```
+
+## Example
+
+Add `ask_for_clarification` tool that interrupts until new input is provided, then resumes and continues.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md
new file mode 100644
index 00000000000..338bebd9c8d
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md
@@ -0,0 +1,150 @@
+---
+Description: ""
+date: "2025-12-01"
+lastmod: ""
+tags: []
+title: 'Eino ADK MultiAgent: DeepAgents'
+weight: 5
+---
+
+## DeepAgents Overview
+
+Eino ADK DeepAgents is a high-level multi-agent coordinator whose design aligns closely with LangChain’s Deep Agents concept. Think of it as Eino ADK’s practical engineering implementation of that idea: a "commander" main agent plans, decomposes, delegates, and supervises complex tasks that are executed by specialized subagents or tools.
+
+DeepAgents’ core value is automating complex, multi-step, multi-role workflows. It is not just an executor; it is a “project manager” with deep reasoning, planning, and dynamic adjustment abilities.
+
+### ImportPath
+
+Eino version must be >= v0.5.14
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/deep
+
+agent, err := deep.New(ctx, &deep.Config{})
+```
+
+### DeepAgents Structure
+
+DeepAgents uses a main agent to coordinate, plan, and delegate tasks. The main agent does not execute everything itself; it leverages a chat model and a set of tools to interact with the world or break down work to specialized subagents.
+
+
+
+Core components and relationships:
+
+- MainAgent: entry point and commander; receives the initial task, uses ReAct to call tools, and presents final results
+- ChatModel (ToolCallingChatModel): a model that supports tool calling to understand, reason, select, and call tools
+- Tools: capabilities available to MainAgent, including:
+ - WriteTodos: built-in planning tool that decomposes tasks into a structured todo list
+ - TaskTool: a special tool acting as a unified entry to call subagents
+ - CustomTools: user-defined tools for business needs
+- SubAgents: execute specific, independent subtasks with isolated context
+ - GeneralPurpose: a default subagent with the same tools as MainAgent (except TaskTool) for clean-context execution
+ - CustomSubAgents: user-defined subagents for business needs
+
+### Task Decomposition and Planning
+
+WriteTodos’ Description encodes planning principles. The main agent calls WriteTodos to add a list of subtasks into the context to guide subsequent reasoning and execution:
+
+
+
+1. Model receives user input
+2. Model calls WriteTodos with a todo list based on its Description; this call is added to context as reference
+3. Model follows the todos and calls TaskTool to complete the first todo
+4. Model calls WriteTodos again to update progress
+
+> 💡
+> For simple tasks, calling WriteTodos every time may be counterproductive. The default Description includes general positive/negative examples to avoid skipping or overusing WriteTodos. You can add prompts tailored to your scenario so WriteTodos is called at the right moments.
+
+> 💡
+> WriteTodos is enabled by default; set `WithoutWriteTodos=true` to disable it.
+
+### Delegation and SubAgents
+
+**TaskTool**
+
+All subagents bind to TaskTool. When the main agent delegates a subtask, it calls TaskTool specifying the subagent and the task. TaskTool routes the task to the chosen subagent and returns its result to the main agent. The default TaskTool Description explains general rules and appends each subagent’s Description; you can customize it via `TaskToolDescriptionGenerator`.
+
+**Context isolation**
+
+- Information transfer: MainAgent and subagents do not share context. Subagents receive only the delegated goal, not the entire task history; MainAgent receives only results, not the subagent’s internal process.
+- Avoid pollution: isolation ensures heavy tool calls and intermediate steps inside subagents do not pollute MainAgent context; the main agent only gets concise final answers.
+
+**general-purpose**
+
+DeepAgents adds a default subagent: general-purpose. It shares the same system prompt and tools as MainAgent (except TaskTool). When no specialized subagent fits, the main agent can use general-purpose to keep a clean context. Configure `WithoutGeneralSubAgent=true` to remove it.
+
+### Comparison with Other MultiAgent Patterns
+
+- Versus Supervisor (ReAct)
+ - Pros: WriteTodos improves task decomposition and planning; context isolation across agents performs better on large, multi-step tasks
+ - Cons: Planning and subagent calls add extra model requests, increasing time and tokens; poor task splitting can hurt results
+- Versus Plan‑and‑Execute
+ - Pros: Plan/RePlan are tools that the main agent can call freely, skipping unnecessary planning and often reducing calls, latency, and cost
+ - Cons: Planning and delegation happen within a single model call, demanding more model capability and more difficult prompt tuning
+
+## DeepAgents Usage Example
+
+### Scenario
+
+Excel Agent is an “Excel‑savvy assistant”. It decomposes the problem into steps, executes step by step, and validates results. It understands both the user’s question and the uploaded file, proposes a feasible plan, and chooses proper tools (system commands, generate‑and‑run Python code, web search) to accomplish the task.
+
+In real use, treat Excel Agent as an “Excel expert + automation engineer”. Given a raw spreadsheet and a target description, it proposes a plan and completes execution:
+
+- Data cleaning and formatting: deduplicate, handle nulls, normalize dates on large files
+- Analysis and report: aggregate monthly sales, pivot, and export chart report
+- Automated budgeting: compute total budget and generate department allocation
+- Data matching and merge: match and merge client tables from multiple sources into a complete database
+
+Excel Agent structure with DeepAgents:
+
+
+
+1. Add ReadFile tool to MainAgent to inspect files and aid subtask planning
+2. Add Code and WebSearch subagents: Code writes Python to operate Excel; WebSearch searches and summarizes
+
+### Code Implementation
+
+[https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
+
+### Run Result
+
+```yaml
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool name: task
+arguments: {"subagent_type":"CodeAgent","description":"Please help with a CSV file task: 1) Read questions.csv in the current directory; 2) Extract the first column; 3) Save the extracted first column to a new CSV named first_column.csv. Use pandas and ensure the code runs correctly and handles potential path issues."}
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool name: python_runner
+arguments: {"code":"```python\nimport pandas as pd\nimport os\n\n# Get current working directory and build paths\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 # Read CSV file\n df = pd.read_csv(input_path)\n # Extract the first column\n first_column = df.iloc[:, 0]\n # Save to new CSV (without index)\n first_column.to_csv(output_path, index=False)\n print(f\"Successfully saved first column to: {output_path}\")\nexcept FileNotFoundError:\n print(f\"Error: file not found {input_path}\")\nexcept Exception as e:\n print(f\"Error occurred during processing: {str(e)}\")\n```"}
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool response: Successfully saved first column to: /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: Task completed. Read `questions.csv` in the current directory, extracted the first column, and saved it to `first_column.csv`. Output path:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+
+The code handled path composition and exceptions (e.g., file missing or format errors) to ensure stability.
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+tool response: Task completed. Read `questions.csv`, extracted the first column, and saved to `first_column.csv`. Output path:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+
+Handled path composition and exception capture to ensure stable execution.
+
+name: ExcelAgent
+path: [{ExcelAgent}]
+answer: Successfully extracted the first column from `questions.csv` into `first_column.csv`. Saved at:
+
+`/Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/multiagent/deep/playground/262be931-532c-4d83-8cff-96c44b131973/first_column.csv`
+
+Path handling and exception capture (e.g., missing files or format issues) ensured completeness and stability. If you need path adjustments or different formats, let me know.
+```
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
new file mode 100644
index 00000000000..553e8d3d767
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
@@ -0,0 +1,500 @@
+---
+Description: ""
+date: "2025-12-03"
+lastmod: ""
+tags: []
+title: 'Eino ADK MultiAgent: Plan-Execute Agent'
+weight: 3
+---
+
+## Overview
+
+### Import Path
+
+`import "github.com/cloudwego/eino/adk/prebuilt/planexecute"`
+
+### What Is Plan-Execute Agent?
+
+Plan-Execute Agent in Eino ADK follows the "plan–execute–reflect" paradigm to solve complex tasks through step decomposition, execution, and dynamic adjustment. It coordinates three core agents — Planner, Executor, and Replanner — to structure tasks, call tools to execute, evaluate progress, and replan iteratively until the user's goal is achieved.
+
+
+
+It suits multi-step reasoning, tool integration, and strategy adjustment scenarios (e.g., research, complex problem solving, automated workflows). Key advantages:
+
+- Structured planning: break complex tasks into clear, executable steps
+- Iterative execution: complete single-step tasks via tool calling and accumulate results
+- Dynamic adjustment: evaluate progress to update plan or finish
+- Model/tool agnostic: works with any tool-calling model and external tools
+
+### Architecture
+
+Plan-Execute Agent is built from three core agents plus a coordinator, leveraging ChatModelAgent and Workflow Agents capabilities:
+
+
+
+#### 1. Planner
+
+- Core function: generate an initial task plan (ordered step list)
+- Implementation:
+ - Use a tool-calling model with `PlanTool` to produce steps matching a JSON Schema
+ - Or use a structured-output model to directly produce a `Plan`
+- Output: `Plan` object stored in session for later stages
+
+```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.
+ // See https://github.com/cloudwego/eino-ext/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
+
+- Core function: execute the first step in the plan, calling external tools
+- Implementation: built with `ChatModelAgent`, configured with a toolset (search, compute, DB, etc.)
+- Workflow:
+ - Load current `Plan` and executed steps from session
+ - Pick the first unexecuted step as the target
+ - Call tools to execute and store results in session
+- Key capability: supports multi-round tool calling via `MaxIterations` to complete a single step
+
+```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
+
+- Core function: evaluate progress, decide to continue (produce a new plan) or finish (return a response)
+- Implementation: use a tool-calling model configured with `PlanTool` (for new plan) and `RespondTool` (for final response)
+- Decision logic:
+ - Continue: if the goal is not met, generate a new `Plan` with remaining steps and update session
+ - Finish: if the goal is met, call `RespondTool` to produce the user response
+
+```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
+
+- Core function: compose the three agents into a "plan → execute → replan" loop
+- Implementation: combine `SequentialAgent` and `LoopAgent`:
+ - Outer `SequentialAgent`: run `Planner` once to produce the initial plan, then enter the loop
+ - Inner `LoopAgent`: iterate `Executor` and `Replanner` until done or max iterations
+
+```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
+}
+```
+
+### Workflow
+
+1. Initialize: user provides a goal, start `PlanExecuteAgent`
+2. Plan phase:
+ - `Planner` generates the initial `Plan` (step list)
+ - Store `Plan` in session (`PlanSessionKey`)
+3. Execute–replan loop (controlled by `LoopAgent`):
+ - Execute step: `Executor` takes the first step from `Plan`, calls tools, stores results in session (`ExecutedStepsSessionKey`)
+ - Reflect step: `Replanner` evaluates executed steps and results:
+ - If goal reached: call `RespondTool` to generate the final response and exit
+ - If continue: generate a new `Plan`, update session, and loop
+4. Termination: task completes (response) or reaches `MaxIterations`
+
+## Usage Example
+
+### Scenario
+
+Build a "research" agent:
+
+1. Planner: plan detailed steps for the research goal
+2. Executor: execute the first step, using a search tool if needed (duckduckgo)
+3. Replanner: evaluate results, adjust plan if insufficient, otherwise respond with a summary
+
+### Code
+
+#### 1. Initialize model and tools
+
+```go
+// Initialize an OpenAI model that supports tool calling
+func newToolCallingModel(ctx context.Context) model.ToolCallingChatModel {
+ cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: "gpt-4o", // must support tool calling
+ })
+ if err != nil {
+ log.Fatalf("failed to init model: %v", err)
+ }
+ return cm
+}
+
+// Initialize search tool (used by Executor)
+func newSearchTool(ctx context.Context) tool.BaseTool {
+ config := &duckduckgo.Config{
+ MaxResults: 20, // limit to 20 results
+ Region: duckduckgo.RegionWT,
+ Timeout: 10 * time.Second,
+ }
+ tool, err := duckduckgo.NewTextSearchTool(ctx, config)
+ if err != nil {
+ log.Fatalf("failed to init search tool: %v", err)
+ }
+ return tool
+}
+```
+
+#### 2. Create Planner
+
+```go
+func newPlanner(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ planner, err := planexecute.NewPlanner(ctx, &planexecute.PlannerConfig{
+ ToolCallingChatModel: model, // generate plan with tool-calling model
+ ToolInfo: &planexecute.PlanToolInfo, // default Plan tool schema
+ })
+ if err != nil {
+ log.Fatalf("failed to create Planner: %v", err)
+ }
+ return planner
+}
+```
+
+#### 3. Create Executor
+
+```go
+func newExecutor(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ // Configure Executor toolset (only search tool here)
+ 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 runs at most 5 times
+ })
+ if err != nil {
+ log.Fatalf("failed to create Executor: %v", err)
+ }
+ return executor
+}
+```
+
+#### 4. Create Replanner
+
+```go
+func newReplanner(ctx context.Context, model model.ToolCallingChatModel) adk.Agent {
+ replanner, err := planexecute.NewReplanner(ctx, &planexecute.ReplannerConfig{
+ ChatModel: model, // evaluate progress with tool-calling model
+ })
+ if err != nil {
+ log.Fatalf("failed to create Replanner: %v", err)
+ }
+ return replanner
+}
+```
+
+#### 5. Compose into PlanExecuteAgent
+
+```go
+func newPlanExecuteAgent(ctx context.Context) adk.Agent {
+ model := newToolCallingModel(ctx)
+
+ // Instantiate the three core agents
+ planner := newPlanner(ctx, model)
+ executor := newExecutor(ctx, model)
+ replanner := newReplanner(ctx, model)
+
+ // Compose into PlanExecuteAgent (fixed execute–replan max iterations = 10)
+ planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{
+ Planner: planner,
+ Executor: executor,
+ Replanner: replanner,
+ MaxIterations: 10,
+ })
+ if err != nil {
+ log.Fatalf("failed to compose PlanExecuteAgent: %v", err)
+ }
+ return planExecuteAgent
+}
+```
+
+#### 6. Run and Output
+
+```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)
+
+ // Create Runner to execute agent
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent, EnableStreaming: true})
+
+ // User input goal
+ userInput := []adk.Message{
+ schema.UserMessage("Research and summarize the latest developments in AI for healthcare in 2024, including key technologies, applications, and industry trends."),
+ }
+
+ // Execute and print results
+ events := runner.Run(ctx, userInput)
+ for {
+ event, ok := events.Next()
+ if !ok {
+ break
+ }
+ if event.Err != nil {
+ log.Printf("error: %v", event.Err)
+ break
+ }
+ // Print agent output (plan, execution results, final response, etc.)
+ if msg, err := event.Output.MessageOutput.GetMessage(); err == nil && msg.Content != "" {
+ log.Printf("\n=== Agent Output ===\n%s\n", msg.Content)
+ }
+ }
+}
+```
+
+### Run Result
+
+```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/2047[... 1787 chars omitted ...]
+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..."},{"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/industry-reports/artificial-intelligence-in-healthcare-market","summary":"The global artificial intelligence (AI) in healthcare market size reached USD 2[... 1994 chars omitted ...]
+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 c[... 1885 chars omitted ...]
+2025/09/08 11:48:01
+=== 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 journals, market reports, reputable news, and expert thought leadership on 2024 AI innovations, applications, and trends in healthcare.
+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 [... 2328 chars omitted ...]
+2025/09/08 11:48:28
+=== Agent:Executor Output ===
+Key technologies emerging or advancing in AI for healthcare in 2024 include:
+
+1. Machine Learning Models:
+- Advanced ML and DL technologies revolutionize disease prediction, diagnostic accuracy, and patient outcomes.
+- Explainable AI improves transparency and trust in clinical decision-making.
+- Edge AI enables real-time processing at facilities for faster diagnostics.
+
+2. Diagnostic Tools:
+- AI-enabled systems enhance speed, accuracy, and early disease detection.
+- AI in medical imaging improves abnormality detection and tailored plans.
+- NLP analysis of records and literature supports predictive analytics.
+
+3. Robotic Surgery:
+- AI-equipped medical robots increase precision and reduce invasiveness.
+- Real-time guidance and adaptive control improve safety and effectiveness.
+
+4. Personalized Medicine:
+- AI analyzes patient data to recommend customized treatments.
+- Predictive analytics identify risk factors and responses.
+- Genomics and biomarker analysis accelerate personalized drugs.
+
+5. Data Management Solutions:
+- AI platforms integrate and analyze large heterogeneous healthcare data.
+- Remote monitoring and predictive analytics optimize care and operations.
+- Secure, compliant solutions address privacy and ethics.
+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[... 1990 chars omitted ...]
+2025/09/08 11:48:46
+=== Agent:Executor Output ===
+In 2024, AI applications in healthcare advance across these areas:
+
+1. Diagnostics:
+- Faster, more accurate, cost-effective detection and planning
+- ML and generative AI improve early detection and outcomes
+- Imaging and record analysis for precise diagnostics
+- Real-world gains in accessibility and effectiveness
+
+2. Patient Care:
+- Personalized care via predictive analytics, remote monitoring, data integration
+- Generative AI aids documentation and communication
+- Tools for chronic disease management and decision support
+
+3. Drug Discovery:
+- Accelerated target optimization, safety assessment, personalized medicine
+- Generative AI and ML identify candidates and predict responses
+- Reduced costs and timelines
+
+4. Medical Imaging:
+- Better abnormality identification and customized plans
+- DL models interpret complex data with high precision
+- Earlier detection and more effective interventions
+
+5. Administration:
+- Streamlined scheduling, resource allocation, billing
+- Predictive analytics optimize workflows and patient flow
+- Improved efficiency, reduced administrative burden
+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 [... 2096 chars omitted ...]
+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 health[... 1817 chars omitted ...]
+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 subs[... 2214 chars omitted ...]
+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 sec[... 1819 chars omitted ...]
+```
+
+## Summary
+
+Plan-Execute Agent builds a closed-loop workflow of "plan–execute–reflect" to decompose complex tasks into executable steps, combine tool calling and dynamic adjustment, and improve reliability and efficiency.
+
+- Structured task decomposition: reduces cognitive load for complex problems
+- Tool integration: seamlessly connect external tools (search, compute, DB, etc.)
+- Dynamic adaptability: adjust strategy based on feedback to handle uncertainty
+
+With `PlanExecuteAgent` provided by Eino ADK, developers can quickly build agents that handle complex tasks for research analysis, office automation, customer service, and more.
+
+## FAQ
+
+### Error [NodeRunError] no tool call
+
+Planner/Replanner must generate the plan via tool calling. Check:
+
+1. The model supports forced tool calling (e.g., OpenAI `tool_choice="required"`)
+2. The eino-ext wrapper is up to date (older SDKs may not support forced tool calling)
+
+### Error [NodeRunError] unexpected tool call
+
+ChatModel registered for Replanner should not carry extra tools via `WithTools`. Clear any extra tools if present.
+
+### Error [NodeRunError] unmarshal plan error
+
+Planner/Replanner config uses both PlanTool and NewPlan to generate the plan:
+
+- PlanTool describes the Plan to the model
+- NewPlan builds the plan struct used to unmarshal the model's JSON output
+
+If this error occurs, ensure PlanTool's field descriptions match the struct returned by NewPlan, then rerun.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
new file mode 100644
index 00000000000..72c9b62d709
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
@@ -0,0 +1,412 @@
+Description: ""
+date: "2025-12-03"
+lastmod: ""
+tags: []
+title: 'Eino ADK MultiAgent: Supervisor Agent'
+weight: 4
+
+## Supervisor Agent Overview
+
+### Import Path
+
+`import "github.com/cloudwego/eino/adk/prebuilt/supervisor"`
+
+### What Is Supervisor Agent?
+
+Supervisor Agent is a centralized multi-agent collaboration pattern composed of one supervisor and multiple subagents. The Supervisor assigns tasks, monitors subagent execution, aggregates results, and decides next actions; subagents focus on executing their tasks and, when done, automatically transfer control back to the Supervisor via WithDeterministicTransferTo.
+
+
+
+This pattern fits scenarios requiring dynamic coordination among specialized agents to complete complex tasks, such as:
+
+- Research project management (Supervisor assigns research, experiments, and report writing tasks to different subagents)
+- Customer service workflows (Supervisor routes based on issue type to tech support, after‑sales, sales, etc.)
+
+### Structure
+
+- Supervisor Agent: the collaboration core with task assignment logic (rule‑based or LLM‑driven); manages subagents via `SetSubAgents`
+- SubAgents: each subagent is enhanced by WithDeterministicTransferTo with `ToAgentNames` preset to the Supervisor’s name, ensuring automatic transfer back after completion
+
+### Features
+
+1. Deterministic callback: when a subagent finishes without interruption, WithDeterministicTransferTo automatically triggers a Transfer event to hand control back to the Supervisor
+2. Centralized control: Supervisor manages subagents and dynamically adjusts assignments based on results
+3. Loose coupling: subagents can be developed, tested, and replaced independently as long as they implement `Agent` and are bound to the Supervisor
+4. Interrupt/resume support: if subagents or the Supervisor implement `ResumableAgent`, the collaboration can resume after interrupts, preserving context continuity
+
+### Run Flow
+
+1. Start: Runner triggers the Supervisor with an initial task (e.g., “Write a report on the history of LLMs”)
+2. Assign: Supervisor transfers the task to a designated subagent (e.g., ResearchAgent)
+3. Execute: Subagent performs its task and emits output events
+4. Auto‑callback: upon completion, WithDeterministicTransferTo triggers a Transfer event back to Supervisor
+5. Process result: Supervisor decides next steps (e.g., transfer to WriterAgent or output final result)
+
+## Usage Example
+
+### Scenario
+
+Build a report generation system:
+
+- Supervisor: given a topic, assigns tasks to ResearchAgent and WriterAgent, aggregates the final report
+- ResearchAgent: generates a research plan (e.g., LLM key milestones)
+- WriterAgent: writes a full report based on the plan
+
+### Code
+
+#### Step 1: Subagents
+
+```go
+// ResearchAgent: generate a research plan
+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
+}
+
+// WriterAgent: write a report based on the plan
+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
+}
+```
+
+#### Step 2: Supervisor Agent
+
+```go
+// ReportSupervisor: coordinate research and writing
+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
+}
+```
+
+#### Step 3: Compose and Run
+
+```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) Create model (e.g., GPT‑4o)
+ model, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: "YOUR_API_KEY",
+ Model: "gpt-4o",
+ })
+
+ // 2) Create subagents and supervisor
+ researchAgent := NewResearchAgent(model)
+ writerAgent := NewWriterAgent(model)
+ reportSupervisor := NewReportSupervisor(model)
+
+ // 3) Compose supervisor and subagents
+ supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{
+ Supervisor: reportSupervisor,
+ SubAgents: []adk.Agent{researchAgent, writerAgent},
+ })
+
+ // 4) Run Supervisor pattern
+ iter := supervisorAgent.Run(ctx, &adk.AgentInput{
+ Messages: []adk.Message{
+ schema.UserMessage("Write a report on the history of Large Language Models."),
+ },
+ EnableStreaming: true,
+ })
+
+ // 5) Consume event stream (print results)
+ 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===========")
+ }
+ }
+}
+```
+
+### Run Result
+
+```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, 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 TB of text, it exhibited "few-shot" and "zero-shot" learning. GPT-3’s API release introduced LLMs to developers.
+- **2022**: **ChatGPT** (GPT-3.5) brought LLMs to the public with **RLHF** alignment.
+- **2023**: Meta’s **Llama 2** opened weights for research and commercial use; OpenAI’s **GPT-4** expanded multimodality and reasoning.
+- **2023–2024**: Scaling race continued (PaLM 2, Claude 2, Mistral, Falcon); compute usage skyrocketed.
+===========
+Agent[WriterAgent]:
+
+===========
+Agent[WriterAgent]:
+successfully transferred to agent [ReportSupervisor]
+===========
+```
+
+## WithDeterministicTransferTo
+
+### What Is WithDeterministicTransferTo?
+
+WithDeterministicTransferTo is an ADK agent enhancer that injects Transfer capability. It lets developers predefine a fixed transfer path for a target agent so that, upon completion (not interrupted), it automatically generates a Transfer event to route the task to predefined destination agents. This is the foundation of the Supervisor collaboration pattern, ensuring subagents hand control back reliably to form a "assign–execute–feedback" loop.
+
+### Core Implementation
+
+#### Config
+
+```go
+// Wrapper
+func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent
+
+// Config
+type DeterministicTransferConfig struct {
+ Agent Agent // target agent to enhance
+ ToAgentNames []string // destination agent names to transfer to after completion
+}
+```
+
+- Agent: the original agent to be enhanced
+- ToAgentNames: destination agent names (in order) to transfer to when the agent completes without interruption
+
+#### Agent Wrapping
+
+WithDeterministicTransferTo wraps the original agent. Depending on whether it implements `ResumableAgent`, it returns `agentWithDeterministicTransferTo` or `resumableAgentWithDeterministicTransferTo`, preserving compatibility with original capabilities (like `Resume`). The wrapper overrides `Run` (and `Resume` for resumable agents) to append Transfer events after the original event stream.
+
+```go
+// Wrapper for a regular Agent
+type agentWithDeterministicTransferTo struct {
+ agent Agent // original agent
+ toAgentNames []string // destination agent names
+}
+
+// Run: execute original agent and append Transfer after completion
+func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] {
+ aIter := a.agent.Run(ctx, input, options...)
+
+ iterator, generator := NewAsyncIteratorPair[*AgentEvent]()
+
+ // asynchronously forward original events and append Transfer
+ go appendTransferAction(ctx, aIter, generator, a.toAgentNames)
+
+ return iterator
+}
+```
+
+For `ResumableAgent`, implement `Resume` to ensure deterministic transfer on resume completion:
+
+```go
+type resumableAgentWithDeterministicTransferTo struct {
+ agent ResumableAgent // resumable original agent
+ toAgentNames []string // destination agent names
+}
+
+// Resume: resume original agent and append Transfer after completion
+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
+}
+```
+
+#### Append Transfer Event
+
+`appendTransferAction` consumes the original agent’s event stream and, when the task finishes without interruption, generates and sends Transfer events to destination agents:
+
+```go
+func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent], generator *AsyncGenerator[*AgentEvent], toAgentNames []string) {
+ defer func() {
+ // panic handling: capture and send as error event
+ if panicErr := recover(); panicErr != nil {
+ generator.Send(&AgentEvent{Err: safe.NewPanicErr(panicErr, debug.Stack())})
+ }
+ generator.Close() // close generator when stream ends
+ }()
+
+ interrupted := false
+
+ // 1) forward all original events
+ for {
+ event, ok := aIter.Next()
+ if !ok {
+ break
+ }
+ generator.Send(event)
+
+ // check interrupt (e.g., InterruptAction)
+ if event.Action != nil && event.Action.Interrupted != nil {
+ interrupted = true
+ } else {
+ interrupted = false
+ }
+ }
+
+ // 2) if not interrupted and destinations exist, generate Transfer events
+ if !interrupted && len(toAgentNames) > 0 {
+ for _, toAgentName := range toAgentNames {
+ // generate assistant tip and transfer action messages
+ aMsg, tMsg := GenTransferMessages(ctx, toAgentName)
+ // send assistant event
+ aEvent := EventFromMessage(aMsg, nil, schema.Assistant, "")
+ generator.Send(aEvent)
+ // send transfer tool event
+ tEvent := EventFromMessage(tMsg, nil, schema.Tool, tMsg.ToolName)
+ tEvent.Action = &AgentAction{
+ TransferToAgent: &TransferToAgentAction{
+ DestAgentName: toAgentName,
+ },
+ }
+ generator.Send(tEvent)
+ }
+ }
+}
+```
+
+Key logic:
+
+- Event forwarding: all original events (thinking, tool calls, outputs) are forwarded intact
+- Interrupt check: if an interrupt occurs, no Transfer is triggered
+- Transfer generation: for each destination in `ToAgentNames`, send an assistant tip event and a tool event with `TransferToAgentAction` to route to the destination agent
+
+## Summary
+
+WithDeterministicTransferTo provides reliable transfer capability, forming the basis of the Supervisor pattern. Supervisor achieves efficient multi-agent collaboration via centralized coordination and deterministic callback, reducing complexity in development and maintenance. Combining both, developers can quickly build flexible, extensible multi-agent systems.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
new file mode 100644
index 00000000000..a5709b40ab6
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
@@ -0,0 +1,785 @@
+---
+Description: ""
+date: "2025-12-03"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Workflow Agents'
+weight: 2
+---
+
+## Overview
+
+### Import Path
+
+`import "github.com/cloudwego/eino/adk"`
+
+### What are Workflow Agents
+
+Workflow Agents are a specialized Agent type in Eino ADK that let developers organize and run multiple sub‑agents according to preset flows.
+
+Unlike LLM‑driven autonomous Transfer, Workflow Agents use preset decisions defined in code, providing predictable and controllable multi‑agent collaboration.
+
+Eino ADK provides three base Workflow Agent types:
+
+- SequentialAgent — execute sub‑agents in order
+- LoopAgent — repeat the sub‑agent sequence
+- ParallelAgent — run multiple sub‑agents concurrently
+
+These can be nested to build complex flows.
+
+# SequentialAgent
+
+## Functionality
+
+SequentialAgent executes sub‑agents strictly in the order provided. Each sub‑agent’s output is passed via History to the next sub‑agent, forming a linear chain.
+
+
+
+```go
+type SequentialAgentConfig struct {
+ Name string // Agent name
+ Description string // Agent description
+ SubAgents []Agent // Sub‑agents in execution order
+}
+
+func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Agent, error)
+```
+
+Execution rules:
+
+1. Linear execution: strictly in `SubAgents` order
+2. History passing: each agent’s result is added to History; subsequent agents can access prior history
+3. Early termination: if any sub‑agent emits ExitAction / Interrupt, the whole Sequential flow ends immediately
+
+Suitable for:
+
+- Multi‑step pipelines: e.g., preprocessing → analysis → report
+- Pipeline processing: each step’s output feeds the next
+- Dependent task sequences: later tasks rely on earlier results
+
+## Example
+
+Create a three‑step document processing pipeline:
+
+1. DocumentAnalyzer — analyze document content
+2. ContentSummarizer — summarize analysis
+3. ReportGenerator — generate final report
+
+```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文档处理流水线执行完成!")
+}
+```
+
+Run result:
+
+```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
+
+## Functionality
+
+LoopAgent builds on SequentialAgent and repeats the sub‑agent sequence until reaching `MaxIterations` or a sub‑agent emits ExitAction. Ideal for iterative optimization, repeated processing, or continuous monitoring.
+
+
+
+```go
+type LoopAgentConfig struct {
+ Name string // Agent name
+ Description string // Agent description
+ SubAgents []Agent // Sub‑agent list
+ MaxIterations int // Max iterations; 0 for infinite loop
+}
+
+func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
+```
+
+Execution rules:
+
+1. Loop execution: repeat the `SubAgents` sequence; each loop is a full Sequential run
+2. History accumulation: results from each iteration accumulate into History
+3. Exit conditions: ExitAction or reaching `MaxIterations` stops the loop; `MaxIterations=0` means infinite loop
+
+Suitable for:
+
+- Iterative optimization
+- Continuous monitoring
+- Repeated processing to reach a satisfactory result
+- Self‑improvement based on prior outputs
+
+## Example
+
+An iterative code optimization loop:
+
+1. CodeAnalyzer — analyze code issues
+2. CodeOptimizer — optimize based on analysis
+3. ExitController — decide whether to exit the loop
+
+```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代码优化循环执行完成!")
+}
+```
+
+Run result (excerpt):
+
+```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
+}
+```
+
+... (analysis and optimized code)
+
+=== 第 1 轮 - ExitController ===
+TERMINATE
+```
+
+## BreakLoop
+
+Use ADK’s Break Action to stop a loop without affecting outer agents:
+
+```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,
+ }}}
+```
+
+Illustration:
+
+
+
+- If Agent1 emits BreakAction, the Loop Agent stops and Sequential continues to Agent3
+- If Agent1 emits ExitAction, the overall Sequential flow terminates; Agent2 / Agent3 do not run
+
+# ParallelAgent
+
+## Functionality
+
+ParallelAgent runs multiple sub‑agents concurrently over shared input; all start together and it waits for all to finish. Best for independently processable tasks.
+
+
+
+```go
+type ParallelAgentConfig struct {
+ Name string // Agent name
+ Description string // Agent description
+ SubAgents []Agent // Concurrent sub‑agents
+}
+
+func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, error)
+```
+
+Execution rules:
+
+1. Concurrent execution: each sub‑agent runs in its own goroutine
+2. Shared input: all sub‑agents receive the same initial input and context
+3. Wait and aggregate: use sync.WaitGroup to wait for completion; collect outputs and emit in received order
+
+Defaults include:
+
+- Panic recovery per goroutine
+- Error isolation: one sub‑agent’s error does not affect others
+- Interrupt handling: supports sub‑agent interrupt/resume
+
+Suitable for:
+
+- Independent task parallelism
+- Multi‑perspective analysis
+- Performance optimization
+- Multi‑expert consultation
+
+## Example
+
+Analyze a product proposal from four perspectives:
+
+1. TechnicalAnalyst — technical feasibility
+2. BusinessAnalyst — business value
+3. UXAnalyst — user experience
+4. SecurityAnalyst — security risks
+
+```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))
+}
+```
+
+Run result (excerpt):
+
+```markdown
+开始多角度并行分析...
+
+=== BusinessAnalyst 分析完成 ===
+
+=== UXAnalyst 分析完成 ===
+
+=== SecurityAnalyst 分析完成 ===
+
+=== TechnicalAnalyst 分析完成 ===
+
+============================================================
+多角度分析结果汇总
+============================================================
+
+【技术分析】
+针对该智能客服系统方案,下面从技术实现、架构设计及性能优化等角度进行详细分析:
+...
+```
+
+# Summary
+
+Workflow Agents provide robust multi‑agent collaboration in Eino ADK. By choosing and composing these agents appropriately, developers can build efficient, reliable multi‑agent systems for complex business needs.
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_interface.md b/content/en/docs/eino/core_modules/eino_adk/agent_interface.md
new file mode 100644
index 00000000000..d26bfe2b9a0
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_interface.md
@@ -0,0 +1,265 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Agent Interface'
+weight: 3
+---
+
+## Agent Definition
+
+Implementing the following interface makes a struct an 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 | Description |
+| Name | Agent identifier (name) |
+| Description | Capabilities description to help other agents understand its role |
+| Run | Core execution method; returns an iterator to continuously receive Agent events |
+
+
+## AgentInput
+
+```go
+type AgentInput struct {
+ Messages []Message
+ EnableStreaming bool
+}
+
+type Message = *schema.Message
+```
+
+Agents typically center around a chat model, so input uses `Messages` compatible with Eino ChatModel. Include user instructions, dialogue history, background knowledge, examples, etc.
+
+```go
+import (
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/schema"
+)
+
+input := &adk.AgentInput{
+ Messages: []adk.Message{
+ schema.UserMessage("What's the capital of France?"),
+ schema.AssistantMessage("The capital of France is Paris.", nil),
+ schema.UserMessage("How far is it from London? "),
+ },
+}
+```
+
+`EnableStreaming` suggests output mode for components that support both streaming and non‑streaming (e.g., ChatModel). It is not a hard constraint. The actual output type is indicated by `AgentOutput.IsStreaming`.
+
+- When `EnableStreaming=false`: for components that support both, prefer non‑streaming (return full result at once).
+- When `EnableStreaming=true`: components capable of streaming should stream; components that do not support streaming continue non‑streaming.
+
+As shown below, ChatModel may stream or not, while Tool outputs non‑stream only:
+
+- `EnableStreaming=false`: both output non‑stream
+- `EnableStreaming=true`: ChatModel streams; Tool remains non‑stream
+
+
+
+## AgentRunOption
+
+Options can adjust configuration or behavior per request. ADK provides common options:
+
+- `WithSessionValues` — set cross‑agent KV state
+- `WithSkipTransferMessages` — when transferring, do not append transfer event messages to history
+
+Wrapping and reading implementation‑specific options:
+
+```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
+
+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...
+}
+```
+
+Designate option targets for specific agents in a multi‑agent system:
+
+```go
+opt := adk.WithSessionValues(map[string]any{}).DesignateAgent("agent_1", "agent_2")
+```
+
+## AsyncIterator
+
+`Agent.Run` returns `AsyncIterator[*AgentEvent]`, an asynchronous iterator (production and consumption are decoupled) for consuming events in order:
+
+```go
+// github.com/cloudwego/eino/adk/utils.go
+
+type AsyncIterator[T any] struct { /* ... */ }
+
+func (ai *AsyncIterator[T]) Next() (T, bool) { /* ... */ }
+```
+
+Consume with a blocking `Next()` loop until closed:
+
+```go
+iter := myAgent.Run(xxx)
+for {
+ event, ok := iter.Next()
+ if !ok { break }
+ // handle event
+}
+```
+
+Create with `NewAsyncIteratorPair` and produce via `AsyncGenerator`:
+
+```go
+func NewAsyncIteratorPair[T any]() (*AsyncIterator[T], *AsyncGenerator[T])
+```
+
+Agents usually run in a goroutine and return the iterator immediately, so the caller can start consuming events in real time:
+
+```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
+
+Configure common behaviors before running via `AgentWithOptions`:
+
+```go
+// github.com/cloudwego/eino/adk/flow.go
+func AgentWithOptions(ctx context.Context, agent Agent, opts ...AgentOption) Agent
+```
+
+Built‑in options:
+
+- `WithDisallowTransferToParent` — disallow transferring to parent; triggers `OnDisallowTransferToParent`
+- `WithHistoryRewriter` — rewrite history into input messages before execution
+
+# AgentEvent
+
+Core event structure produced by agents:
+
+```go
+// github.com/cloudwego/eino/adk/interface.go
+
+type AgentEvent struct {
+ AgentName string
+ RunPath []RunStep
+ Output *AgentOutput
+ Action *AgentAction
+ Err error
+}
+
+// EventFromMessage builds a standard event
+func EventFromMessage(msg Message, msgStream MessageStream, role schema.RoleType, toolName string) *AgentEvent
+```
+
+## AgentName & RunPath
+
+Filled by the framework to provide event provenance in multi‑agent systems:
+
+```go
+type RunStep struct { agentName string }
+```
+
+- `AgentName` — which agent produced the event
+- `RunPath` — chain from entry agent to current agent
+
+## AgentOutput
+
+Encapsulates agent output:
+
+```go
+type AgentOutput struct {
+ MessageOutput *MessageVariant
+ CustomizedOutput any
+}
+
+type MessageVariant struct {
+ IsStreaming bool
+ Message Message
+ MessageStream MessageStream
+ Role schema.RoleType
+ ToolName string // when Role is Tool
+}
+```
+
+`MessageVariant`:
+
+1. Unifies streaming vs non‑streaming messages via `IsStreaming`:
+ - Streaming: return chunks over time that form a complete message (read from `MessageStream`).
+ - Non‑streaming: return a complete message at once (read from `Message`).
+2. Surfaces convenient metadata at top level:
+ - `Role`: Assistant or Tool
+ - `ToolName`: when `Role` is Tool, provide the tool’s name
+
+## AgentAction
+
+Control multi‑agent collaboration: exit, interrupt, transfer, or custom:
+
+```go
+type AgentAction struct {
+ Exit bool
+
+ Interrupted *InterruptInfo
+
+ TransferToAgent *TransferToAgentAction
+
+ BreakLoop *BreakLoopAction
+
+ CustomizedAction any
+}
+
+type InterruptInfo struct { Data any }
+
+type TransferToAgentAction struct { DestAgentName string }
+```
+
+Prebuilt actions:
+
+```go
+func NewExitAction() *AgentAction { return &AgentAction{Exit: true} }
+func NewTransferToAgentAction(dest string) *AgentAction {
+ return &AgentAction{TransferToAgent: &TransferToAgentAction{DestAgentName: dest}}
+}
+```
+
+Interrupt sends custom info for checkpoint/resume flows (see Runner docs). For example, ChatModelAgent sends an interrupt event as:
+
+```go
+// e.g., when ChatModelAgent interrupts, it emits:
+h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{
+ Interrupted: &InterruptInfo{
+ Data: &ChatModelAgentInterruptInfo{Data: data, Info: info},
+ },
+}})
+```
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_preview.md b/content/en/docs/eino/core_modules/eino_adk/agent_preview.md
new file mode 100644
index 00000000000..b3b95c4741e
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_preview.md
@@ -0,0 +1,156 @@
+---
+Description: ""
+date: "2025-12-11"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Overview'
+weight: 1
+---
+
+# What is Eino ADK?
+
+Eino ADK, inspired by Google ADK, is a flexible Go framework for building Agents and Multi‑Agent applications. It standardizes context passing, event streaming and conversion, task transfer, interrupts & resume, and cross‑cutting aspects. It is model‑agnostic and deployment‑agnostic, aiming to make Agent and Multi‑Agent development simpler and more robust while offering production‑grade governance capabilities.
+
+Eino ADK helps developers build and manage agent applications, providing a resilient development environment to support conversational and non‑conversational agents, complex tasks, and workflows.
+
+# Architecture
+
+
+
+## Agent Interface
+
+The core of ADK is the `Agent` abstraction. See the full details in [Eino ADK: Agent Interface](/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. Reads task details and related data from `AgentInput`, `AgentRunOption`, and optional session context
+2. Executes the task and writes progress/results into an `AgentEvent` iterator
+3. Requires a future‑style asynchronous execution. In practice (see ChatModelAgent `Run`):
+ - Create a pair of Iterator/Generator
+ - Start the agent’s async task with the Generator, process `AgentInput` (e.g., call LLM) and emit events into the Generator
+ - Return the Iterator immediately to the caller
+
+## Collaboration
+
+ADK provides rich composition primitives to build Multi‑Agent systems: Supervisor, Plan‑Execute, Group‑Chat, etc. See [Eino ADK: Agent Collaboration](/docs/eino/core_modules/eino_adk/agent_collaboration).
+
+Primitives:
+
+
+| Collaboration | Description |
+| Transfer | Directly transfer the task to another Agent; current Agent exits and does not track the transferred task |
+| ToolCall (AgentAsTool) | Treat an Agent as a tool call, wait for its response, consume its output, and continue processing |
+
+
+Context strategies:
+
+
+| Context Strategy | Description |
+| Upstream full dialogue | Provide the child Agent with the complete upstream conversation |
+| New task description | Ignore upstream conversation and provide a fresh summarized task as the child Agent’s input |
+
+
+Decision autonomy:
+
+
+| Autonomy | Description |
+| Autonomous | Inside the Agent, choose downstream Agents as needed (often via LLM). Even if decisions are based on preset logic, from the outside this is treated as autonomous. |
+| Preset | Pre‑define the next Agent. Execution order is fixed and predictable. |
+
+
+Compositions:
+
+
+| Type | Description | Run Mode | Collaboration | Context | Autonomy |
+| SubAgents | Treat a user‑provided Agent as the parent, and its subAgents list as children, forming an autonomously deciding Agent. Name/Description identify the Agent.Currently limited to one parent per AgentUse SetSubAgents to build a “multi‑branch tree” Multi‑AgentAgentName must be unique within the tree |  | Transfer | Upstream full dialogue | Autonomous |
+| Sequential | Compose SubAgents to execute in order. Name/Description identify the Sequential Agent. Executes subagents sequentially until all finish. |  | Transfer | Upstream full dialogue | Preset |
+| Parallel | Compose SubAgents to run concurrently under the same context. Name/Description identify the Parallel Agent. Executes subagents in parallel, ends after all complete. |  | Transfer | Upstream full dialogue | Preset |
+| Loop | Compose SubAgents to run in array order, repeat cyclically. Name/Description identify the Loop Agent. Executes subagents in sequence per loop. |  | Transfer | Upstream full dialogue | Preset |
+| AgentAsTool | Convert an Agent into a Tool for use by other Agents. Whether an Agent can call other Agents as Tools depends on its implementation. ChatModelAgent supports AgentAsTool. |  | ToolCall | New task description | Autonomous |
+
+
+## ChatModelAgent
+
+`ChatModelAgent` is the key implementation of the agent abstraction. It wraps LLM interaction and implements a ReAct‑style control flow via Eino Graph, exporting events as `AgentEvent`s. See [Eino ADK: ChatModelAgent](/docs/eino/core_modules/eino_adk/agent_implementation/chat_model).
+
+```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
+}
+
+func NewChatModelAgent(_ context.Context, config *ChatModelAgentConfig) (*ChatModelAgent, error) {
+ // omit code
+}
+```
+
+## AgentRunner
+
+Runner executes agents and enables advanced features. See [Eino ADK: Agent Runner & Extensions](/docs/eino/core_modules/eino_adk/agent_extension).
+
+Runner‑only capabilities:
+
+- Interrupt & Resume
+- Cross‑cutting hooks (coming)
+- Context preprocessing
+
+```go
+type RunnerConfig struct {
+ Agent Agent
+ EnableStreaming bool
+
+ CheckPointStore compose.CheckPointStore
+}
+
+func NewRunner(_ context.Context, conf RunnerConfig) *Runner {
+ // omit code
+}
+```
+
+
diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_quickstart.md b/content/en/docs/eino/core_modules/eino_adk/agent_quickstart.md
new file mode 100644
index 00000000000..2f937a54c9e
--- /dev/null
+++ b/content/en/docs/eino/core_modules/eino_adk/agent_quickstart.md
@@ -0,0 +1,89 @@
+---
+Description: ""
+date: "2025-12-11"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Quickstart'
+weight: 1
+---
+
+# Installation
+
+Eino provides ADK from `v0.5.0`. Upgrade your project:
+
+```go
+// stable >= eino@v0.5.0
+go get github.com/cloudwego/eino@latest
+```
+
+# Agent
+
+### What is Eino ADK
+
+Eino ADK, inspired by [Google‑ADK](https://google.github.io/adk-docs/agents/), is a Go framework for building Agent and Multi‑Agent applications. It standardizes context passing, event streaming, task transfer, interrupts/resume, and cross‑cutting features.
+
+### What is an Agent
+
+An Agent represents an executable, intelligent task unit with a clear name and description so other agents can discover and transfer tasks to it. Typical use cases:
+
+- Query weather information
+- Book meetings
+- Answer domain‑specific questions
+
+### Agent in ADK
+
+All ADK features build on the `Agent` abstraction:
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput) *AsyncIterator[*AgentEvent]
+}
+```
+
+ADK provides three base categories:
+
+- `ChatModel Agent` — the “thinking” part powered by LLMs; understand, reason, plan, respond, and call tools
+- `Workflow Agents` — coordination layer with preset logic (sequential/parallel/loop). Deterministic, predictable flows.
+ - Sequential — execute subagents in order
+ - Loop — repeat subagents until a condition
+ - Parallel — run subagents concurrently
+- `Custom Agent` — implement the interface for bespoke logic
+
+Combine these to compose Multi‑Agent systems. Eino also offers built‑in best‑practice paradigms:
+
+- Supervisor — centralized coordinator controlling communications and delegation
+- Plan‑Execute — planner generates steps; executor carries them out; replanner decides finish or replan
+
+
+
+
+| Category | ChatModel Agent | Workflow Agents | Custom Logic | EinoBuiltInAgent (supervisor, plan‑execute) |
+| Function | Thinking, generation, tool calls | Control execution flow among agents | Run custom logic | Out‑of‑the‑box multi‑agent patterns |
+| Core | LLM | Predetermined flows (sequential/parallel/loop) | Custom code | High‑level encapsulation based on Eino practice |
+| Purpose | Generation, dynamic decisions | Structured orchestration | Specific customization | Turnkey solutions for common scenarios |
+
+
+# ADK Examples
+
+Explore examples in [Eino‑examples](https://github.com/cloudwego/eino-examples/tree/main/adk). The table summarizes project paths, key points, and diagrams:
+
+
+| Project Path | Intro | Diagram |
+| Sequential workflow | This example shows a sequential multi‑agent workflow built with Eino ADK’s Workflow paradigm.Sequential construction: create a ResearchAgent via adk.NewSequentialAgent with two subagents — PlanAgent (planning) and WriterAgent (writing).Clear responsibilities: PlanAgent outputs a detailed plan; WriterAgent writes a structured report based on the plan.Chained IO: PlanAgent’s output feeds WriterAgent’s input, illustrating ordered dependency. |  |
+| Loop workflow | Built with LoopAgent to form a reflection‑iteration framework.Iterative reflection: ReflectionAgent combines MainAgent (solve) and CritiqueAgent (review), up to 5 iterations.MainAgent: produces an initial solution.CritiqueAgent: audits quality, suggests improvements; terminates when satisfactory.Loop mechanism: repeatedly improves outputs across iterations. |  |
+| Parallel workflow | Built with ParallelAgent for concurrent data collection.Concurrent framework: DataCollectionAgent launches multiple info collectors.Responsibility split: each subagent handles one channel independently.Parallel execution: starts tasks simultaneously to improve throughput. |  |
+| supervisor | Single‑layer Supervisor manages two composite subagents: Research Agent (retrieval) and Math Agent (math operations: add/multiply/divide). All math ops are handled by one Math Agent rather than splitting into many; suitable for focused tasks and quick deployment. |  |
+| layered‑supervisor | Multi‑tier supervision: top Supervisor manages Research Agent and Math Agent; Math Agent further manages Subtract/Multiply/Divide subagents.Top Supervisor delegates research/math tasks.Mid‑tier Math Agent delegates specific operations.Good for fine‑grained decomposition and multi‑level delegation. |  |
+| plan‑execute example | Implements a plan‑execute‑replan travel planner: Planner generates stepwise plan; Executor calls mock tools (get_weather/search_flights/search_hotels/search_attractions/ask_for_clarification); Replanner decides replan or finish. Two layers:Layer 2: loop of execute + replan.Layer 1: sequential of plan + layer‑2 loop. |  |
+| book recommendation agent (interrupt/resume) | Demonstrates a ChatModel agent with tools and checkpointing.Agent: BookRecommender via adk.NewChatModelAgent.Tools: BookSearch and AskForClarification.State: in‑memory checkpoint storage.Events: iterate runner.Query and runner.Resume.Custom input: drive flow via options. |  |
+
+
+# What's Next
+
+After this quickstart, you should have a basic understanding of Eino ADK and Agents.
+
+The next articles dive into ADK core concepts to help you understand its internals and use it effectively:
+
+
diff --git a/content/en/docs/eino/core_modules/flow_integration_components/_index.md b/content/en/docs/eino/core_modules/flow_integration_components/_index.md
index 97f8c4e689d..02dad631652 100644
--- a/content/en/docs/eino/core_modules/flow_integration_components/_index.md
+++ b/content/en/docs/eino/core_modules/flow_integration_components/_index.md
@@ -1,24 +1,24 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Flow integration'
-weight: 0
+title: 'Eino: Flow Integration'
+weight: 3
---
-LLM applications have **common scenarios and patterns**. By abstracting these scenarios, templates can be provided to help developers quickly build LLM applications. Eino's Flow module is designed to do just this.
+LLM applications have common patterns. Abstracting them yields templates that accelerate development. Eino’s Flow module provides these ready-to-use integrations.
-Currently, Eino has integrated two commonly used Agent patterns, `react agent` and `host multi agent`, as well as MultiQueryRetriever, ParentIndexer, and others.
+Currently integrated: `ReAct Agent`, `Host Multi-Agent`, `MultiQueryRetriever`, `ParentIndexer`, and more.
-- React Agent: [Eino: React Agent Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual)
-- Multi Agent: [Eino Tutorial: Host Multi-Agent ](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
+- ReAct Agent: [Eino: ReAct Agent Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual)
+- Multi Agent: [Eino Tutorial: Host Multi-Agent](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
-## Orchestrate Flows
+## Using Flows in Orchestration
-The Flow integration component itself is generally composed of one or more graphs. At the same time, these flows can also be used as nodes to participate in the orchestration of other graphs, and there are three ways to do this:
+Flows are often backed by one or more graphs. You can embed them as nodes in other graphs in three ways:
-1. If a flow implements the interface of a certain component, methods such as AddXXXNode corresponding to that component can be used to add it to a graph. For example, for the multiquery retriever:
+1. If a flow implements a component interface, add it via the component’s `AddXXXNode` methods, e.g., `multiquery retriever`:
```go
// instantiate the flow: multiquery.NewRetriever
@@ -59,7 +59,7 @@ _ = graph.AddChatTemplateNode("template", prompt.FromMessages(schema._FString_,
// ...
```
-1. If a flow is internally orchestrated by a single graph and the function of the flow is completely equivalent to the operation of this graph (there is no customized logic that cannot be covered within the graph run), the graph of the flow can be exported and added to orchestration through methods such as AddGraphNode, like ReActAgent and Host Multi-Agent:
+2. If a flow is internally a single graph and the flow’s behavior is fully equivalent to that graph (no extra custom logic), export the graph and add it via `AddGraphNode`, e.g., Host Multi‑Agent:
```go
// instantiate the host multi-agent
@@ -92,7 +92,7 @@ out, err := fullGraph.Invoke(ctx, map[string]any{"country_name": "China"},
DesignateNodeWithPath(compose.NewNodePath("host_ma_node", hostMA.HostNodeKey())))
```
-1. All flows can be encapsulable as Lambdas and added to orchestration through methods such as AddLambdaNode. Currently, all flows can be added to orchestration through method 1 or 2, so there is no need to downgrade to using Lambdas. We give an example just in case you need it:
+3. All flows can be wrapped into Lambda and added via `AddLambdaNode`. Current flows can already be added via methods 1 or 2, so wrapping is not needed; if used:
```go
// instantiate the flow
@@ -127,11 +127,11 @@ res, err := r.Invoke(ctx, []*schema.Message{{Role: schema._User_, Content: "hell
compose.WithCallbacks(callbackForTest))
```
-The comparison of the three methods is as follows:
+Comparison:
-| Method | Applicable Scenario | Advantage |
-| As a Component | Need to implement the interface of the component | Simple and straightforward, with clear semantics |
-| As a Graph | Orchestrated by a single graph, and the function does not exceed the scope of this graph | The nodes within the graph are exposed to the outer graph. Runtime options can be uniformly allocated. There is one less layer of conversion compared to using Lambda. The relationship between the upper and lower graphs can be obtained through GraphCompileCallback |
-| As a Lambda | All scenarios | Universal |
+| Mode | Scenario | Advantages |
+| As Component | Implements the component’s interface | Simple and clear semantics |
+| As Graph | Single graph composition; behavior fully within that graph | Inner nodes exposed to outer graph; unified runtime options; fewer conversions than Lambda; can obtain graph nesting via GraphCompileCallback |
+| As Lambda | All | Universal |
diff --git a/content/en/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md b/content/en/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
index e1495b0fd48..89543a16f30 100644
--- a/content/en/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
+++ b/content/en/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
@@ -1,17 +1,17 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino Tutorial: Host Multi-Agent '
-weight: 0
+title: 'Eino Tutorial: Host Multi-Agent'
+weight: 2
---
-Host Multi-Agent is a Host that performs intent recognition and then delegates the actual generation task to a specific expert agent.
+Host Multi-Agent is a pattern where a Host recognizes intent and hands off to a specialist agent to perform the actual generation.
-Take a simple "Journal Assistant" as an example: it can write journals, read journals, and answer questions based on journal entries.
+Example: a “journal assistant” that can write journal, read journal, and answer questions based on journal.
-> For a full example, see: [https://github.com/cloudwego/eino-examples/tree/main/flow/agent/multiagent/host/journal](https://github.com/cloudwego/eino-examples/tree/main/flow/agent/multiagent/host/journal)
+Full sample: https://github.com/cloudwego/eino-examples/tree/main/flow/agent/multiagent/host/journal
Host:
@@ -34,7 +34,7 @@ func newHost(ctx context.Context, baseURL, apiKey, modelName string) (*host.Host
}
```
-The "specialist" for writing journals: after the host recognizes that the user's intent is to write a journal, it delegates the task here, where the user's intended content is written to a file.
+Write-journal specialist:
```go
func newWriteJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
@@ -46,7 +46,7 @@ func newWriteJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
Temperature: 0.000001,
},
})
- if (err != nil) {
+ if err != nil {
return nil, err
}
@@ -62,7 +62,7 @@ func newWriteJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
chain.AppendLambda(compose.InvokableLambda(func(ctx context.Context, input []*schema.Message) ([]*schema.Message, error) {
systemMsg := &schema.Message{
Role: schema._System_,
- Content: "You are responsible for preparing the user query for insertion into journal. The user's query is expected to contain the actual text the user wants to write to the journal, as well as convey the intention that this query should be written to the journal. Your job is to remove that intention from the user query, while preserving as much as possible the user's original query, and output ONLY the text to be written into the journal.",
+ Content: "You are responsible for preparing the user query for insertion into journal. The user's query is expected to contain the actual text the user want to write to journal, as well as convey the intention that this query should be written to journal. You job is to remove that intention from the user query, while preserving as much as possible the user's original query, and output ONLY the text to be written into journal",
}
return append([]*schema.Message{systemMsg}, input...), nil
})).
@@ -95,7 +95,7 @@ func newWriteJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
}
```
-The "specialist" for reading journals: after the host recognizes that the user's intent is to read a journal, it delegates the task here. This specialist reads the journal file content and outputs it line by line. It functions as a local function.
+Read-journal specialist (streams lines):
```go
func newReadJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
@@ -141,7 +141,7 @@ func newReadJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
}
```
-According to the journal answer specialist:
+Answer-with-journal specialist:
```go
func newAnswerWithJournalSpecialist(ctx context.Context) (*host.Specialist, error) {
@@ -230,7 +230,7 @@ func newAnswerWithJournalSpecialist(ctx context.Context) (*host.Specialist, erro
}
```
-Compose into a host multi-agent and start it via command line:
+Compose host multi-agent and run a CLI:
```go
func main() {
@@ -269,53 +269,53 @@ func main() {
cb := &logCallback{}
- for { // Multi-turn conversation, loops continuously unless the user inputs "exit"
- println("\n\nYou: ") // Prompting for user input
+ for { // multi-turn until user enters "exit"
+ println("\n\nYou: ") // prompt for user input
- var message string
- scanner := bufio.NewScanner(os.Stdin) // Getting user input from command line
- for scanner.Scan() {
- message += scanner.Text()
- break
- }
+ var message string
+ scanner := bufio.NewScanner(os.Stdin) // read from CLI
+ for scanner.Scan() {
+ message += scanner.Text()
+ break
+ }
- if err := scanner.Err(); err != nil {
- panic(err)
- }
+ if err := scanner.Err(); err != nil {
+ panic(err)
+ }
- if message == "exit" {
- return
- }
+ if message == "exit" {
+ return
+ }
- msg := &schema.Message{
- Role: schema._User_,
- Content: message,
- }
+ msg := &schema.Message{
+ Role: schema._User_,
+ Content: message,
+ }
- out, err := hostMA.Stream(ctx, []*schema.Message{msg}, host.WithAgentCallbacks(cb))
- if err != nil {
- panic(err)
- }
+ out, err := hostMA.Stream(ctx, []*schema.Message{msg}, host.WithAgentCallbacks(cb))
+ if err != nil {
+ panic(err)
+ }
- defer out.Close()
+ defer out.Close()
- println("\nAnswer:")
+ println("\nAnswer:")
- for {
- msg, err := out.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
- }
+ for {
+ msg, err := out.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ }
- print(msg.Content)
- }
+ print(msg.Content)
+ }
}
}
```
-Running console output:
+Console output example:
```go
You:
@@ -334,7 +334,6 @@ HandOff to view_journal_content with argument {"reason":"User wants to read the
Answer:
I got up at 7:00 in the morning
-
You:
when did I get up in the morning?
@@ -346,13 +345,13 @@ You got up at 7:00 in the morning.
## FAQ
-### Host direct answer does not have streaming effect
+### No streaming when Host outputs directly
-Host Multi-Agent provides a configuration for `StreamToolCallChecker` to determine whether the Host outputs directly.
+Host Multi-Agent provides `StreamToolCallChecker` to determine whether Host outputs directly.
-Different models may output tool calls in different ways in streaming mode: some models (e.g., OpenAI) output tool calls directly; some models (e.g., Claude) output text first and then output tool calls. Therefore, different methods are needed for the determination. This field is used to specify a function for determining whether the model's streaming output contains tool calls.
+Different providers in streaming mode may output tool calls differently: some output tool calls directly (e.g., OpenAI); some output text first then tool calls (e.g., Claude). Configure a checker accordingly.
-It is optional. If not filled, the determination of whether the "non-empty package" contains tool calls is used:
+Default checker (first non-empty chunk must be tool-call):
```go
func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
@@ -380,9 +379,9 @@ func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[
}
```
-The above default implementation is applicable when the Tool Call Message output by the model contains only Tool Calls.
+The default fits providers whose Tool Call messages contain only tool calls.
-The default implementation is not applicable when there is a non-empty content chunk before outputting the Tool Call. In this case, a custom tool call checker is required as follows:
+When a provider outputs non-empty content before tool calls, define a custom checker:
```go
toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) {
@@ -406,14 +405,12 @@ toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Mes
}
```
-This custom `StreamToolCallChecker` may need to check **all packages** for the presence of ToolCalls in extreme cases, resulting in the loss of the "streaming judgment" effect. If you want to maintain the "streaming judgment" effect as much as possible, the suggested solution is:
-
-Try to add a prompt to constrain the model not to output additional text when making tool calls, for example: "If you need to call a tool, output the tool directly without outputting text."
+Note: in extreme cases you may need to scan all chunks, degrading the streaming decision. To preserve streaming behavior as much as possible:
-Different models may be affected by the prompt differently. You need to adjust the prompt and verify the effect in actual use.
+> Tip: add a prompt such as “If you need to call tools, output the tool calls only, do not output text.” Prompt effectiveness varies; adjust and verify with your provider.
-### Host picks multiple Specialists simultaneously
+### Host selects multiple Specialists
-The Host provides the selection of Specialists in the form of Tool Calls, so it may select multiple Specialists simultaneously in the form of a Tool Call list. At this time, the Host Multi-Agent will route the request to these multiple Specialists at the same time. After the multiple Specialists complete their tasks, the Summarizer node will summarize multiple Messages into one Message as the final output of the Host Multi-Agent.
+Host may select multiple specialists via a list of tool calls. In that case, Host Multi-Agent routes to all selected specialists in parallel, and after they finish, summarizes multiple messages into one via a Summarizer node as the final output.
-Users can customize the behavior of the Summarizer by configuring the Summarizer, specifying a ChatModel and a SystemPrompt. If not specified, the Host Multi-Agent will concatenate the output Message Contents of multiple Specialists and return the result.
+Users can configure a Summarizer (ChatModel + SystemPrompt) to customize behavior. If unspecified, Host Multi-Agent concatenates contents from multiple specialists.
diff --git a/content/en/docs/eino/core_modules/flow_integration_components/react_agent_manual.md b/content/en/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
index ff0e38fce38..a4ab703d3d5 100644
--- a/content/en/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
+++ b/content/en/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
@@ -1,32 +1,29 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino: React Agent Manual'
-weight: 0
+title: 'Eino: ReAct Agent Manual'
+weight: 1
---
-# **Introduction**
+# Introduction
-Eino React Agent is an Agent framework that implements [React logic](https://react-lm.github.io/), which users can use to quickly and flexibly build and invoke React Agents.
+Eino’s ReAct Agent implements the [ReAct logic](https://react-lm.github.io/), enabling fast, flexible agent construction and invocation.
-> 💡
-> For the code implementation, see: [Implementation Code Directory](https://github.com/cloudwego/eino/tree/main/flow/agent/react)
->
-> Example code path: [https://github.com/cloudwego/eino-examples/blob/main/flow/agent/react/react.go](https://github.com/cloudwego/eino-examples/blob/main/flow/agent/react/react.go)
+> Code: [Implementation Directory](https://github.com/cloudwego/eino/tree/main/flow/agent/react)
-## **Node Topology & Data Flow Diagram**
+## Topology and Data Flow
-The React Agent uses `compose.Graph` as the orchestration scheme at its core. Typically, there are 2 nodes: ChatModel and Tools. All historical messages during the intermediate running process are stored in the state. Before passing all historical messages to the ChatModel, the messages are copied and processed by the MessageModifier, and the processed results are then passed to the ChatModel. This process continues until there are no more tool calls in the messages returned by the ChatModel, and then it returns the final message.
+Under the hood, ReAct Agent uses `compose.Graph`. Typically two nodes: `ChatModel` and `Tools`. All historical messages are stored in `state`. Before passing history to `ChatModel`, messages are copied and processed by `MessageModifier`. When `ChatModel` returns without any tool call, the final message is returned.
-When at least one Tool in the Tools list is configured with ReturnDirectly, the ReAct Agent structure becomes more complex: a Branch is added after the ToolsNode to determine whether a Tool configured with ReturnDirectly is called. If so, it directly ends (END), otherwise, it proceeds as usual to the ChatModel.
+If any tool is marked `ReturnDirectly`, a `Branch` follows `ToolsNode` to short-circuit and end when such a tool is invoked; otherwise the flow returns to `ChatModel`.
-## **Initialization**
+## Initialization
-A ReactAgent initialization function is provided, with mandatory parameters being Model and ToolsConfig, and optional parameters being MessageModifier, MaxStep, ToolReturnDirectly, and StreamToolCallChecker.
+Provide a `ToolCallingChatModel` and `ToolsConfig`. Optional: `MessageModifier`, `MaxStep`, `ToolReturnDirectly`, `StreamToolCallChecker`.
```bash
go get github.com/cloudwego/eino-ext/components/model/openai@latest
@@ -45,47 +42,57 @@ import (
)
func main() {
- // Initialize the required chatModel first
+ // initialize chat model
toolableChatModel, err := openai.NewChatModel(...)
- // Initialize the required tools
+ // initialize tools
tools := compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- mytool,
- ...
- },
+ InvokableTools: []tool.InvokableTool{mytool},
+ StreamableTools: []tool.StreamableTool{myStreamTool},
}
- // Create an agent
+ // create agent
agent, err := react.NewAgent(ctx, &react.AgentConfig{
ToolCallingModel: toolableChatModel,
ToolsConfig: tools,
...
- })
+ }
}
```
### Model
-The model receives a ChatModel, and within the agent, it will call the BindTools interface, defined as:
+ReAct requires a `ToolCallingChatModel`. Inside the agent, `WithTools` is called to bind the agent’s tools to the model:
```go
-type ChatModel interface {
+// 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)
-
- BindTools(tools []*schema.ToolInfo) error
+ *schema.StreamReader[*schema.Message], error)
}
-```
-Currently, eino provides implementations such as openai and ark. As long as the underlying model supports tool call, it is sufficient.
+// 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)
+}
+```
+
+Supported implementations include OpenAI and Ark (any provider that supports tool calls).
```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"
@@ -107,13 +114,12 @@ func openaiExample() {
}
func arkExample() {
- arkModel, err := ark.NewChatModel(context.Background(), &ark.ChatModelConfig{
+ arkModel, err := ark.NewChatModel(context.Background(), ark.ChatModelConfig{
APIKey: os.Getenv("ARK_API_KEY"),
Model: os.Getenv("ARK_MODEL"),
- BaseURL: os.Getenv("ARK_BASE_URL"),
})
- agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ agent, err := react.NewAgent(ctx, react.AgentConfig{
ToolCallingModel: arkModel,
ToolsConfig: ...,
})
@@ -122,8 +128,7 @@ func arkExample() {
### ToolsConfig
-The toolsConfig type is `compose.ToolsNodeConfig`. In eino, to build a Tool node, you need to provide information about the Tool and call the Tool's function. The interface definition for the tool is as follows:
-
+`toolsConfig` is `compose.ToolsNodeConfig`. To build a Tools node, provide Tool info and a run function. Tool interfaces:
```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)
@@ -145,21 +150,20 @@ type StreamableTool interface {
}
```
-Users can implement the required tool according to the tool's interface definition. The framework also provides a more straightforward method for constructing tools:
-
+You can implement tools per the interfaces, or use helpers to construct tools:
```go
userInfoTool := utils.NewTool(
&schema.ToolInfo{
Name: "user_info",
- Desc: "Query user's company, position, and salary information based on the user's name and email",
+ Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"name": {
Type: "string",
- Desc: "User's name",
+ Desc: "用户的姓名",
},
"email": {
Type: "string",
- Desc: "User's email",
+ Desc: "用户的邮箱",
},
}),
},
@@ -174,23 +178,20 @@ userInfoTool := utils.NewTool(
})
toolConfig := &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- mytool,
- ...
- },
+ InvokableTools: []tool.InvokableTool{invokeTool},
}
```
### MessageModifier
-MessageModifier is executed each time before all historical messages are passed to the ChatModel. It is defined as:
+Executed before each call to `ChatModel`:
```go
// modify the input messages before the model is called.
type MessageModifier func(ctx context.Context, input []*schema.Message) []*schema.Message
```
-The framework provides a convenient PersonaModifier to add a system message representing the agent's personality at the top of the message list. It is used as follows:
+Configure `MessageModifier` inside the Agent to adjust the messages passed to the model:
```go
import (
@@ -205,142 +206,86 @@ func main() {
MessageModifier: func(ctx context.Context, input []*schema.Message) []*schema.Message {
res := make([]*schema.Message, 0, len(input)+1)
-
- res = append(res, schema.SystemMessage("You are an expert Go developer."))
+
+ res = append(res, schema.SystemMessage("你是一个 golang 开发专家."))
res = append(res, input...)
return res
},
})
- agent.Generate(ctx, []*schema.Message{schema.UserMessage("Write a hello world code")})
- // The actual input to the ChatModel would be
+ agent.Generate(ctx, []*schema.Message{schema.UserMessage("写一个 hello world 的代码")})
+ // 实际输入:
// []*schema.Message{
- // {Role: schema.System, Content: "You are an expert Go developer."},
- // {Role: schema.Human, Content: "Write a hello world code"}
- //}
+ // {Role: schema.System, Content:"你是一个 golang 开发专家."},
+ // {Role: schema.Human, Content: "写一个 hello world 的代码"}
+ // }
}
```
### MaxStep
-Specifies the maximum running steps for an Agent. Each transition from one node to another counts as one step. The default value is the number of nodes + 2.
+Specify the maximum number of steps. One loop is `ChatModel` + `Tools` (2 steps). Default is `node count + 2`.
-Since one loop in the Agent comprises the ChatModel and Tools, it equals 2 steps. Therefore, the default value of 12 allows up to 6 loops. However, since the final step must be a ChatModel response (because the ChatModel determines no further tool runs are needed to return the final result), up to 5 tool runs are possible.
+Since one loop is 2 steps, default `12` supports up to 6 loops. The final step must be a `ChatModel` result (no tool call), so at most 5 Tools.
-Similarly, if you want the Agent to run up to 10 loops (10 ChatModel + 9 Tools), set MaxStep to 20. If you want the Agent to run up to 20 loops, set MaxStep to 40.
+For 10 loops (10×ChatModel + 9×Tools), set `MaxStep` to 20; for 20 loops set `MaxStep` to 40.
-```go
-func main() {
- agent, err := react.NewAgent(ctx, &react.AgentConfig{
- ToolCallingModel: toolableChatModel,
- ToolsConfig: tools,
- MaxStep: 20,
- }
-}
-```
-
-### ToolReturnDirectly
-
-If you wish for the Agent to directly return the Tool's Response ToolMessage after the ChatModel selects and executes a specific Tool, you can configure this Tool in ToolReturnDirectly.
-
-```go
-a, err = NewAgent(ctx, &AgentConfig{
- ToolCallingModel: cm,
- ToolsConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{fakeTool, fakeStreamTool},
- },
-
- MaxStep: 40,
- ToolReturnDirectly: map[string]struct{}{fakeToolName: {}}, // one of the two tools is return directly
-})
-```
+### ToolReturnDirectly and Stream Tool Call Checking
-### **StreamToolCallChecker**
-
-Different models may output tool calls in different ways in streaming mode: some models (e.g., OpenAI) will output tool calls directly; some models (e.g., Claude) will output text first and then output tool calls. Therefore, different methods are needed for judgment. This field is used to specify a function for judging whether the streaming output of the model contains tool calls.
-
-It is optional. If not filled in, the method of judging whether the "non-empty package" contains tool calls will be used:
+If a tool is `ReturnDirectly`, its output is returned immediately; configure `ToolReturnDirectly` with the tool name. For streaming models, set `StreamToolCallChecker` to determine tool-call presence in streams (model-dependent behavior).
+Default checker (first non-empty chunk must be tool-call):
```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
+ msg, err := sr.Recv()
+ if errors.Is(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 { continue }
+ return false, nil
}
}
```
-The above default implementation applies to the situation where there are only Tool Calls in the Tool Call Message output by the model.
-
-Situations where the default implementation does not apply: There is a non - empty content chunk before outputting the Tool Call. In this case, you need to customize the tool Call checker as follows:
+If the provider outputs non-empty text before tool-calls, implement a custom checker that scans all chunks for tool-calls:
```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
- }
+ msg, err := sr.Recv()
+ if errors.Is(err, io.EOF) { break }
+ if err != nil { return false, err }
+ if len(msg.ToolCalls) > 0 { return true, nil }
}
return false, nil
}
```
-The custom StreamToolCallChecker above may need to check whether all packages contain ToolCall in extreme cases, resulting in the loss of the "streaming judgment" effect. If you want to retain the "streaming judgment" effect as much as possible, the suggestion to solve this problem is:
-
-
-> 💡
-> Try to add a prompt to restrict the model from outputting additional text when invoking tools. For example: "If you need to invoke the tool, directly output the tool's name without additional text."
->
-> Different models may be affected by prompts to different extents. In actual use, you need to adjust the prompt by yourself and verify the effect.
+Tip: add a prompt like “If you need to call tools, output only tool-calls, not text” to preserve a streaming experience where possible.
-## **Invocation**
+## Invocation
-### **Generate**
+### Generate
```go
agent, _ := react.NewAgent(...)
var outMessage *schema.Message
outMessage, err = agent.Generate(ctx, []*schema.Message{
- schema.UserMessage("Write a hello world program in Go"),
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
})
```
-### **Stream**
+### Stream
```go
agent, _ := react.NewAgent(...)
var msgReader *schema.StreamReader[*schema.Message]
msgReader, err = agent.Stream(ctx, []*schema.Message{
- schema.UserMessage("Write a hello world program in Go"),
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
})
for {
@@ -360,12 +305,15 @@ for {
}
```
-### **WithCallbacks**
+### WithCallbacks
-Callback is a function that executes at specific times when the Agent is running. Since the Agent graph only includes ChatModel and ToolsNode, the Agent's Callback is essentially the Callback for the ChatModel and Tool. The react package provides a helper function to help users quickly build Callback Handlers for these two component types.
+Callback handlers run at defined timings. Since the agent graph has only ChatModel and ToolsNode, the agent’s callbacks are those two component callbacks. A helper is provided to build them:
```go
-// BuildAgentCallback builds a callback handler for the agent.
+import (
+ template "github.com/cloudwego/eino/utils/callbacks"
+)
+// BuildAgentCallback builds a callback handler for agent.
// e.g.
//
// callback := BuildAgentCallback(modelHandler, toolHandler)
@@ -376,35 +324,158 @@ func BuildAgentCallback(modelHandler *template.ModelCallbackHandler, toolHandler
}
```
-## Agent In Graph/Chain
+### Options
+
+React agent supports dynamic runtime options.
-Agent can be embedded as a Lambda into other Graphs:
+Scenario 1: modify the model config at runtime:
```go
-agent, _ := NewAgent(ctx, &AgentConfig{
- ToolCallingModel: cm,
- ToolsConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{fakeTool, &fakeStreamToolGreetForTest{}},
- },
+// 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...))
+}
+```
- MaxStep: 40,
-})
+Scenario 2: modify the Tool list at runtime:
+
+```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...)))
+}
+```
-chain := compose.NewChain[[]*schema.Message, string]()
-agentLambda, _ := compose.AnyLambda(agent.Generate, agent.Stream, nil, nil)
+Also update ChatModel’s bound tools: `WithChatModelOptions(model.WithTools(...))`
-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)
+Scenario 3: modify options for a specific Tool:
-res, _ := r.Invoke(ctx, []*schema.Message{{Role: schema.User, Content: "hello"}},
- compose.WithCallbacks(callbackForTest))
+```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...)))
+}
```
-## **Related Reading**
+### Get Intermediate Results
+
+Use `WithMessageFuture` to capture intermediate `*schema.Message` during execution:
+
+```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
+}
+```
+
+Pass the option into Generate or Stream. Use `GetMessages` or `GetMessageStreams` to read intermediate messages.
+
+Tip: the agent still runs synchronously. Read the future in a goroutine or run the agent in a goroutine.
+
+### Agent In Graph/Chain
+
+Agent can be embedded via `compose.AnyLambda` and appended to Chain/Graph.
+
+## Demo
+
+### Basic Info
+
+简介:这是一个拥有两个 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` 方法,因此流式返回的大模型结果。
+
+## Related Reading
- [Eino Tutorial: Host Multi-Agent ](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
diff --git a/content/en/docs/eino/ecosystem_integration/_index.md b/content/en/docs/eino/ecosystem_integration/_index.md
index a5637e65186..4ebdb86df53 100644
--- a/content/en/docs/eino/ecosystem_integration/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/_index.md
@@ -1,18 +1,18 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-07-21"
lastmod: ""
tags: []
-title: 'Eino: Component Integration'
-weight: 0
+title: 'Eino: Ecosystem Integration'
+weight: 5
---
-## Components
+## Ecosystem Integration
### ChatModel
- openai: [ChatModel - OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
-- ark: [ChatModel - ARK](/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- ark: [ChatModel - Ark](/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
- ollama: [ChatModel - Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
### Document
@@ -36,7 +36,7 @@ weight: 0
### Embedding
-- ark: [Embedding - ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark)
+- ark: [Embedding - Ark](/docs/eino/ecosystem_integration/embedding/embedding_ark)
- openai: [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai)
### Indexer
diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/_index.md b/content/en/docs/eino/ecosystem_integration/callbacks/_index.md
index 8ce39879359..91730eec94d 100644
--- a/content/en/docs/eino/ecosystem_integration/callbacks/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/callbacks/_index.md
@@ -1,10 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Callbacks
weight: 0
---
-
diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md b/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
index e5e3d35e0b2..0376afeb9cf 100644
--- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
+++ b/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
@@ -7,21 +7,7 @@ title: Callback - APMPlus
weight: 0
---
-Eino encapsulates APMPlus's trace and metrics capabilities based on [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) capabilities (see [Document](https://www.volcengine.com/docs/6431/69092) and [Console](https://console.volcengine.com/apmplus-server)).
-
-## Features
-
-- Implements `github.com/cloudwego/eino/callbacks.Handler`
-- Implements session functionality to associate multiple requests in a single session and conduct [AI Session Analysis](https://www.volcengine.com/docs/6431/1587839)
-- Easy integration with Eino's application
-
-## Installation
-
-```bash
-go get github.com/cloudwego/eino-ext/callbacks/apmplus
-```
-
-## Quick Start
+Eino provides APMPlus tracing and metrics wrappers based on [graph callbacks](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual). See APMPlus [docs](https://www.volcengine.com/docs/6431/69092) and [console](https://console.volcengine.com/apmplus-server). Example:
```go
package main
@@ -29,17 +15,17 @@ package main
import (
"context"
"log"
-
+
"github.com/cloudwego/eino-ext/callbacks/apmplus"
"github.com/cloudwego/eino/callbacks"
)
func main() {
ctx := context.Background()
- // Create apmplus handler
+ // create apmplus handler
cbh, showdown, err := apmplus.NewApmplusHandler(&apmplus.Config{
- Host: "apmplus-cn-beijing.volces.com:4317",
- AppKey: "appkey-xxx",
+ Host: "apmplus-cn-beijing.volces.com:4317",
+ AppKey: "xxx",
ServiceName: "eino-app",
Release: "release/v0.0.1",
})
@@ -47,33 +33,19 @@ func main() {
log.Fatal(err)
}
- // Set apmplus as a global callback
+ // set apmplus as global callback
callbacks.AppendGlobalHandlers(cbh)
-
+
g := NewGraph[string,string]()
/*
* compose and run graph
*/
- runner, _ := g.Compile(ctx)
- // To set session information, use apmplus.SetSession method
- ctx = apmplus.SetSession(ctx, apmplus.WithSessionID("your_session_id"), apmplus.WithUserID("your_user_id"))
- // Execute the runner
- result, _ := runner.Invoke(ctx, "input")
- /*
- * Process the result
- */
-
- // Exit after all trace and metrics reporting is complete
+
+ // wait until all traces and metrics are flushed before exit
showdown(ctx)
}
```
-You can view the trace and metrics in the [APMPlus](https://console.volcengine.com/apmplus-server):
+You can view traces and metrics in [APMPlus](https://console.volcengine.com/apmplus-server):
-
-After passing the Session information when calling the Eino application, you can view [AI Session Analysis](https://www.volcengine.com/docs/6431/1587839) in APMPlus:
-
-
-
-
diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md b/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
index 94b870ac588..281bc44f888 100644
--- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
+++ b/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
@@ -1,54 +1,54 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Callback - cozeloop
+title: Callback - CozeLoop
weight: 0
---
-# CozeLoop Callbacks
+# **CozeLoop Callback**
-A CozeLoop callback implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Handler` interface. This enables seamless integration with Eino's application for enhanced observability.
+This is a Trace callback implementation for [CozeLoop](https://github.com/cloudwego/eino). It implements the `Handler` interface and integrates seamlessly with Eino applications to provide enhanced observability.
-## Features
+## **Features**
-- Implements `github.com/cloudwego/eino/internel/callbacks.Handler`
-- Easy integration with Eino's application
+- Implements `github.com/cloudwego/eino/internel/callbacks.Handler` interface
+- Easy integration with Eino applications
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/callbacks/cozeloop
```
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- ccb "github.com/cloudwego/eino-ext/callbacks/cozeloop"
- "github.com/cloudwego/eino/callbacks"
- "github.com/coze-dev/cozeloop-go"
+ ccb "github.com/cloudwego/eino-ext/callbacks/cozeloop"
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/coze-dev/cozeloop-go"
)
func main() {
- // Set relevant environment variables
- // COZELOOP_WORKSPACE_ID=your workspace id
- // COZELOOP_API_TOKEN=your token
- client, err := cozeloop.NewClient()
- if err != nil {
- panic(err)
- }
- defer client.Close(ctx)
- // Call once during service initialization
- handler := ccb.NewLoopHandler(client)
- callbacks.AppendGlobalHandlers(handler)
+ // Set environment variables
+ // COZELOOP_WORKSPACE_ID=your workspace id
+ // COZELOOP_API_TOKEN=your token
+ client, err := cozeloop.NewClient()
+ if err != nil {
+ panic(err)
+ }
+ defer client.Close(ctx)
+ // Call once during service init
+ handler := ccb.NewLoopHandler(client)
+ callbacks.AppendGlobalHandlers(handler)
}
```
-## For More Details
-- [CozeLoop Documentation](https://github.com/coze-dev/cozeloop-go)
+## **More Details**
+- [CozeLoop Docs](https://github.com/coze-dev/cozeloop-go)
diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md b/content/en/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
index a7e7e503081..6e0e5a33d9e 100644
--- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
+++ b/content/en/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
@@ -7,9 +7,7 @@ title: Callback - Langfuse
weight: 0
---
-Eino encapsulates langfuse's trace capabilities based on [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) capabilities (see [https://langfuse.com/docs/get-started](https://langfuse.com/docs/get-started)).
-
-An example usage is as follows:
+Eino provides Langfuse tracing wrappers based on [graph callbacks](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) (see https://langfuse.com/docs/get-started). Example:
```go
package main
@@ -20,22 +18,23 @@ import (
)
func main() {
- cbh, flusher := langfuse.NewLangfuseHandler(&langfuse.Config{
+ cbh, flusher := NewLangfuseHandler(&Config{
Host: "https://cloud.langfuse.com",
PublicKey: "pk-xxx",
SecretKey: "sk-xxx",
})
- defer flusher() // Exit after all trace reporting is complete
-
- callbacks.AppendGlobalHandlers(cbh) // Set langfuse as a global callback
-
+
+ callbacks.AppendGlobalHandlers(cbh) // set langfuse as global callback
+
g := NewGraph[string,string]()
/*
* compose and run graph
*/
+
+ flusher() // wait until all traces are flushed before exit
}
```
-You can view the trace in the Langfuse project:
+You can view traces in the Langfuse project:
-
+
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/_index.md b/content/en/docs/eino/ecosystem_integration/chat_model/_index.md
index 1dd6d4f9587..3c2219951cf 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/_index.md
@@ -1,10 +1,8 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
title: ChatModel
weight: 0
---
-
-
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
index 8605f8f7eb2..22597cdf915 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
@@ -7,24 +7,24 @@ title: ChatModel - ark
weight: 0
---
-A Volcengine Ark model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A Volcengine Ark model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-This package provides two distinct models:
-- **ChatModel**: For text-based and multi-modal chat completions.
-- **ImageGenerationModel**: For generating images from text prompts or image.
-- **ResponseAPI**: Contains methods and other services that help with interacting with the openai API.
+This package provides three components:
+- **ChatModel**: for text-based and multi-modal chat completion.
+- **ImageGenerationModel**: for generating images from text prompts or images.
+- **ResponseAPI**: methods and helpers for interacting with the OpenAI-compatible API.
## Features
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for both chat completion, image generation and response api
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion, image generation, and Response API
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-## Installation
+## Quick Install
```bash
go get github.com/cloudwego/eino-ext/components/model/ark@latest
@@ -32,92 +32,91 @@ go get github.com/cloudwego/eino-ext/components/model/ark@latest
---
-## Chat Completion
+## Chat
This model is used for standard chat and text generation tasks.
### Quick Start
-Here's a quick example of how to use the `ChatModel`:
+Here's a quick example of how to use `ChatModel`:
```go
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "how do you generate answer for user question as a machine, please answer in short?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output: \n")
- log.Printf(" request_id: %s\n")
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- sr, err := chatModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- log.Printf("stream final output: \n")
- log.Printf(" request_id: %s\n")
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "how do you generate answer for user question as a machine, please answer in short?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output: \n")
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
+
+ sr, err := chatModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ log.Printf("stream final output: \n")
+ log.Printf(" request_id: %s \n")
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf("body: %s \n", string(respBody))
}
```
### Configuration
-The `ChatModel` can be configured using the `ark.ChatModelConfig` struct:
+`ChatModel` can be configured using the `ark.ChatModelConfig` struct:
```go
type ChatModelConfig struct {
@@ -189,97 +188,87 @@ type ChatModelConfig struct {
}
```
-### Request Options
-
-The `ChatModel` supports various request options to customize the behavior of API calls. Here are the available options:
-
-```go
-// WithCustomHeader sets custom headers for a single request
-// the headers will override all the headers given in ChatModelConfig.CustomHeader
-func WithCustomHeader(m map[string]string) model.Option {}
-```
-
---
## Image Generation
-This model is used specifically for generating images from text prompts.
+This model is specifically designed for generating images from text prompts.
### Quick Start
-Here's a quick example of how to use the `ImageGenerationModel`:
+Here's a quick example of how to use `ImageGenerationModel`:
```go
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and an image generation model ID
- imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_IMAGE_MODEL_ID"), // Use an appropriate image model ID
- })
-
- if err != nil {
- log.Fatalf("NewImageGenerationModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "a photo of a cat sitting on a table",
- },
- }
-
- msg, err := imageGenerationModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("\ngenerate output: \n")
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- sr, err := imageGenerationModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- log.Printf("stream output: \n")
- index := 0
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
- log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
- index++
- }
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and an image generation model ID
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_IMAGE_MODEL_ID"), // Use an appropriate image model ID
+ })
+
+ if err != nil {
+ log.Fatalf("NewImageGenerationModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "a photo of a cat sitting on a table",
+ },
+ }
+
+ msg, err := imageGenerationModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s", string(respBody))
+
+ sr, err := imageGenerationModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ log.Printf("stream output:")
+ index := 0
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
+ log.Printf("stream chunk %d: body: %s \n", index, string(respBody))
+ index++
+ }
}
```
### Configuration
-The `ImageGenerationModel` can be configured using the `ark.ImageGenerationConfig` struct:
+`ImageGenerationModel` can be configured using the `ark.ImageGenerationConfig` struct:
```go
-
type ImageGenerationConfig struct {
// For authentication, APIKey is required as the image generation API only supports API Key authentication.
// For authentication details, see: https://www.volcengine.com/docs/82379/1298459
@@ -350,914 +339,906 @@ type ImageGenerationConfig struct {
}
```
+## Examples
-## examples
-
-### generate
+### Text Generation
```go
-
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "how do you generate answer for user question as a machine, please answer in short?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("\ngenerate output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- sr, err := chatModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- log.Printf("stream final output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "how do you generate answer for user question as a machine, please answer in short?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s", string(respBody))
+
+ sr, err := chatModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ log.Printf("stream final output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
```
-### generate_with_image
+### Multimodal Support (Image Understanding)
```go
-
package main
import (
- "context"
- "encoding/base64"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "log"
+ "os"
- "github.com/cloudwego/eino/components/prompt"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- multiModalMsg := schema.UserMessage("")
- image, err := os.ReadFile("./examples/generate_with_image/eino.png")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- multiModalMsg.UserInputMultiContent = []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/png",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("Ark ChatModel output: \n%v", resp)
-
- // demonstrate how to use ChatTemplate to generate with image
- imgPlaceholder := "{img}"
- ctx = context.Background()
- chain := compose.NewChain[map[string]any, *schema.Message]()
- _ = chain.AppendChatTemplate(prompt.FromMessages(schema.FString,
- &schema.Message{
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imgPlaceholder,
- MIMEType: "image/png",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }))
- _ = chain.AppendChatModel(chatModel)
- r, err := chain.Compile(ctx)
- if err != nil {
- log.Fatalf("Compile failed, err=%v", err)
- }
-
- resp, err = r.Invoke(ctx, map[string]any{
- "img": imageStr,
- })
- if err != nil {
- log.Fatalf("Run failed, err=%v", err)
- }
-
- log.Printf("Ark ChatModel output with ChatTemplate: \n%v", resp)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ multiModalMsg := schema.UserMessage("")
+ image, err := os.ReadFile("./examples/generate_with_image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v \n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ multiModalMsg.UserInputMultiContent = []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ multiModalMsg,
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("Ark ChatModel output:%v \n", resp)
+
+ // demonstrate how to use ChatTemplate to generate with image
+ imgPlaceholder := "{img}"
+ ctx = context.Background()
+ chain := compose.NewChain[map[string]any, *schema.Message]()
+ _ = chain.AppendChatTemplate(prompt.FromMessages(schema.FString,
+ &schema.Message{
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imgPlaceholder,
+ MIMEType: "image/png",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ }))
+ _ = chain.AppendChatModel(chatModel)
+ r, err := chain.Compile(ctx)
+ if err != nil {
+ log.Fatalf("Compile failed, err=%v", err)
+ }
+
+ resp, err = r.Invoke(ctx, map[string]any{
+ "img": imageStr,
+ })
+ if err != nil {
+ log.Fatalf("Run failed, err=%v", err)
+ }
+
+ log.Printf("Ark ChatModel output with ChatTemplate:%v \n", resp)
}
```
-### stream
+### Streaming Generation
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- }, ark.WithReasoningEffort(arkModel.ReasoningEffortHigh))
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close() // do not forget to close the stream
-
- msgs := make([]*schema.Message, 0)
-
- log.Printf("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- msgs = append(msgs, msg)
- if err != nil {
- log.Printf("\nstream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Printf("ConcatMessages failed, err=%v", err)
- return
- }
-
- log.Printf("output: %s\n", msg.Content)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ }, ark.WithReasoningEffort(arkModel.ReasoningEffortHigh))
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close() // do not forget to close the stream
+
+ msgs := make([]*schema.Message, 0)
+
+ log.Printf("typewriter output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ msgs = append(msgs, msg)
+ if err != nil {
+ log.Printf("stream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Printf("ConcatMessages failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output: %s \n", msg.Content)
}
```
-### intent_tool
+### Tool Calling
```go
-
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Printf("BindForcedTools failed, err=%v", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output: \n%v", resp)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("BindForcedTools failed, err=%v", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output:%v \n", resp)
}
```
-### image_generate
+### Image Generation
```go
-
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+ "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func ptr[T any](v T) *T {
- return &v
+ return &v
}
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
-
- // Control the size of image generated by the model.
- Size: "1K",
-
- // Control whether to generate a set of images.
- SequentialImageGeneration: ark.SequentialImageGenerationAuto,
-
- // Control the maximum number of images to generate
- SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{
- MaxImages: ptr(2),
- },
-
- // Control the format of the generated jpeg image.
- ResponseFormat: ark.ImageResponseFormatURL,
-
- // Control whether to add a watermark to the generated image
- DisableWatermark: false,
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "generate two images of a cat",
- },
- }
-
- // Use ImageGeneration API
- msg, err := imageGenerationModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("\ngenerate output: \n")
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- sr, err := imageGenerationModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- log.Printf("stream output: \n")
- index := 0
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
-
- respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
- log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
- index++
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- log.Printf("stream final output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+
+ // Control the size of image generated by the model.
+ Size: "1K",
+
+ // Control whether to generate a set of images.
+ SequentialImageGeneration: ark.SequentialImageGenerationAuto,
+
+ // Control the maximum number of images to generate
+ SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{
+ MaxImages: ptr(2),
+ },
+
+ // Control the format of the generated jpeg image.
+ ResponseFormat: ark.ImageResponseFormatURL,
+
+ // Control whether to add a watermark to the generated image
+ DisableWatermark: false,
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "generate two images of a cat",
+ },
+ }
+
+ // Use ImageGeneration API
+ msg, err := imageGenerationModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
+
+ sr, err := imageGenerationModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ log.Printf("stream output:")
+ index := 0
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+
+ respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
+ log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
+ index++
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ log.Printf("stream final output:")
+ log.Printf(" request_id: %s \n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
```
-### prefixcache-contextapi
+### ContextAPI Prefix Cache
```go
-
package main
import (
- "context"
- "encoding/json"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- info, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
- schema.UserMessage("my name is megumin"),
- }, 3600)
- if err != nil {
- log.Fatalf("CreatePrefix failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "what is my name?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &info.ContextID,
- }))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("\ngenerate output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- outStreamReader, err := chatModel.Stream(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &info.ContextID,
- }))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- var msgs []*schema.Message
- for {
- item, e := outStreamReader.Recv()
- if e == io.EOF {
- break
- }
- if e != nil {
- log.Fatal(e)
- }
-
- msgs = append(msgs, item)
- }
- msg, err = schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- log.Printf("\nstream output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ info, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
+ schema.UserMessage("my name is megumin"),
+ }, 3600)
+ if err != nil {
+ log.Fatalf("CreatePrefix failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "what is my name?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &info.ContextID,
+ }))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("\ngenerate output: \n")
+ log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
+
+ outStreamReader, err := chatModel.Stream(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &info.ContextID,
+ }))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ var msgs []*schema.Message
+ for {
+ item, e := outStreamReader.Recv()
+ if e == io.EOF {
+ break
+ }
+ if e != nil {
+ log.Fatal(e)
+ }
+
+ msgs = append(msgs, item)
+ }
+ msg, err = schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ log.Printf("\nstream output: \n")
+ log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
```
-### prefixcache-responsesapi
+### Response API Prefix Cache
```go
-
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- Cache: &ark.CacheConfig{
- APIType: ptrOf(ark.ResponsesAPI),
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "article_content_extractor",
- Desc: "Extract key statements and chapter summaries from the provided article content",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "content": {
- Type: schema.String,
- Desc: "The full article content to analyze and extract key information from",
- Required: true,
- },
- }),
- },
- })
-
- if err != nil {
- log.Fatalf("BindTools failed, err=%v", err)
- }
-
- // create response prefix cache, note: more than 1024 tokens are required, otherwise the prefix cache cannot be created
- cacheInfo, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
- schema.SystemMessage(`Once upon a time, in a quaint little village surrounded by vast green forests and blooming meadows, there lived a spirited young girl known as Little Red Riding Hood. She earned her name from the vibrant red cape that her beloved grandmother had sewn for her, a gift that she cherished deeply. This cape was more than just a piece of clothing; it was a symbol of the bond between her and her grandmother, who lived on the other side of the great woods, near a sparkling brook that bubbled merrily all year round.
-
- One sunny morning, Little Red Riding Hood's mother called her into the cozy kitchen, where the aroma of freshly baked bread filled the air. “My dear,” she said, “your grandmother isn’t feeling well today. I want you to take her this basket of treats. There are some delicious cakes, a jar of honey, and her favorite herbal tea. Can you do that for me?”
-
- Little Red Riding Hood’s eyes sparkled with excitement as she nodded eagerly. “Yes, Mama! I’ll take good care of them!” Her mother handed her a beautifully woven basket, filled to the brim with goodies, and reminded her, “Remember to stay on the path and don’t talk to strangers.”
-
- “I promise, Mama!” she replied confidently, pulling her red hood over her head and setting off on her adventure. The sun shone brightly, and birds chirped merrily as she walked, making her feel like she was in a fairy tale.
-
- As she journeyed through the woods, the tall trees whispered secrets to one another, and colorful flowers danced in the gentle breeze. Little Red Riding Hood was so enchanted by the beauty around her that she began to hum a tune, her voice harmonizing with the sounds of nature.
-
- However, unbeknownst to her, lurking in the shadows was a cunning wolf. The wolf was known throughout the forest for his deceptive wit and insatiable hunger. He watched Little Red Riding Hood with keen interest, contemplating his next meal.
-
- “Good day, little girl!” the wolf called out, stepping onto the path with a friendly yet sly smile.
-
- Startled, she halted and took a step back. “Hello there! I’m just on my way to visit my grandmother,” she replied, clutching the basket tightly.
-
- “Ah, your grandmother! I know her well,” the wolf said, his eyes glinting with mischief. “Why don’t you pick some lovely flowers for her? I’m sure she would love them, and I’m sure there are many beautiful ones just off the path.”
-
- Little Red Riding Hood hesitated for a moment but was easily convinced by the wolf’s charming suggestion. “That’s a wonderful idea! Thank you!” she exclaimed, letting her curiosity pull her away from the safety of the path. As she wandered deeper into the woods, her gaze fixed on the vibrant blooms, the wolf took a shortcut towards her grandmother’s house.
-
- When the wolf arrived at Grandma’s quaint cottage, he knocked on the door with a confident swagger. “It’s me, Little Red Riding Hood!” he shouted in a high-pitched voice to mimic the girl.
-
- “Come in, dear!” came the frail voice of the grandmother, who had been resting on her cozy bed, wrapped in warm blankets. The wolf burst through the door, his eyes gleaming with the thrill of his plan.
-
- With astonishing speed, the wolf gulped down the unsuspecting grandmother whole. Afterward, he dressed in her nightgown, donning her nightcap and climbing into her bed. He lay there, waiting for Little Red Riding Hood to arrive, concealing his wicked smile behind a facade of innocence.
-
- Meanwhile, Little Red Riding Hood was merrily picking flowers, completely unaware of the impending danger. After gathering a beautiful bouquet of wildflowers, she finally made her way back to the path and excitedly skipped towards her grandmother’s cottage.
-
- Upon arriving, she noticed the door was slightly ajar. “Grandmother, it’s me!” she called out, entering the dimly lit home. It was silent, with only the faint sound of an old clock ticking in the background. She stepped into the small living room, a feeling of unease creeping over her.
-
- “Grandmother, are you here?” she asked, peeking into the bedroom. There, she saw a figure lying under the covers.
-
- “Grandmother, what big ears you have!” she exclaimed, taking a few cautious steps closer.
-
- “All the better to hear you with, my dear,” the wolf replied in a voice that was deceptively sweet.
-
- “Grandmother, what big eyes you have!” Little Red Riding Hood continued, now feeling an unsettling chill in the air.
-
- “All the better to see you with, my dear,” the wolf said, his eyes narrowing as he tried to contain his glee.
-
- “Grandmother, what big teeth you have!” she exclaimed, the terror flooding her senses as she began to realize this was no ordinary visit.
-
- “All the better to eat you with!” the wolf roared, springing out of the bed with startling speed.
-
- Just as the wolf lunged towards her, a brave woodsman, who had been passing by the cottage and heard the commotion, burst through the door. His strong presence was a beacon of hope in the dire situation. “Stay back, wolf!” he shouted with authority, brandishing his axe.
-
- The wolf, taken aback by the sudden intrusion, hesitated for a moment. Before he could react, the woodsman swung his axe with determination, and with a swift motion, he drove the wolf away, rescuing Little Red Riding Hood and her grandmother from certain doom.
-
- Little Red Riding Hood was shaking with fright, but relief washed over her as the woodsman helped her grandmother out from behind the bed where the wolf had hidden her. The grandmother, though shaken, was immensely grateful to the woodsman for his bravery. “Thank you so much! You saved us!” she cried, embracing him warmly.
-
- Little Red Riding Hood, still in shock but filled with gratitude, looked up at the woodsman and said, “I promise I will never stray from the path again. Thank you for being our hero!”
-
- From that day on, the woodland creatures spoke of the brave woodsman who saved Little Red Riding Hood and her grandmother. Little Red Riding Hood learned a valuable lesson about being cautious and listening to her mother’s advice. The bond between her and her grandmother grew stronger, and they often reminisced about that day’s adventure over cups of tea, surrounded by cookies and laughter.
-
- To ensure safety, Little Red Riding Hood always took extra precautions when traveling through the woods, carrying a small whistle her grandmother had given her. It would alert anyone nearby if she ever found herself in trouble again.
-
- And so, in the heart of that small village, life continued, filled with love, laughter, and the occasional adventure, as Little Red Riding Hood and her grandmother thrived, forever grateful for the friendship of the woodsman who had acted as their guardian that fateful day.
-
- And they all lived happily ever after.
-
- The end.`),
- }, 300)
- if err != nil {
- log.Fatalf("CreatePrefixCache failed, err=%v", err)
- }
-
- // use cache information in subsequent requests
- cacheOpt := &ark.CacheOption{
- APIType: ark.ResponsesAPI,
- HeadPreviousResponseID: &cacheInfo.ResponseID,
- }
-
- outMsg, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("What is the main idea expressed above?"),
- }, ark.WithCache(cacheOpt))
-
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- respID, ok := ark.GetResponseID(outMsg)
- if !ok {
- log.Fatalf("not found response id in message")
- }
-
- log.Printf("\ngenerate output: \n")
- log.Printf(" request_id: %s\n", respID)
- respBody, _ := json.MarshalIndent(outMsg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Cache: &ark.CacheConfig{
+ APIType: ptrOf(ark.ResponsesAPI),
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "article_content_extractor",
+ Desc: "Extract key statements and chapter summaries from the provided article content",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "content": {
+ Type: schema.String,
+ Desc: "The full article content to analyze and extract key information from",
+ Required: true,
+ },
+ }),
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("BindTools failed, err=%v", err)
+ }
+
+ // create response prefix cache, note: more than 1024 tokens are required, otherwise the prefix cache cannot be created
+ cacheInfo, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
+ schema.SystemMessage(`Once upon a time, in a quaint little village surrounded by vast green forests and blooming meadows, there lived a spirited young girl known as Little Red Riding Hood. She earned her name from the vibrant red cape that her beloved grandmother had sewn for her, a gift that she cherished deeply. This cape was more than just a piece of clothing; it was a symbol of the bond between her and her grandmother, who lived on the other side of the great woods, near a sparkling brook that bubbled merrily all year round.
+
+ One sunny morning, Little Red Riding Hood's mother called her into the cozy kitchen, where the aroma of freshly baked bread filled the air. “My dear,” she said, “your grandmother isn’t feeling well today. I want you to take her this basket of treats. There are some delicious cakes, a jar of honey, and her favorite herbal tea. Can you do that for me?”
+
+ Little Red Riding Hood’s eyes sparkled with excitement as she nodded eagerly. “Yes, Mama! I’ll take good care of them!” Her mother handed her a beautifully woven basket, filled to the brim with goodies, and reminded her, “Remember to stay on the path and don’t talk to strangers.”
+
+ “I promise, Mama!” she replied confidently, pulling her red hood over her head and setting off on her adventure. The sun shone brightly, and birds chirped merrily as she walked, making her feel like she was in a fairy tale.
+
+ As she journeyed through the woods, the tall trees whispered secrets to one another, and colorful flowers danced in the gentle breeze. Little Red Riding Hood was so enchanted by the beauty around her that she began to hum a tune, her voice harmonizing with the sounds of nature.
+
+ However, unbeknownst to her, lurking in the shadows was a cunning wolf. The wolf was known throughout the forest for his deceptive wit and insatiable hunger. He watched Little Red Riding Hood with keen interest, contemplating his next meal.
+
+ “Good day, little girl!” the wolf called out, stepping onto the path with a friendly yet sly smile.
+
+ Startled, she halted and took a step back. “Hello there! I’m just on my way to visit my grandmother,” she replied, clutching the basket tightly.
+
+ “Ah, your grandmother! I know her well,” the wolf said, his eyes glinting with mischief. “Why don’t you pick some lovely flowers for her? I’m sure she would love them, and I’m sure there are many beautiful ones just off the path.”
+
+ Little Red Riding Hood hesitated for a moment but was easily convinced by the wolf’s charming suggestion. “That’s a wonderful idea! Thank you!” she exclaimed, letting her curiosity pull her away from the safety of the path. As she wandered deeper into the woods, her gaze fixed on the vibrant blooms, the wolf took a shortcut towards her grandmother’s house.
+
+ When the wolf arrived at Grandma’s quaint cottage, he knocked on the door with a confident swagger. “It’s me, Little Red Riding Hood!” he shouted in a high-pitched voice to mimic the girl.
+
+ “Come in, dear!” came the frail voice of the grandmother, who had been resting on her cozy bed, wrapped in warm blankets. The wolf burst through the door, his eyes gleaming with the thrill of his plan.
+
+ With astonishing speed, the wolf gulped down the unsuspecting grandmother whole. Afterward, he dressed in her nightgown, donning her nightcap and climbing into her bed. He lay there, waiting for Little Red Riding Hood to arrive, concealing his wicked smile behind a facade of innocence.
+
+ Meanwhile, Little Red Riding Hood was merrily picking flowers, completely unaware of the impending danger. After gathering a beautiful bouquet of wildflowers, she finally made her way back to the path and excitedly skipped towards her grandmother’s cottage.
+
+ Upon arriving, she noticed the door was slightly ajar. “Grandmother, it’s me!” she called out, entering the dimly lit home. It was silent, with only the faint sound of an old clock ticking in the background. She stepped into the small living room, a feeling of unease creeping over her.
+
+ “Grandmother, are you here?” she asked, peeking into the bedroom. There, she saw a figure lying under the covers.
+
+ “Grandmother, what big ears you have!” she exclaimed, taking a few cautious steps closer.
+
+ “All the better to hear you with, my dear,” the wolf replied in a voice that was deceptively sweet.
+
+ “Grandmother, what big eyes you have!” Little Red Riding Hood continued, now feeling an unsettling chill in the air.
+
+ “All the better to see you with, my dear,” the wolf said, his eyes narrowing as he tried to contain his glee.
+
+ “Grandmother, what big teeth you have!” she exclaimed, the terror flooding her senses as she began to realize this was no ordinary visit.
+
+ “All the better to eat you with!” the wolf roared, springing out of the bed with startling speed.
+
+ Just as the wolf lunged towards her, a brave woodsman, who had been passing by the cottage and heard the commotion, burst through the door. His strong presence was a beacon of hope in the dire situation. “Stay back, wolf!” he shouted with authority, brandishing his axe.
+
+ The wolf, taken aback by the sudden intrusion, hesitated for a moment. Before he could react, the woodsman swung his axe with determination, and with a swift motion, he drove the wolf away, rescuing Little Red Riding Hood and her grandmother from certain doom.
+
+ Little Red Riding Hood was shaking with fright, but relief washed over her as the woodsman helped her grandmother out from behind the bed where the wolf had hidden her. The grandmother, though shaken, was immensely grateful to the woodsman for his bravery. “Thank you so much! You saved us!” she cried, embracing him warmly.
+
+ Little Red Riding Hood, still in shock but filled with gratitude, looked up at the woodsman and said, “I promise I will never stray from the path again. Thank you for being our hero!”
+
+ From that day on, the woodland creatures spoke of the brave woodsman who saved Little Red Riding Hood and her grandmother. Little Red Riding Hood learned a valuable lesson about being cautious and listening to her mother’s advice. The bond between her and her grandmother grew stronger, and they often reminisced about that day’s adventure over cups of tea, surrounded by cookies and laughter.
+
+ To ensure safety, Little Red Riding Hood always took extra precautions when traveling through the woods, carrying a small whistle her grandmother had given her. It would alert anyone nearby if she ever found herself in trouble again.
+
+ And so, in the heart of that small village, life continued, filled with love, laughter, and the occasional adventure, as Little Red Riding Hood and her grandmother thrived, forever grateful for the friendship of the woodsman who had acted as their guardian that fateful day.
+
+ And they all lived happily ever after.
+
+ The end.`),
+ }, 300)
+ if err != nil {
+ log.Fatalf("CreatePrefixCache failed, err=%v", err)
+ }
+
+ // use cache information in subsequent requests
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ResponsesAPI,
+ HeadPreviousResponseID: &cacheInfo.ResponseID,
+ }
+
+ outMsg, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("What is the main idea expressed above?"),
+ }, ark.WithCache(cacheOpt))
+
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ respID, ok := ark.GetResponseID(outMsg)
+ if !ok {
+ log.Fatalf("not found response id in message")
+ }
+
+ log.Printf("\ngenerate output: \n")
+ log.Printf(" request_id: %s\n", respID)
+ respBody, _ := json.MarshalIndent(outMsg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
func ptrOf[T any](v T) *T {
- return &v
+ return &v
}
```
-### sessioncache-contextapi
+### ContextAPI Session Cache
```go
-
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
-
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- instructions := []*schema.Message{
- schema.SystemMessage("Your name is superman"),
- }
-
- cacheInfo, err := chatModel.CreateSessionCache(ctx, instructions, 86400, nil)
- if err != nil {
- log.Fatalf("CreateSessionCache failed, err=%v", err)
- }
-
- thinking := &arkModel.Thinking{
- Type: arkModel.ThinkingTypeDisabled,
- }
-
- cacheOpt := &ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &cacheInfo.ContextID,
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- }
-
- msg, err := chatModel.Generate(ctx, instructions,
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- <-time.After(500 * time.Millisecond)
-
- msg, err = chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What's your name?",
- },
- },
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("\ngenerate output: \n")
- fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- fmt.Printf(" body: %s\n", string(respBody))
-
- outStreamReader, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What do I ask you last time?",
- },
- },
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- fmt.Println("\ntypewriter output:")
- var msgs []*schema.Message
- for {
- item, e := outStreamReader.Recv()
- if e == io.EOF {
- break
- }
- if e != nil {
- log.Fatal(e)
- }
-
- fmt.Print(item.Content)
- msgs = append(msgs, item)
- }
-
- msg, err = schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- fmt.Print("\n\nstream output: \n")
- fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- fmt.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ instructions := []*schema.Message{
+ schema.SystemMessage("Your name is superman"),
+ }
+
+ cacheInfo, err := chatModel.CreateSessionCache(ctx, instructions, 86400, nil)
+ if err != nil {
+ log.Fatalf("CreateSessionCache failed, err=%v", err)
+ }
+
+ thinking := &arkModel.Thinking{
+ Type: arkModel.ThinkingTypeDisabled,
+ }
+
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &cacheInfo.ContextID,
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, instructions,
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ <-time.After(500 * time.Millisecond)
+
+ msg, err = chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's your name?",
+ },
+ },
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ fmt.Printf("\ngenerate output: \n")
+ fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ fmt.Printf(" body: %s\n", string(respBody))
+
+ outStreamReader, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What do I ask you last time?",
+ },
+ },
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ fmt.Println("\ntypewriter output:")
+ var msgs []*schema.Message
+ for {
+ item, e := outStreamReader.Recv()
+ if e == io.EOF {
+ break
+ }
+ if e != nil {
+ log.Fatal(e)
+ }
+
+ fmt.Print(item.Content)
+ msgs = append(msgs, item)
+ }
+
+ msg, err = schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ fmt.Print("\n\nstream output: \n")
+ fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ fmt.Printf(" body: %s\n", string(respBody))
}
```
-### sessioncache-responsesapi
+### ResponseAPI Session Cache
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- Cache: &ark.CacheConfig{
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- thinking := &arkModel.Thinking{
- Type: arkModel.ThinkingTypeDisabled,
- }
- cacheOpt := &ark.CacheOption{
- APIType: ark.ResponsesAPI,
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- }
-
- useMsgs := []*schema.Message{
- schema.UserMessage("Your name is superman"),
- schema.UserMessage("What's your name?"),
- schema.UserMessage("What do I ask you last time?"),
- }
-
- var input []*schema.Message
- for _, msg := range useMsgs {
- input = append(input, msg)
-
- streamResp, err := chatModel.Stream(ctx, input,
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
-
- resp, err := schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of ark failed, err=%v", err)
- }
-
- fmt.Printf("stream output: \n%v\n\n", resp)
-
- input = append(input, resp)
- }
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Cache: &ark.CacheConfig{
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ thinking := &arkModel.Thinking{
+ Type: arkModel.ThinkingTypeDisabled,
+ }
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ResponsesAPI,
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ }
+
+ useMsgs := []*schema.Message{
+ schema.UserMessage("Your name is superman"),
+ schema.UserMessage("What's your name?"),
+ schema.UserMessage("What do I ask you last time?"),
+ }
+
+ var input []*schema.Message
+ for _, msg := range useMsgs {
+ input = append(input, msg)
+
+ streamResp, err := chatModel.Stream(ctx, input,
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ var messages []*schema.Message
+ for {
+ chunk, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamResp failed, err=%v", err)
+ }
+ messages = append(messages, chunk)
+ }
+
+ resp, err := schema.ConcatMessages(messages)
+ if err != nil {
+ log.Fatalf("ConcatMessages of ark failed, err=%v", err)
+ }
+
+ fmt.Printf("stream output: \n%v\n\n", resp)
+
+ input = append(input, resp)
+ }
}
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/ark/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Volcengine Ark Model Documentation](https://www.volcengine.com/docs/82379/1263272)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `ChatModel - OpenAI` at `/docs/eino/ecosystem_integration/chat_model/chat_model_openai`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
+- [Volcengine Official Site](https://www.volcengine.com/product/doubao)
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
index f1610434a2d..b02d5c9f68a 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: ChatModel - arkbot
+title: ChatModel - ARKBot
weight: 0
---
-A Volcengine Ark Bot implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A Volcengine Ark Bot implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.ToolCallingChatModel`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/arkbot@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the Ark Bot:
@@ -34,65 +34,64 @@ Here's a quick example of how to use the Ark Bot:
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output: \n")
- log.Printf(" request_id: %s\n", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s\n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s\n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-}
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s \n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s\n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
+}
```
-## Configuration
+## **Configuration**
-The model can be configured using the `arkbot.Config` struct:
+You can configure the model using the `arkbot.Config` struct:
```go
-
- type Config struct {
+
+type Config struct {
// Timeout specifies the maximum duration to wait for API responses
// If HTTPClient is set, Timeout will not be used.
// Optional. Default: 10 minutes
@@ -173,12 +172,10 @@ The model can be configured using the `arkbot.Config` struct:
// ResponseFormat specifies the format that the model must output.
ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
- }
+}
```
-## Request Options
-
-The Ark model supports various request options to customize the behavior of API calls. Here are the available options:
+## **Request Options**
```go
// WithCustomHeader sets custom headers for a single request
@@ -186,154 +183,151 @@ The Ark model supports various request options to customize the behavior of API
func WithCustomHeader(m map[string]string) model.Option {}
```
+## **Examples**
-## examples
-
-### generate
+### **Text Generation**
```go
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output: \n")
- log.Printf(" request_id: %s\n", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s\n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s\n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:\n")
+ log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s\n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s\n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
```
-### stream
+### **Streaming Generation**
```go
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close() // do not forget to close the stream
-
- msgs := make([]*schema.Message, 0)
-
- log.Printf("stream output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- msgs = append(msgs, msg)
- if err != nil {
- log.Printf("\nstream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Printf("ConcatMessages failed, err=%v", err)
- return
- }
-
- log.Printf("generate output: \n")
- log.Printf(" request_id: %s\n", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s\n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s\n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-}
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
-```
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close() // do not forget to close the stream
+
+ msgs := make([]*schema.Message, 0)
+
+ log.Printf("stream output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ msgs = append(msgs, msg)
+ if err != nil {
+ log.Printf("stream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Printf("ConcatMessages failed, err=%v", err)
+ return
+ }
+ log.Printf("generate output: \n")
+ log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s\n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s\n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
+}
+
+```
-## For More Details
+## **More Information**
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
+- [Eino Documentation](https://www.cloudwego.io/docs/eino/)
- [Volcengine Ark Model Documentation](https://www.volcengine.com/docs/82379/1263272)
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
index fa831a05a9f..032b27454a6 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - claude
weight: 0
---
-A Claude model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A Claude model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/claude@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the Claude model:
@@ -33,83 +33,83 @@ Here's a quick example of how to use the Claude model:
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- thinking, ok := claude.GetThinking(resp)
- fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ thinking, ok := claude.GetThinking(resp)
+ fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
```
-## Configuration
+## **Configuration**
-The model can be configured using the `claude.ChatModelConfig` struct:
+You can configure the model using the `claude.ChatModelConfig` struct:
```go
type Config struct {
@@ -190,685 +190,644 @@ type Config struct {
}
```
+## **Examples**
-
-
-
-## examples
-
-### generate
+### **Text Generation**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- thinking, ok := claude.GetThinking(resp)
- fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ thinking, ok := claude.GetThinking(resp)
+ fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
```
-### generate_with_image
+### **Multimodal Support (Image Understanding)**
```go
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- imageBinary, err := os.ReadFile("examples/test.jpg")
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(imageBinary)),
- MIMEType: "image/jpeg",
- },
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ imageBinary, err := os.ReadFile("examples/test.jpg")
+ if err != nil {
+ log.Fatalf("read file failed, err=%v", err)
+ }
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: of(base64.StdEncoding.EncodeToString(imageBinary)),
+ MIMEType: "image/jpeg",
+ },
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](v T) *T {
- return &v
+ return &v
}
```
-### stream
+### **Streaming Generation**
```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
- {
- Role: schema.User,
- Content: "Write a short poem about spring, word by word.",
- },
- }
-
- stream, err := cm.Stream(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Stream error: %v", err)
- return
- }
- isFirstThinking := false
- isFirstContent := false
-
- fmt.Print("Assistant: ----------\n")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("Stream receive error: %v", err)
- return
- }
-
- thinkingContent, ok := claude.GetThinking(resp)
- if ok {
- if !isFirstThinking {
- isFirstThinking = true
- fmt.Print("\nThinking: ----------\n")
- }
- fmt.Print(thinkingContent)
- }
-
- if len(resp.Content) > 0 {
- if !isFirstContent {
- isFirstContent = true
- fmt.Print("\nContent: ----------\n")
- }
- fmt.Print(resp.Content)
- }
- }
- fmt.Println("\n----------")
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring, word by word.",
+ },
+ }
+
+ stream, err := cm.Stream(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Stream error: %v", err)
+ return
+ }
+ isFirstThinking := false
+ isFirstContent := false
+
+ fmt.Print("Assistant: ----------\n")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("Stream receive error: %v", err)
+ return
+ }
+
+ thinkingContent, ok := claude.GetThinking(resp)
+ if ok {
+ if !isFirstThinking {
+ isFirstThinking = true
+ fmt.Print("\nThinking: ----------\n")
+ }
+ fmt.Print(thinkingContent)
+ }
+
+ if len(resp.Content) > 0 {
+ if !isFirstContent {
+ isFirstContent = true
+ fmt.Print("\nContent: ----------\n")
+ }
+ fmt.Print(resp.Content)
+ }
+ }
+ fmt.Println("\n----------")
}
```
-### intent_tool
+### **Claude Prompt Cache**
```go
package main
import (
- "context"
- "fmt"
-
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/claude"
- "github.com/cloudwego/eino/schema"
- "github.com/eino-contrib/jsonschema"
- orderedmap "github.com/wk8/go-ordered-map/v2"
+ "context"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- _, err = cm.WithTools([]*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get current weather information for a city",
- ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&jsonschema.Schema{
- Type: "object",
- Properties: orderedmap.New[string, *jsonschema.Schema](orderedmap.WithInitialData[string, *jsonschema.Schema](
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "city",
- Value: &jsonschema.Schema{
- Type: "string",
- Description: "The city name",
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "unit",
- Value: &jsonschema.Schema{
- Type: "string",
- Enum: []interface{}{"celsius", "fahrenheit"},
- },
- },
- )),
- Required: []string{"city"},
- }),
- },
- })
- if err != nil {
- log.Printf("Bind tools error: %v", err)
- return
- }
-
- streamResp, err := cm.Stream(ctx, []*schema.Message{
- schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
- schema.UserMessage("call 'get_weather' to query what's the weather like in Paris today? Please use Celsius."),
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- msgs := make([]*schema.Message, 0)
- for {
- msg, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Stream receive error: %v", err)
- }
- msgs = append(msgs, msg)
- }
- resp, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("Concat error: %v", err)
- }
-
- fmt.Printf("assistant content:\n %v\n----------\n", resp.Content)
- if len(resp.ToolCalls) > 0 {
- fmt.Printf("Function called: %s\n", resp.ToolCalls[0].Function.Name)
- fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
-
- weatherResp, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("What's the weather like in Paris today? Please use Celsius."),
- resp,
- schema.ToolMessage(`{"temperature": 18, "condition": "sunny"}`, resp.ToolCalls[0].ID),
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Final response: %s\n", weatherResp.Content)
- }
+ systemCache()
+ //toolInfoCache()
+ //sessionCache()
+}
+
+func systemCache() {
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ breakpoint := claude.SetMessageBreakpoint(&schema.Message{
+ Role: schema.System,
+ Content: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant.",
+ })
+
+ for i := 0; i < 2; i++ {
+ now := time.Now()
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are an AI assistant tasked with analyzing literary works. " +
+ "Your goal is to provide insightful commentary on themes.",
+ },
+ breakpoint,
+ {
+ Role: schema.User,
+ Content: "Analyze the major themes in the content.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
+ }
+}
+
+func toolInfoCache() {
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ breakpoint := claude.SetToolInfoBreakpoint(&schema.ToolInfo{
+ Name: "get_time",
+ Desc: "Get the current time in a given time zone",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "timezone": {
+ Required: true,
+ Type: "string",
+ Desc: "The IANA time zone name, e.g. America/Los_Angeles",
+ },
+ },
+ )},
+ )
+
+ mockTools := []*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get the current weather in a given location",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "location": {
+ Type: "string",
+ Desc: "The city and state, e.g. San Francisco, CA",
+ },
+ "unit": {
+ Type: "string",
+ Enum: []string{"celsius", "fahrenheit"},
+ Desc: "The unit of temperature, either celsius or fahrenheit",
+ },
+ },
+ ),
+ },
+ breakpoint,
+ }
+
+ chatModelWithTools, err := cm.WithTools(mockTools)
+ if err != nil {
+ log.Fatalf("WithTools failed, err=%v", err)
+ }
+
+ for i := 0; i < 2; i++ {
+ now := time.Now()
+
+ resp, err := chatModelWithTools.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a tool calling assistant.",
+ },
+ {
+ Role: schema.User,
+ Content: "Mock the get_weather tool input yourself, and then call get_weather",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
+ }
+}
+
+func sessionCache() {
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ opts := []model.Option{
+ claude.WithEnableAutoCache(true),
+ }
+
+ mockTools := []*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get the current weather in a given location",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "location": {
+ Type: "string",
+ Desc: "The city and state, e.g. San Francisco, CA",
+ },
+ "unit": {
+ Type: "string",
+ Enum: []string{"celsius", "fahrenheit"},
+ Desc: "The unit of temperature, either celsius or fahrenheit",
+ },
+ },
+ ),
+ },
+ }
+
+ chatModelWithTools, err := cm.WithTools(mockTools)
+ if err != nil {
+ log.Fatalf("WithTools failed, err=%v", err)
+ return
+ }
+
+ input := []*schema.Message{
+ schema.SystemMessage("You are a tool calling assistant."),
+ schema.UserMessage("What tools can you call?"),
+ }
+
+ resp, err := chatModelWithTools.Generate(ctx, input, opts...)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ return
+ }
+ log.Printf("output_1: \n%v\n\n", resp)
+
+ input = append(input, resp, schema.UserMessage("What am I asking last time?"))
+
+ resp, err = chatModelWithTools.Generate(ctx, input, opts...)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output_2: \n%v\n\n", resp)
}
```
-### claude_prompt_cache
+### **function_call**
```go
package main
import (
- "context"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/claude"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
-)
+ "context"
+ "fmt"
-func main() {
- systemCache()
- //toolInfoCache()
- //sessionCache()
-}
-
-func systemCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- breakpoint := claude.SetMessageBreakpoint(&schema.Message{
- Role: schema.System,
- Content: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
- })
-
- for i := 0; i < 2; i++ {
- now := time.Now()
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are an AI assistant tasked with analyzing literary works. " +
- "Your goal is to provide insightful commentary on themes.",
- },
- breakpoint,
- {
- Role: schema.User,
- Content: "Analyze the major themes in the content.",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
- }
-}
+ "io"
+ "log"
+ "os"
-func toolInfoCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- breakpoint := claude.SetToolInfoBreakpoint(&schema.ToolInfo{
- Name: "get_time",
- Desc: "Get the current time in a given time zone",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "timezone": {
- Required: true,
- Type: "string",
- Desc: "The IANA time zone name, e.g. America/Los_Angeles",
- },
- },
- )},
- )
-
- mockTools := []*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get the current weather in a given location",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "location": {
- Type: "string",
- Desc: "The city and state, e.g. San Francisco, CA",
- },
- "unit": {
- Type: "string",
- Enum: []string{"celsius", "fahrenheit"},
- Desc: "The unit of temperature, either celsius or fahrenheit",
- },
- },
- ),
- },
- {
- Name: "mock_tool_1",
- Desc: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "mock parameter",
- },
- },
- ),
- },
- {
- Name: "mock_tool_2",
- Desc: "For the administration, the biggest gain was Japan's commitment to investing $550 billion in the United States, particularly with funds to be directed to sectors deemed strategic, ranging from semiconductors and pharmaceuticals to energy infrastructure and critical minerals.The White House highlighted that the deal represents what it called the \"single largest foreign investment commitment ever secured by any country.\" It touted the agreement as a \"strategic realignment of the U.S.-Japan economic relationship\" that will advance the mutual interests of both countries.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "Bessent said it will check the extent of Japan's compliance every quarter and \"if the president is unhappy, then they will boomerang back to the 25 percent tariff rates, both on cars and the rest of their products.\"",
- },
- },
- ),
- },
- {
- Name: "mock_tool_3",
- Desc: "Of the amount, up to 100,000 tons is imported for direct consumption. Most of the remainder is used for animal feed and processing into other food products. Any rice imported to Japan beyond the special quota is subject to a tariff of 341 yen ($2.3) per kilogram.\n\n\n In fiscal 2024, the United States shipped roughly 346,000 tons of rice to Japan, compared with 286,000 tons exported by Thailand and 70,000 tons by Australia, under the quota system, according to Japan's farm ministry.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "Currently, Japan imports about 770,000 tons of tariff-free rice a year under the WTO framework, with the United States the No. 1 exporter, accounting for nearly half of the total, followed by Thailand and Australia.",
- },
- },
- ),
- },
- breakpoint,
- }
-
- chatModelWithTools, err := cm.WithTools(mockTools)
- if err != nil {
- log.Fatalf("WithTools failed, err=%v", err)
- }
-
- for i := 0; i < 2; i++ {
- now := time.Now()
-
- resp, err := chatModelWithTools.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a tool calling assistant.",
- },
- {
- Role: schema.User,
- Content: "Mock the get_weather tool input yourself, and then call get_weather",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
- }
-}
+ "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino/schema"
+ "github.com/eino-contrib/jsonschema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
+)
-func sessionCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- opts := []model.Option{
- claude.WithEnableAutoCache(true),
- }
-
- mockTools := []*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get the current weather in a given location",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "location": {
- Type: "string",
- Desc: "The city and state, e.g. San Francisco, CA",
- },
- "unit": {
- Type: "string",
- Enum: []string{"celsius", "fahrenheit"},
- Desc: "The unit of temperature, either celsius or fahrenheit",
- },
- },
- ),
- },
- }
-
- chatModelWithTools, err := cm.WithTools(mockTools)
- if err != nil {
- log.Fatalf("WithTools failed, err=%v", err)
- return
- }
-
- input := []*schema.Message{
- schema.SystemMessage("You are a tool calling assistant."),
- schema.UserMessage("What tools can you call?"),
- }
-
- resp, err := chatModelWithTools.Generate(ctx, input, opts...)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- return
- }
- log.Printf("output_1: \n%v\n\n", resp)
-
- input = append(input, resp, schema.UserMessage("What am I asking last time?"))
-
- resp, err = chatModelWithTools.Generate(ctx, input, opts...)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output_2: \n%v\n\n", resp)
+func main() {
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ _, err = cm.WithTools([]*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get current weather information for a city",
+ ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&jsonschema.Schema{
+ Type: "object",
+ Properties: orderedmap.New[string, *jsonschema.Schema](orderedmap.WithInitialData[string, *jsonschema.Schema](
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "city",
+ Value: &jsonschema.Schema{
+ Type: "string",
+ Description: "The city name",
+ },
+ },
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "unit",
+ Value: &jsonschema.Schema{
+ Type: "string",
+ Enum: []interface{}{"celsius", "fahrenheit"},
+ },
+ },
+ )),
+ Required: []string{"city"},
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("Bind tools error: %v", err)
+ return
+ }
+
+ streamResp, err := cm.Stream(ctx, []*schema.Message{
+ schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
+ schema.UserMessage("call 'get_weather' to query what's the weather like in Paris today? Please use Celsius."),
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ msgs := make([]*schema.Message, 0)
+ for {
+ msg, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream receive error: %v", err)
+ }
+ msgs = append(msgs, msg)
+ }
+ resp, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("Concat error: %v", err)
+ }
+
+ fmt.Printf("assistant content:\n %v\n----------\n", resp.Content)
+ if len(resp.ToolCalls) > 0 {
+ fmt.Printf("Function called: %s\n", resp.ToolCalls[0].Function.Name)
+ fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
+
+ weatherResp, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("What's the weather like in Paris today? Please use Celsius."),
+ resp,
+ schema.ToolMessage(`{"temperature": 18, "condition": "sunny"}`, resp.ToolCalls[0].ID),
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Final response: %s\n", weatherResp.Content)
+ }
}
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/claude/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Claude Documentation](https://docs.claude.com/en/api/messages)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `Eino: ToolsNode & Tool Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- `ChatModel - ARK` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ark`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
index c58934a760e..6ee312e5ac4 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - deepseek
weight: 0
---
-A DeepSeek model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A DeepSeek model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/deepseek@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the DeepSeek model:
@@ -33,455 +33,449 @@ Here's a quick example of how to use the DeepSeek model:
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
-
-
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- reasoning, ok := deepseek.GetReasoningContent(resp)
- if !ok {
- fmt.Printf("Unexpected: non-reasoning")
- } else {
- fmt.Printf("Reasoning Content: %s\n", reasoning)
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ reasoning, ok := deepseek.GetReasoningContent(resp)
+ if !ok {
+ fmt.Printf("Unexpected: non-reasoning")
+ } else {
+ fmt.Printf("Reasoning Content: %s\n", reasoning)
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMetaUsage.TotalTokens)
+ }
}
```
-## Configuration
+## **Configuration**
-The model can be configured using the `deepseek.ChatModelConfig` struct:
+You can configure the model using the `deepseek.ChatModelConfig` struct:
```go
type ChatModelConfig struct {
- // APIKey is your authentication key
- // Required
- APIKey string `json:"api_key"`
-
- // Timeout specifies the maximum duration to wait for API responses
- // Optional. Default: 5 minutes
- Timeout time.Duration `json:"timeout"`
-
- // HTTPClient specifies the client to send HTTP requests.
- // Optional. Default http.DefaultClient
- HTTPClient *http.Client `json:"http_client"`
-
- // BaseURL is your custom deepseek endpoint url
- // Optional. Default: https://api.deepseek.com/
- BaseURL string `json:"base_url"`
-
- // Path sets the path for the API request. Defaults to "chat/completions", if not set.
- // Example usages would be "/c/chat/" or any http after the baseURL extension
- // Path 用于设置 API 请求的路径。如果未设置,则默认为 "chat/completions"。
- // 用法示例可以是 "/c/chat/" 或 baseURL 之后的任何 http 路径。
- Path string `json:"path"`
-
- // The following fields correspond to DeepSeek's chat API parameters
- // Ref: https://api-docs.deepseek.com/api/create-chat-completion
-
- // Model specifies the ID of the model to use
- // Required
- Model string `json:"model"`
-
- // MaxTokens limits the maximum number of tokens that can be generated in the chat completion
- // Range: [1, 8192].
- // Optional. Default: 4096
- MaxTokens int `json:"max_tokens,omitempty"`
-
- // Temperature specifies what sampling temperature to use
- // Generally recommend altering this or TopP but not both.
- // Range: [0.0, 2.0]. Higher values make output more random
- // Optional. Default: 1.0
- Temperature float32 `json:"temperature,omitempty"`
-
- // TopP controls diversity via nucleus sampling
- // Generally recommend altering this or Temperature but not both.
- // Range: [0.0, 1.0]. Lower values make output more focused
- // Optional. Default: 1.0
- TopP float32 `json:"top_p,omitempty"`
-
- // Stop sequences where the API will stop generating further tokens
- // Optional. Example: []string{"\n", "User:"}
- Stop []string `json:"stop,omitempty"`
-
- // PresencePenalty prevents repetition by penalizing tokens based on presence
- // Range: [-2.0, 2.0]. Positive values increase likelihood of new topics
- // Optional. Default: 0
- PresencePenalty float32 `json:"presence_penalty,omitempty"`
-
- // ResponseFormat specifies the format of the model's response
- // Optional. Use for structured outputs
- ResponseFormatType ResponseFormatType `json:"response_format_type,omitempty"`
-
- // FrequencyPenalty prevents repetition by penalizing tokens based on frequency
- // Range: [-2.0, 2.0]. Positive values decrease likelihood of repetition
- // Optional. Default: 0
- FrequencyPenalty float32 `json:"frequency_penalty,omitempty"`
-
- // LogProbs specifies whether to return log probabilities of the output tokens.
- LogProbs bool `json:"log_probs"`
-
- // TopLogProbs specifies the number of most likely tokens to return at each token position, each with an associated log probability.
- TopLogProbs int `json:"top_log_probs"`
+ // APIKey is your authentication key
+ // Required
+ APIKey string `json:"api_key"`
+
+ // Timeout specifies the maximum duration to wait for API responses
+ // Optional. Default: 5 minutes
+ Timeout time.Duration `json:"timeout"`
+
+ // HTTPClient specifies the client to send HTTP requests.
+ // Optional. Default http.DefaultClient
+ HTTPClient *http.Client `json:"http_client"`
+
+ // BaseURL is your custom deepseek endpoint url
+ // Optional. Default: https://api.deepseek.com/
+ BaseURL string `json:"base_url"`
+
+ // Path sets the path for the API request. Defaults to "chat/completions", if not set.
+ // Example usages would be "/c/chat/" or any http after the baseURL extension
+ // Path 用于设置 API 请求的路径。如果未设置,则默认为 "chat/completions"。
+ // 用法示例可以是 "/c/chat/" 或 baseURL 之后的任何 http 路径。
+ Path string `json:"path"`
+
+ // The following fields correspond to DeepSeek's chat API parameters
+ // Ref: https://api-docs.deepseek.com/api/create-chat-completion
+
+ // Model specifies the ID of the model to use
+ // Required
+ Model string `json:"model"`
+
+ // MaxTokens limits the maximum number of tokens that can be generated in the chat completion
+ // Range: [1, 8192].
+ // Optional. Default: 4096
+ MaxTokens int `json:"max_tokens,omitempty"`
+
+ // Temperature specifies what sampling temperature to use
+ // Generally recommend altering this or TopP but not both.
+ // Range: [0.0, 2.0]. Higher values make output more random
+ // Optional. Default: 1.0
+ Temperature float32 `json:"temperature,omitempty"`
+
+ // TopP controls diversity via nucleus sampling
+ // Generally recommend altering this or Temperature but not both.
+ // Range: [0.0, 1.0]. Lower values make output more focused
+ // Optional. Default: 1.0
+ TopP float32 `json:"top_p,omitempty"`
+
+ // Stop sequences where the API will stop generating further tokens
+ // Optional. Example: []string{"\n", "User:"}
+ Stop []string `json:"stop,omitempty"`
+
+ // PresencePenalty prevents repetition by penalizing tokens based on presence
+ // Range: [-2.0, 2.0]. Positive values increase likelihood of new topics
+ // Optional. Default: 0
+ PresencePenalty float32 `json:"presence_penalty,omitempty"`
+
+ // ResponseFormat specifies the format of the model's response
+ // Optional. Use for structured outputs
+ ResponseFormatType ResponseFormatType `json:"response_format_type,omitempty"`
+
+ // FrequencyPenalty prevents repetition by penalizing tokens based on frequency
+ // Range: [-2.0, 2.0]. Positive values decrease likelihood of repetition
+ // Optional. Default: 0
+ FrequencyPenalty float32 `json:"frequency_penalty,omitempty"`
+
+ // LogProbs specifies whether to return log probabilities of the output tokens.
+ LogProbs bool `json:"log_probs"`
+
+ // TopLogProbs specifies the number of most likely tokens to return at each token position, each with an associated log probability.
+ TopLogProbs int `json:"top_log_probs"`
}
```
+## **Examples**
-
-
-
-## examples
-
-### generate
+### **Text Generation**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
-
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- reasoning, ok := deepseek.GetReasoningContent(resp)
- if !ok {
- fmt.Printf("Unexpected: non-reasoning")
- } else {
- fmt.Printf("Reasoning Content: %s\n", reasoning)
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ reasoning, ok := deepseek.GetReasoningContent(resp)
+ if !ok {
+ fmt.Printf("Unexpected: non-reasoning")
+ } else {
+ fmt.Printf("Reasoning Content: %s\n", reasoning)
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
```
-### generate_with_prefix
+### **Text Generation with Prefix**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- schema.UserMessage("Please write quick sort code"),
- schema.AssistantMessage("```python\n", nil),
- }
- deepseek.SetPrefix(messages[1])
-
- result, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- }
-
- reasoningContent, ok := deepseek.GetReasoningContent(result)
- if !ok {
- fmt.Printf("No reasoning content")
- } else {
- fmt.Printf("Reasoning: %v\n", reasoningContent)
- }
- fmt.Printf("Content: %v\n", result)
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ schema.UserMessage("Please write quick sort code"),
+ schema.AssistantMessage("```python\n", nil),
+ }
+ deepseek.SetPrefix(messages[1])
+
+ result, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ }
+
+ reasoningContent, ok := deepseek.GetReasoningContent(result)
+ if !ok {
+ fmt.Printf("No reasoning content")
+ } else {
+ fmt.Printf("Reasoning: %v\n", reasoningContent)
+ }
+ fmt.Printf("Content: %v\n", result)
}
```
-### stream
+### **Streaming Generation**
```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.User,
- Content: "Write a short poem about spring, word by word.",
- },
- }
-
- stream, err := cm.Stream(ctx, messages)
- if err != nil {
- log.Printf("Stream error: %v", err)
- return
- }
-
- fmt.Print("Assistant: ")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("Stream receive error: %v", err)
- return
- }
- if reasoning, ok := deepseek.GetReasoningContent(resp); ok {
- fmt.Printf("Reasoning Content: %s\n", reasoning)
- }
- if len(resp.Content) > 0 {
- fmt.Printf("Content: %s\n", resp.Content)
- }
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring, word by word.",
+ },
+ }
+
+ stream, err := cm.Stream(ctx, messages)
+ if err != nil {
+ log.Printf("Stream error: %v", err)
+ return
+ }
+
+ fmt.Print("Assistant: ")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("Stream receive error: %v", err)
+ return
+ }
+ if reasoning, ok := deepseek.GetReasoningContent(resp); ok {
+ fmt.Printf("Reasoning Content: %s\n", reasoning)
+ }
+ if len(resp.Content) > 0 {
+ fmt.Printf("Content: %s\n", resp.Content)
+ }
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
+ }
}
```
-### intent_tool
+### **Tool Calling**
```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- _, err = cm.WithTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Retrieve the user's company and position based on their name and email.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"}}),
- }, {
- Name: "user_salary",
- Desc: "Retrieve the user's salary based on their name and email.\n",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"},
- }),
- }})
- if err != nil {
- log.Fatalf("BindTools of deepseek failed, err=%v", err)
- }
- resp, err := cm.Generate(ctx, []*schema.Message{{
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- }})
- if err != nil {
- log.Fatalf("Generate of deepseek failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
-
- streamResp, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- },
- })
- if err != nil {
- log.Fatalf("Stream of deepseek failed, err=%v", err)
- }
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
- resp, err = schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of deepseek failed, err=%v", err)
- }
- fmt.Printf("stream output: \n%v", resp)
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ _, err = cm.WithTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Retrieve the user's company and position based on their name and email.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"}},
+ ),
+ }, {
+ Name: "user_salary",
+ Desc: "Retrieve the user's salary based on their name and email.\n",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"},
+ },
+ ),
+ }})
+ if err != nil {
+ log.Fatalf("BindTools of deepseek failed, err=%v", err)
+ }
+ resp, err := cm.Generate(ctx, []*schema.Message{{
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ }})
+ if err != nil {
+ log.Fatalf("Generate of deepseek failed, err=%v", err)
+ }
+ fmt.Printf("output: \n%v", resp)
+
+ streamResp, err := cm.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream of deepseek failed, err=%v", err)
+ }
+ var messages []*schema.Message
+ for {
+ chunk, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamResp failed, err=%v", err)
+ }
+ messages = append(messages, chunk)
+ }
+ resp, err = schema.ConcatMessages(messages)
+ if err != nil {
+ log.Fatalf("ConcatMessages of deepseek failed, err=%v", err)
+ }
+ fmt.Printf("stream output: \n%v", resp)
}
```
+## **More Information**
-
-
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
+- [Eino Documentation](https://www.cloudwego.io/docs/eino/)
- [DeepSeek Documentation](https://api-docs.deepseek.com/api/create-chat-completion)
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
index 2cd01849d88..e8a57d87b63 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - gemini
@@ -9,24 +9,24 @@ weight: 0
A Google Gemini implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-- Caching support for generated responses
+- Supports caching of generated responses
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/gemini@latest
```
-## Quick start
+## **Quick Start**
Here's a quick example of how to use the Gemini model:
@@ -34,812 +34,806 @@ Here's a quick example of how to use the Gemini model:
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: "gemini-2.5-flash",
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- // If you are using a model that supports image understanding (e.g., gemini-2.5-flash-image-preview),
- // you can provide both image and text input like this:
- /*
- image, err := os.ReadFile("./path/to/your/image.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- */
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- fmt.Printf("Assistant: %s\n", resp.Content)
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
+ apiKey := os.Getenv("GEMINI_API_KEY")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: "gemini-1.5-flash",
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ // If you are using a model that supports image understanding (e.g., gemini-1.5-flash-image-preview),
+ // you can provide both image and text input like this:
+ /*
+ image, err := os.ReadFile("./path/to/your/image.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ */
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
}
```
-## Configuration
+## **Configuration**
-The model can be configured using the `gemini.Config` struct:
+You can configure the model using the `gemini.Config` struct:
```go
type Config struct {
- // Client is the Gemini API client instance
- // Required for making API calls to Gemini
- Client *genai.Client
-
- // Model specifies which Gemini model to use
- // Examples: "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"
- Model string
-
- // MaxTokens limits the maximum number of tokens in the response
- // Optional. Example: maxTokens := 100
- MaxTokens *int
-
- // Temperature controls randomness in responses
- // Range: [0.0, 1.0], where 0.0 is more focused and 1.0 is more creative
- // Optional. Example: temperature := float32(0.7)
- Temperature *float32
-
- // TopP controls diversity via nucleus sampling
- // Range: [0.0, 1.0], where 1.0 disables nucleus sampling
- // Optional. Example: topP := float32(0.95)
- TopP *float32
-
- // TopK controls diversity by limiting the top K tokens to sample from
- // Optional. Example: topK := int32(40)
- TopK *int32
-
- // ResponseSchema defines the structure for JSON responses
- // Optional. Used when you want structured output in JSON format
- ResponseSchema *openapi3.Schema
-
- // EnableCodeExecution allows the model to execute code
- // Warning: Be cautious with code execution in production
- // Optional. Default: false
- EnableCodeExecution bool
-
- // SafetySettings configures content filtering for different harm categories
- // Controls the model's filtering behavior for potentially harmful content
- // Optional.
- SafetySettings []*genai.SafetySetting
-
- ThinkingConfig *genai.ThinkingConfig
-
- // ResponseModalities specifies the modalities the model can return.
- // Optional.
- ResponseModalities []
-
- MediaResolution genai.MediaResolution
-
- // Cache controls prefix cache settings for the model.
- // Optional. used to CreatePrefixCache for reused inputs.
- Cache *CacheConfig
+ // Client is the Gemini API client instance
+ // Required for making API calls to Gemini
+ Client *genai.Client
+
+ // Model specifies which Gemini model to use
+ // Examples: "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"
+ Model string
+
+ // MaxTokens limits the maximum number of tokens in the response
+ // Optional. Example: maxTokens := 100
+ MaxTokens *int
+
+ // Temperature controls randomness in responses
+ // Range: [0.0, 1.0], where 0.0 is more focused and 1.0 is more creative
+ // Optional. Example: temperature := float32(0.7)
+ Temperature *float32
+
+ // TopP controls diversity via nucleus sampling
+ // Range: [0.0, 1.0], where 1.0 disables nucleus sampling
+ // Optional. Example: topP := float32(0.95)
+ TopP *float32
+
+ // TopK controls diversity by limiting the top K tokens to sample from
+ // Optional. Example: topK := int32(40)
+ TopK *int32
+
+ // ResponseSchema defines the structure for JSON responses
+ // Optional. Used when you want structured output in JSON format
+ ResponseSchema *openapi3.Schema
+
+ // EnableCodeExecution allows the model to execute code
+ // Warning: Be cautious with code execution in production
+ // Optional. Default: false
+ EnableCodeExecution bool
+
+ // SafetySettings configures content filtering for different harm categories
+ // Controls the model's filtering behavior for potentially harmful content
+ // Optional.
+ SafetySettings []*genai.SafetySetting
+
+ ThinkingConfig *genai.ThinkingConfig
+
+ // ResponseModalities specifies the modalities the model can return.
+ // Optional.
+ ResponseModalities []
+
+ MediaResolution genai.MediaResolution
+
+ // Cache controls prefix cache settings for the model.
+ // Optional. used to CreatePrefixCache for reused inputs.
+ Cache *CacheConfig
}
// CacheConfig controls prefix cache settings for the model.
type CacheConfig struct {
- // TTL specifies how long cached resources remain valid (now + TTL).
- TTL time.Duration `json:"ttl,omitempty"`
- // ExpireTime sets the absolute expiration timestamp for cached resources.
- ExpireTime time.Time `json:"expireTime,omitempty"`
+ // TTL specifies how long cached resources remain valid (now + TTL).
+ TTL time.Duration `json:"ttl,omitempty"`
+ // ExpireTime sets the absolute expiration timestamp for cached resources.
+ ExpireTime time.Time `json:"expireTime,omitempty"`
}
```
-## Caching
+## **Cache**
This component supports two caching strategies to improve latency and reduce API calls:
-- Explicit caching (prefix cache): Build a reusable context from the system instruction, tools, and messages. Use `CreatePrefixCache` to create the cache and pass its name with `gemini.WithCachedContentName(...)` in subsequent requests. Configure TTL and absolute expiry via `CacheConfig` (`TTL`, `ExpireTime`). When a cached content is used, the request omits system instruction and tools and relies on the cached prefix.
-- Implicit caching: Managed by Gemini itself. The service may reuse prior requests or responses automatically. Expiry and reuse are controlled by Gemini and cannot be configured.
+- Explicit cache (prefix cache): Builds reusable context from system instructions, tools, and messages. Use `CreatePrefixCache` to create a cache, and pass its name in subsequent requests with `gemini.WithCachedContentName(...)`. Configure TTL and absolute expiration with `CacheConfig` (`TTL`, `ExpireTime`). When using cached content, the request omits system instructions and tools, relying on the cached prefix.
+- Implicit cache: Managed by Gemini itself. The service may automatically reuse previous requests or responses. Expiration and reuse are controlled by Gemini and cannot be configured.
-```
+The example below shows how to create a prefix cache and reuse it in subsequent calls.
+```go
toolInfoList := []*schema.ToolInfo{
- {
- Name: "tool_a",
- Desc: "desc",
- ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{}),
- },
+ {
+ Name: "tool_a",
+ Desc: "desc",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{}),
+ },
}
cacheInfo, _ := cm.CreatePrefixCache(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: `aaa`,
- },
- {
- Role: schema.User,
- Content: `bbb`,
- },
- }, model.WithTools(toolInfoList))
+ {
+ Role: schema.System,
+ Content: `aaa`,
+ },
+ {
+ Role: schema.User,
+ Content: `bbb`,
+ },
+ }, model.WithTools(toolInfoList))
msg, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "give a very short summary about this transcript",
- },
- }, gemini.WithCachedContentName(cacheInfo.Name))
+ {
+ Role: schema.User,
+ Content: "give a very short summary about this transcript",
+ },
+ }, gemini.WithCachedContentName(cacheInfo.Name))
```
-The example above shows how to create a prefix cache and reuse it in a follow-up call.
-
-## examples
+## **Examples**
-### generate
+### **Text Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- fmt.Printf("Assistant: %s\n", resp.Content)
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
}
```
-### generate_with_image
+### **Multimodal Support (Image Understanding)**
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
```
-### generate_with_prefix_cache
+### **Text Generation with Prefix Cache**
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/components/tool/utils"
- "github.com/cloudwego/eino/schema"
- "google.golang.org/genai"
-
- "github.com/cloudwego/eino-ext/components/model/gemini"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
- ctx := context.Background()
-
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: os.Getenv("GEMINI_API_KEY"),
- })
- if err != nil {
- log.Fatalf("genai.NewClient failed: %v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Model: os.Getenv("GEMINI_MODEL"),
- Client: client,
- })
- if err != nil {
- log.Fatalf("gemini.NewChatModel failed: %v", err)
- }
-
- type toolCallInput struct {
- Answer int `json:"answer" jsonschema_description:"the answer of the question"`
- }
- answerTool, err := utils.InferTool("answer_to_user",
- "answer to user",
- func(ctx context.Context, in *toolCallInput) (string, error) {
- return fmt.Sprintf("answer: %v", in.Answer), nil
- })
- if err != nil {
- log.Fatalf("utils.InferTool failed: %v", err)
- }
-
- info, err := answerTool.Info(ctx)
- if err != nil {
- log.Fatalf("get tool info failed: %v", err)
- }
-
- // this file is from gemini cache usage example
- fileData, err := os.ReadFile("./a11.test.txt")
- if err != nil {
- log.Fatalf("os.ReadFile failed: %v", err)
- }
-
- txtFileBase64 := base64.StdEncoding.EncodeToString(fileData)
- cacheInfo, err := cm.CreatePrefixCache(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: `You are an expert at analyzing transcripts.
+ ctx := context.Background()
+
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: os.Getenv("GEMINI_API_KEY"),
+ })
+ if err != nil {
+ log.Fatalf("genai.NewClient failed: %v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Model: os.Getenv("GEMINI_MODEL"),
+ Client: client,
+ })
+ if err != nil {
+ log.Fatalf("gemini.NewChatModel failed: %v", err)
+ }
+
+ type toolCallInput struct {
+ Answer int `json:"answer" jsonschema_description:"the answer of the question"`
+ }
+ answerTool, err := utils.InferTool("answer_to_user",
+ "answer to user",
+ func(ctx context.Context, in *toolCallInput) (string, error) {
+ return fmt.Sprintf("answer: %v", in.Answer), nil
+ })
+ if err != nil {
+ log.Fatalf("utils.InferTool failed: %v", err)
+ }
+
+ info, err := answerTool.Info(ctx)
+ if err != nil {
+ log.Fatalf("get tool info failed: %v", err)
+ }
+
+ // this file is from gemini cache usage example
+ fileData, err := os.ReadFile("./a11.test.txt")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed: %v", err)
+ }
+
+ txtFileBase64 := base64.StdEncoding.EncodeToString(fileData)
+ cacheInfo, err := cm.CreatePrefixCache(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: `You are an expert at analyzing transcripts.
answer the question with the tool "answer_to_user"
always include the start_time and end_time of the transcript in the output`,
- },
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeFileURL,
- File: &schema.MessageInputFile{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &txtFileBase64,
- MIMEType: "text/plain",
- },
- },
- },
- },
- },
- }, model.WithTools([]*schema.ToolInfo{info}), model.WithToolChoice(schema.ToolChoiceForced))
- if err != nil {
- log.Fatalf("CreatePrefixCache failed: %v", err)
- }
-
- data, _ := sonic.MarshalIndent(cacheInfo, "", " ")
- log.Printf("prefix cache info:\n%v\n", string(data))
-
- msg, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "give a very short summary about this transcript",
- },
- }, gemini.WithCachedContentName(cacheInfo.Name),
- model.WithTools([]*schema.ToolInfo{info}),
- model.WithToolChoice(schema.ToolChoiceForced))
- if err != nil {
- log.Fatalf("Generate failed: %v", err)
- }
- msgData, _ := sonic.MarshalIndent(msg, "", " ")
- log.Printf("model output:\n%v\n", string(msgData))
+ },
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeFileURL,
+ File: &schema.MessageInputFile{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &txtFileBase64,
+ MIMEType: "text/plain",
+ },
+ },
+ },
+ },
+ },
+ }, model.WithTools([]*schema.ToolInfo{info}), model.WithToolChoice(schema.ToolChoiceForced))
+ if err != nil {
+ log.Fatalf("CreatePrefixCache failed: %v", err)
+ }
+
+ data, _ := sonic.MarshalIndent(cacheInfo, "", " ")
+ log.Printf("prefix cache info:\n%v\n", string(data))
+
+ msg, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "give a very short summary about this transcript",
+ },
+ }, gemini.WithCachedContentName(cacheInfo.Name),
+ model.WithTools([]*schema.ToolInfo{info}),
+ model.WithToolChoice(schema.ToolChoiceForced))
+ if err != nil {
+ log.Fatalf("Generate failed: %v", err)
+ }
+ msgData, _ := sonic.MarshalIndent(msg, "", " ")
+ log.Printf("model output:\n%v\n", string(msgData))
}
```
-### stream
+### **Streaming Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
- stream, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Write a short poem about spring.",
- },
- })
- if err != nil {
- log.Fatalf("Stream error: %v", err)
- }
-
- fmt.Println("Assistant: ")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Stream receive error: %v", err)
- }
-
- fmt.Println("frame: ")
- if len(resp.Content) > 0 {
- fmt.Println("content: ", resp.Content)
- }
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
- }
- fmt.Println()
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+ stream, err := cm.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream error: %v", err)
+ }
+
+ fmt.Println("Assistant: ")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream receive error: %v", err)
+ }
+
+ fmt.Println("frame: ")
+ if len(resp.Content) > 0 {
+ fmt.Println("content: ", resp.Content)
+ }
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
+ }
+ fmt.Println()
}
```
-### intent_tool
+### **Tool Calling**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "book_recommender",
- Desc: "Recommends books based on user preferences and provides purchase links",
- ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
- "genre": {
- Type: "string",
- Desc: "Preferred book genre",
- Enum: []string{"fiction", "sci-fi", "mystery", "biography", "business"},
- },
- "max_pages": {
- Type: "integer",
- Desc: "Maximum page length (0 for no limit)",
- },
- "min_rating": {
- Type: "number",
- Desc: "Minimum user rating (0-5 scale)",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("Bind tools error: %v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- if len(resp.ToolCalls) > 0 {
- fmt.Printf("Function called: \n")
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
- fmt.Println("Name: ", resp.ToolCalls[0].Function.Name)
- fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
- } else {
- log.Printf("Function called without tool calls: %s\n", resp.Content)
- }
-
- resp, err = cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
- },
- resp,
- {
- Role: schema.Tool,
- ToolCallID: resp.ToolCalls[0].ID,
- Content: "{\"book name\":\"Microeconomics for Managers\"}",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- fmt.Printf("Function call final result: %s\n", resp.Content)
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+ err = cm.BindTools([]*schema.ToolInfo{
+ {
+ Name: "book_recommender",
+ Desc: "Recommends books based on user preferences and provides purchase links",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "genre": {
+ Type: "string",
+ Desc: "Preferred book genre",
+ Enum: []string{"fiction", "sci-fi", "mystery", "biography", "business"},
+ },
+ "max_pages": {
+ Type: "integer",
+ Desc: "Maximum page length (0 for no limit)",
+ },
+ "min_rating": {
+ Type: "number",
+ Desc: "Minimum user rating (0-5 scale)",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Fatalf("Bind tools error: %v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ if len(resp.ToolCalls) > 0 {
+ fmt.Printf("Function called: \n")
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
+ fmt.Println("Name: ", resp.ToolCalls[0].Function.Name)
+ fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
+ } else {
+ log.Printf("Function called without tool calls: %s\n", resp.Content)
+ }
+
+ resp, err = cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
+ },
+ resp,
+ {
+ Role: schema.Tool,
+ ToolCallID: resp.ToolCalls[0].ID,
+ Content: "{\"book name\":\"Microeconomics for Managers\"}",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ fmt.Printf("Function call final result: %s\n", resp.Content)
}
```
-### image_generate
+### **Image Generation**
```go
-
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ResponseModalities: []gemini.GeminiResponseModality{
- gemini.GeminiResponseModalityText,
- gemini.GeminiResponseModalityImage,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- /*
- The generated multimodal content is stored in the `AssistantGenMultiContent` field.
- For this example, the resulting message will have a structure similar to this:
-
- resp := &schema.Message{
- Role: schema.Assistant,
- AssistantGenMultiContent: []schema.MessageOutputPart{
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageOutputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &base64String, // The base64 encoded image data
- MIMEType: "image/png",
- },
- },
- },
- },
- }
- */
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "Generate an image of a cat",
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- log.Printf("\ngenerate output: \n")
- respBody, _ := json.MarshalIndent(resp, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ResponseModalities: []gemini.GeminiResponseModality{
+ gemini.GeminiResponseModalityText,
+ gemini.GeminiResponseModalityImage,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ /*
+ The generated multimodal content is stored in the `AssistantGenMultiContent` field.
+ For this example, the resulting message will have a structure similar to this:
+
+ resp := &schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent: []schema.MessageOutputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &base64String, // The base64 encoded image data
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ log.Printf("\ngenerate output: \n")
+ respBody, _ := json.MarshalIndent(resp, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
```
-### react
+### **React Agent Mode Example**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
- "google.golang.org/genai"
-
- "github.com/cloudwego/eino-ext/components/model/gemini"
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
- ctx := context.Background()
-
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: os.Getenv("GEMINI_API_KEY"),
- })
- if err != nil {
- log.Fatalf("genai.NewClient failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Model: os.Getenv("GEMINI_MODEL"),
- Client: client,
- })
- if err != nil {
- log.Fatalf("gemini.NewChatModel failed, err=%v", err)
- }
-
- type toolCallInput struct {
- LastCount int `json:"last_count" jsonschema_description:"the last count"`
- }
- countsTool, err := utils.InferTool("count_tool_call",
- "count the number of tool calls",
- func(ctx context.Context, in *toolCallInput) (string, error) {
- counts := in.LastCount + 1
- return fmt.Sprintf("tool call counts: %v", counts), nil
- })
- if err != nil {
- log.Fatalf("utils.InferTool failed, err=%v", err)
- }
-
- agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- Name: "react_agent",
- Description: "react_agent",
- Instruction: `call count_tool_call 5 times, then say 'done'`,
- Model: cm,
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- countsTool,
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("adk.NewChatModelAgent failed, err=%v", err)
- }
-
- iter := agent.Run(ctx, &adk.AgentInput{
- Messages: []adk.Message{
- {
- Role: schema.User,
- Content: "start to count",
- },
- },
- })
- idx := 0
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatalf("agent.Run failed, err=%v", event.Err)
- }
-
- msg, err_ := event.Output.MessageOutput.GetMessage()
- if err_ != nil {
- log.Fatalf("GetMessage failed, err=%v", err_)
- }
-
- idx++
- msgData, _ := sonic.MarshalIndent(msg, "", " ")
- log.Printf("\nmessage %v:\n%v\n", idx, string(msgData))
- }
+ ctx := context.Background()
+
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: os.Getenv("GEMINI_API_KEY"),
+ })
+ if err != nil {
+ log.Fatalf("genai.NewClient failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Model: os.Getenv("GEMINI_MODEL"),
+ Client: client,
+ })
+ if err != nil {
+ log.Fatalf("gemini.NewChatModel failed, err=%v", err)
+ }
+
+ type toolCallInput struct {
+ LastCount int `json:"last_count" jsonschema_description:"the last count"`
+ }
+ countsTool, err := utils.InferTool("count_tool_call",
+ "count the number of tool calls",
+ func(ctx context.Context, in *toolCallInput) (string, error) {
+ counts := in.LastCount + 1
+ return fmt.Sprintf("tool call counts: %v", counts), nil
+ })
+ if err != nil {
+ log.Fatalf("utils.InferTool failed, err=%v", err)
+ }
+
+ agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "react_agent",
+ Description: "react_agent",
+ Instruction: `call count_tool_call 5 times, then say 'done'`,
+ Model: cm,
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{
+ countsTool,
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("adk.NewChatModelAgent failed, err=%v", err)
+ }
+
+ iter := agent.Run(ctx, &adk.AgentInput{
+ Messages: []adk.Message{
+ {
+ Role: schema.User,
+ Content: "start to count",
+ },
+ },
+ })
+ idx := 0
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Fatalf("agent.Run failed, err=%v", event.Err)
+ }
+
+ msg, err_ := event.Output.MessageOutput.GetMessage()
+ if err_ != nil {
+ log.Fatalf("GetMessage failed, err=%v", err_)
+ }
+
+ idx++
+ msgData, _ := sonic.MarshalIndent(msg, "", " ")
+ log.Printf("\nmessage %v:\n%v\n", idx, string(msgData))
+ }
}
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://github.com/cloudwego/eino)
-- [Gemini API Documentation](https://ai.google.dev/api/generate-content?hl=zh-cn#v1beta.GenerateContentResponse)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `Eino: ToolsNode & Tool Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- `ChatModel - ARK` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ark`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
index fcc8b5da0bf..abb0b52172a 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - ollama
weight: 0
---
-A Ollama model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A Ollama model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
+- Supports chat completion
+- Supports streaming responses
+- Supports custom response parsing
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/ollama@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the Ollama model:
@@ -33,45 +33,45 @@ Here's a quick example of how to use the Ollama model:
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
-
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output: \n%v\n", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output: \n%v\n", resp)
}
```
-## Configuration
+## **Configuration**
The model can be configured using the `ollama.ChatModelConfig` struct:
@@ -134,316 +134,317 @@ type ThinkValue struct {
```
+## **Examples**
-## examples
-
-### generate
+### **Text Generation**
```go
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output: \n%v\n", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output: \n%v\n", resp)
}
```
-### generate_with_image
+### **Multimodal Support (Image Understanding)**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- multiModalMsg := &schema.Message{
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "this picture is a landscape photo, what's the picture's content",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ multiModalMsg := &schema.Message{
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "this picture is a landscape photo, what's the picture's content",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ multiModalMsg,
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](a T) *T {
- return &a
+ return &a
}
```
-### stream
+### **Streaming Generation**
```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close()
-
- log.Println("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("\nstream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- fmt.Print("\n")
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close()
+
+ log.Println("typewriter output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("\nstream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ fmt.Print("\n")
}
```
-### intent_tool
+### **Tool Calling**
```go
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Printf("BindForcedTools failed, err=%v", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output: \n%+v", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("BindForcedTools failed, err=%v", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output: \n%+v", resp)
}
```
-### thinking
+### **Enable Thinking Mode**
```go
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
- ollamaapi "github.com/eino-contrib/ollama/api"
+ "github.com/cloudwego/eino/schema"
+ ollamaapi "github.com/eino-contrib/ollama/api"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- thinking := ollamaapi.ThinkValue{Value: true}
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- Thinking: &thinking,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output thinking: \n%v\n", resp.ReasoningContent)
- log.Printf("output content: \n%v\n", resp.Content)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ thinking := ollamaapi.ThinkValue{Value: true}
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ Thinking: &thinking,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output thinking: \n%v\n", resp.ReasoningContent)
+ log.Printf("output content: \n%v\n", resp.Content)
}
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/ollama/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Ollama Documentation](https://ollama.readthedocs.io/api/#generate-a-chat-completion)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `ChatModel - OpenAI` at `/docs/eino/ecosystem_integration/chat_model/chat_model_openai`
+- `Eino: ToolsNode Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- [Ollama Model Library](https://ollama.ai/library)
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
index 648d849a0bb..482233b3591 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
@@ -1,15 +1,15 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - openai
weight: 0
---
-A OpenAI model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+An OpenAI model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
@@ -19,72 +19,60 @@ A OpenAI model implementation for [Eino](https://github.com/cloudwego/eino) that
- Custom response parsing support
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/openai@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the OpenAI model:
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // if you want to use Azure OpenAI Service, set these two field.
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
-
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // if you want to use Azure OpenAI Service, set these two field.
+ // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
+ // ByAzure: true,
+ // APIVersion: "2024-06-01",
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" { return true }
+ return false
+ }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ })
+ if err != nil { log.Fatalf("NewChatModel failed, err=%v", err) }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{{
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ }})
+ if err != nil { log.Fatalf("Generate failed, err=%v", err) }
+ fmt.Printf("output: \n%v", resp)
}
-
-
```
-## Configuration
+## **Configuration**
-The model can be configured using the `openai.ChatModelConfig` struct:
+You can configure the model using the `openai.ChatModelConfig` struct:
```go
@@ -200,469 +188,277 @@ Audio *Audio `json:"audio,omitempty"`
}
```
+## **Examples**
-## examples
-
-### generate
+### **Text Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // if you want to use Azure OpenAI Service, set these two field.
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
-
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // if you want to use Azure OpenAI Service, set these fields
+ // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
+ // ByAzure: true,
+ // APIVersion: "2024-06-01",
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+ fmt.Printf("output: \n%v", resp)
}
-
```
-### generate_with_image
+### **Multimodal Support (Image Understanding)**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
-
- }
-
- multiModalMsg := &schema.Message{
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "this picture is a landscape photo, what's the picture's content",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
-}
-
-func of[T any](a T) *T {
- return &a
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ })
+ if err != nil { log.Fatalf("NewChatModel failed, err=%v", err) }
+
+ multi := &schema.Message{ UserInputMultiContent: []schema.MessageInputPart{
+ {Type: schema.ChatMessagePartTypeText, Text: "this picture is a landscape photo, what's the picture's content"},
+ {Type: schema.ChatMessagePartTypeImageURL, Image: &schema.MessageInputImage{ MessagePartCommon: schema.MessagePartCommon{ URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s") }, Detail: schema.ImageURLDetailAuto }},
+ } }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{ multi })
+ if err != nil { log.Fatalf("Generate failed, err=%v", err) }
+ fmt.Printf("output: \n%v", resp)
}
+func of[T any](a T) *T { return &a }
```
-### stream
+### **Streaming Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino/schema"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel of openai failed, err=%v", err)
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
-
- if err != nil {
- log.Fatalf("Stream of openai failed, err=%v", err)
- }
-
- defer streamMsgs.Close()
-
- fmt.Printf("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamMsgs failed, err=%v", err)
- }
- fmt.Print(msg.Content)
- }
-
- fmt.Print("\n")
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ })
+ if err != nil { log.Fatalf("NewChatModel of openai failed, err=%v", err) }
+
+ sr, err := chatModel.Stream(ctx, []*schema.Message{{ Role: schema.User, Content: "as a machine, how do you answer user's question?" }})
+ if err != nil { log.Fatalf("Stream of openai failed, err=%v", err) }
+ defer sr.Close()
+
+ fmt.Printf("typewriter output:")
+ for {
+ msg, err := sr.Recv()
+ if err == io.EOF { break }
+ if err != nil { log.Fatalf("Recv failed, err=%v", err) }
+ fmt.Print(msg.Content)
+ }
+ fmt.Print("\n")
}
-
```
-### intent_tool
+### **Audio Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel of openai failed, err=%v", err)
- }
- err = chatModel.BindForcedTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Retrieve the user's company and position based on their name and email.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"}}),
- }, {
- Name: "user_salary",
- Desc: "Retrieve the user's salary based on their name and email.\n",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"},
- }),
- }})
- if err != nil {
- log.Fatalf("BindForcedTools of openai failed, err=%v", err)
- }
- resp, err := chatModel.Generate(ctx, []*schema.Message{{
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- }})
- if err != nil {
- log.Fatalf("Generate of openai failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
-
- streamResp, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- },
- })
- if err != nil {
- log.Fatalf("Stream of openai failed, err=%v", err)
- }
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
- resp, err = schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of openai failed, err=%v", err)
- }
- fmt.Printf("stream output: \n%v", resp)
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ Modalities: []openai.Modality{openai.AudioModality, openai.TextModality},
+ Audio: &openai.Audio{ Format: openai.AudioFormatMp3, Voice: openai.AudioVoiceAlloy },
+ })
+ if err != nil { log.Fatalf("NewChatModel failed, err=%v", err) }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{{
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {Type: schema.ChatMessagePartTypeText, Text: "help me convert the following text to speech"},
+ {Type: schema.ChatMessagePartTypeText, Text: "Hello, what can I help you with?"},
+ },
+ }})
+ if err != nil { log.Fatalf("Generate failed, err=%v", err) }
+
+ body, _ := sonic.MarshalIndent(resp, " ", " ")
+ log.Printf(" body: %s\n", string(body))
}
-
```
-### audio_generate
+### **Tool Calling**
```go
-
package main
import (
- "context"
-
- "log"
- "os"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // if you want to use Azure OpenAI Service, set these two field.
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- Modalities: []openai.Modality{openai.AudioModality, openai.TextModality},
- Audio: &openai.Audio{
- Format: openai.AudioFormatMp3,
- Voice: openai.AudioVoiceAlloy,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {Type: schema.ChatMessagePartTypeText, Text: "help me convert the following text to speech"},
- {Type: schema.ChatMessagePartTypeText, Text: "Hello, what can I help you with?"},
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- respBody, _ := sonic.MarshalIndent(resp, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ })
+ if err != nil { log.Fatalf("NewChatModel of openai failed, err=%v", err) }
+
+ err = chatModel.BindForcedTools([]*schema.ToolInfo{
+ { Name: "user_company", Desc: "Retrieve the user's company and position based on their name and email.", ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ "name": {Type: "string", Desc: "user's name"}, "email": {Type: "string", Desc: "user's email"} }) },
+ { Name: "user_salary", Desc: "Retrieve the user's salary based on their name and email.", ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ "name": {Type: "string", Desc: "user's name"}, "email": {Type: "string", Desc: "user's email"} }) },
+ })
+ if err != nil { log.Fatalf("BindForcedTools of openai failed, err=%v", err) }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{{ Role: schema.System, Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required." }, { Role: schema.User, Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me." }})
+ if err != nil { log.Fatalf("Generate of openai failed, err=%v", err) }
+ fmt.Printf("output: \n%v", resp)
+
+ sr, err := chatModel.Stream(ctx, []*schema.Message{{ Role: schema.System, Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required." }, { Role: schema.User, Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me." }})
+ if err != nil { log.Fatalf("Stream of openai failed, err=%v", err) }
+ var messages []*schema.Message
+ for { chunk, err := sr.Recv(); if err == io.EOF { break } ; if err != nil { log.Fatalf("Recv failed, err=%v", err) } ; messages = append(messages, chunk) }
+ resp, err = schema.ConcatMessages(messages)
+ if err != nil { log.Fatalf("ConcatMessages of openai failed, err=%v", err) }
+ fmt.Printf("stream output: \n%v", resp)
}
-
```
-### structured
+### **Structured Output**
```go
-
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "os"
-
- "github.com/eino-contrib/jsonschema"
- orderedmap "github.com/wk8/go-ordered-map/v2"
-
- "github.com/cloudwego/eino/schema"
-
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
+ "github.com/eino-contrib/jsonschema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
)
func main() {
- type Person struct {
- Name string `json:"name"`
- Height int `json:"height"`
- Weight int `json:"weight"`
- }
-
- js := &jsonschema.Schema{
- Type: string(schema.Object),
- Properties: orderedmap.New[string, *jsonschema.Schema](
- orderedmap.WithInitialData[string, *jsonschema.Schema](
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "name",
- Value: &jsonschema.Schema{
- Type: string(schema.String),
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "height",
- Value: &jsonschema.Schema{
- Type: string(schema.Integer),
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "weight",
- Value: &jsonschema.Schema{
- Type: string(schema.Integer),
- },
- },
- ),
- ),
- }
-
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ResponseFormat: &openai.ChatCompletionResponseFormat{
- Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
- JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
- Name: "person",
- Description: "data that describes a person",
- Strict: false,
- JSONSchema: js,
- },
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "Parse the user input into the specified json struct",
- },
- {
- Role: schema.User,
- Content: "John is one meter seventy tall and weighs sixty kilograms",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of openai failed, err=%v", err)
- }
-
- result := &Person{}
- err = json.Unmarshal([]byte(resp.Content), result)
- if err != nil {
- log.Fatalf("Unmarshal of openai failed, err=%v", err)
- }
- fmt.Printf("%+v", *result)
+ type Person struct { Name string `json:"name"`; Height int `json:"height"`; Weight int `json:"weight"` }
+
+ js := &jsonschema.Schema{ Type: string(schema.Object), Properties: orderedmap.New[string, *jsonschema.Schema]( orderedmap.WithInitialData[string, *jsonschema.Schema]( orderedmap.Pair[string, *jsonschema.Schema]{ Key: "name", Value: &jsonschema.Schema{ Type: string(schema.String) } }, orderedmap.Pair[string, *jsonschema.Schema]{ Key: "height", Value: &jsonschema.Schema{ Type: string(schema.Integer) } }, orderedmap.Pair[string, *jsonschema.Schema]{ Key: "weight", Value: &jsonschema.Schema{ Type: string(schema.Integer) } }, ), ), }
+
+ ctx := context.Background()
+ cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ ResponseFormat: &openai.ChatCompletionResponseFormat{
+ Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
+ JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{ Name: "person", Description: "data that describes a person", Strict: false, JSONSchema: js },
+ },
+ })
+ if err != nil { log.Fatalf("NewChatModel failed, err=%v", err) }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{{ Role: schema.System, Content: "Parse the user input into the specified json struct" }, { Role: schema.User, Content: "John is one meter seventy tall and weighs sixty kilograms" }})
+ if err != nil { log.Fatalf("Generate failed, err=%v", err) }
+
+ result := &Person{}
+ if err := json.Unmarshal([]byte(resp.Content), result); err != nil { log.Fatalf("Unmarshal failed, err=%v", err) }
+ fmt.Printf("%+v", *result)
}
-
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/openai/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [OpenAI Documentation](https://platform.openai.com/docs/api-reference/chat/create)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `Eino: ToolsNode Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- `ChatModel - ARK` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ark`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
index 5c742d940a6..5a8e9390e01 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - qianfan
weight: 0
---
-A Qianfan model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+A Qianfan model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities to enhance natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
+- Supports chat completion
+- Supports streaming responses
- Custom response parsing support
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/qianfan@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the Qianfan model:
@@ -35,52 +35,52 @@ Here's a quick example of how to use the Qianfan model:
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- ir, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(ir)
- // assistant: 你好!我是文心一言,很高兴与你交流。请问你有什么想问我的吗?无论是关于知识、创作还是其他任何问题,我都会尽力回答你。
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ ir, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(ir)
+ // assistant: 你好!我是文心一言,很高兴与你交流。请问你有什么想问我的吗?无论是关于知识、创作还是其他任何问题,我都会尽力回答你。
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
-## Configuration
+## **Configuration**
The model can be configured using the `qianfan.ChatModelConfig` struct:
@@ -89,358 +89,353 @@ The model can be configured using the `qianfan.ChatModelConfig` struct:
// ChatModelConfig config for qianfan chat completion
// see: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Wm3fhy2vb
type ChatModelConfig struct {
- // Model is the model to use for the chat completion.
- Model string
+ // Model is the model to use for the chat completion.
+ Model string
- // LLMRetryCount is the number of times to retry a failed request.
- LLMRetryCount *int
+ // LLMRetryCount is the number of times to retry a failed request.
+ LLMRetryCount *int
- // LLMRetryTimeout is the timeout for each retry attempt.
- LLMRetryTimeout *float32
+ // LLMRetryTimeout is the timeout for each retry attempt.
+ LLMRetryTimeout *float32
- // LLMRetryBackoffFactor is the backoff factor for retries.
- LLMRetryBackoffFactor *float32
+ // LLMRetryBackoffFactor is the backoff factor for retries.
+ LLMRetryBackoffFactor *float32
- // Temperature controls the randomness of the output. A higher value makes the output more random, while a lower value makes it more focused and deterministic. Default is 0.95, range (0, 1.0].
- Temperature *float32
+ // Temperature controls the randomness of the output. A higher value makes the output more random, while a lower value makes it more focused and deterministic. Default is 0.95, range (0, 1.0].
+ Temperature *float32
- // TopP controls the diversity of the output. A higher value increases the diversity of the generated text. Default is 0.7, range [0, 1.0].
- TopP *float32
+ // TopP controls the diversity of the output. A higher value increases the diversity of the generated text. Default is 0.7, range [0, 1.0].
+ TopP *float32
- // PenaltyScore reduces the generation of repetitive tokens by adding a penalty. A higher value means a larger penalty. Range: [1.0, 2.0].
- PenaltyScore *float64
+ // PenaltyScore reduces the generation of repetitive tokens by adding a penalty. A higher value means a larger penalty. Range: [1.0, 2.0].
+ PenaltyScore *float64
- // MaxCompletionTokens is the maximum number of tokens to generate in the completion. Range [2, 2048].
- MaxCompletionTokens *int
+ // MaxCompletionTokens is the maximum number of tokens to generate in the completion. Range [2, 2048].
+ MaxCompletionTokens *int
- // Seed is the random seed for generation. Range (0, 2147483647).
- Seed *int
+ // Seed is the random seed for generation. Range (0, 2147483647).
+ Seed *int
- // Stop is a list of strings that will stop the generation when the model generates a token that is a suffix of one of the strings.
- Stop []string
+ // Stop is a list of strings that will stop the generation when the model generates a token that is a suffix of one of the strings.
+ Stop []string
- // User is a unique identifier representing the end-user.
- User *string
+ // User is a unique identifier representing the end-user.
+ User *string
- // FrequencyPenalty specifies the frequency penalty to control the repetition of generated text. Range [-2.0, 2.0].
- FrequencyPenalty *float64
+ // FrequencyPenalty specifies the frequency penalty to control the repetition of generated text. Range [-2.0, 2.0].
+ FrequencyPenalty *float64
- // PresencePenalty specifies the presence penalty to control the repetition of generated text. Range [-2.0, 2.0].
- PresencePenalty *float64
+ // PresencePenalty specifies the presence penalty to control the repetition of generated text. Range [-2.0, 2.0].
+ PresencePenalty *float64
- // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true.
- ParallelToolCalls *bool
+ // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true.
+ ParallelToolCalls *bool
- // ResponseFormat specifies the format of the response.
- ResponseFormat *ResponseFormat
+ // ResponseFormat specifies the format of the response.
+ ResponseFormat *ResponseFormat
}
```
+## **Examples**
-
-
-
-
-
-
-## examples
-
-### generate
+### **Text Generation**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- ir, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(ir)
+ ctx := context.Background()
+
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ ir, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(ir)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
-### generate_with_image
+### **Multimodal Understanding (Image Understanding)**
```go
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(image)),
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: of(base64.StdEncoding.EncodeToString(image)),
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
-### stream
+### **Streaming Generation**
```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- sr, err := cm.Stream(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Stream of qianfan failed, err=%v", err)
- }
-
- var ms []*schema.Message
- for {
- m, err := sr.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
-
- log.Fatalf("Stream of qianfan failed, err=%v", err)
- }
-
- fmt.Println(m)
- ms = append(ms, m)
- }
- sm, err := schema.ConcatMessages(ms)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- fmt.Println(sm)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ sr, err := cm.Stream(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Stream of qianfan failed, err=%v", err)
+ }
+
+ var ms []*schema.Message
+ for {
+ m, err := sr.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+
+ log.Fatalf("Stream of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(m)
+ ms = append(ms, m)
+ }
+ sm, err := schema.ConcatMessages(ms)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ fmt.Println(sm)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
-### tool
+### **Tool Calling**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
-
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("BindTools of qianfan failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(resp)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ err = cm.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Fatalf("BindTools of qianfan failed, err=%v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(resp)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/qianfan/examples)
+## **Related Documentation**
-## For More Details
-
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Qianfan Documentation](https://cloud.baidu.com/doc/qianfan-api/s/rm7u7qdiq)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `Eino: ToolsNode Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- `ChatModel - ARK` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ark`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
index e01cab8ca22..5da8845649c 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
title: ChatModel - qwen
@@ -9,7 +9,7 @@ weight: 0
A Qwen model implementation for [Eino](https://github.com/cloudwego/eino) that implements the `ToolCallingChatModel` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/model.Model`
- Easy integration with Eino's model system
@@ -19,13 +19,13 @@ A Qwen model implementation for [Eino](https://github.com/cloudwego/eino) that i
- Custom response parsing support
- Flexible model configuration
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/model/qwen@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the Qwen model:
@@ -43,490 +43,268 @@ import (
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("as a machine, how do you answer user's question?"),
- })
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("as a machine, how do you answer user's question?"),
+ })
+ if err != nil {
+ log.Fatalf("Generate of qwen failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
-## Configuration
+## **Configuration**
The model can be configured using the `qwen.ChatModelConfig` struct:
```go
type ChatModelConfig struct {
-
-// APIKey is your authentication key
-// Use OpenAI API key or Azure API key depending on the service
-// Required
-APIKey string `json:"api_key"`
-
-// Timeout specifies the maximum duration to wait for API responses
-// If HTTPClient is set, Timeout will not be used.
-// Optional. Default: no timeout
-Timeout time.Duration `json:"timeout"`
-
-// HTTPClient specifies the client to send HTTP requests.
-// If HTTPClient is set, Timeout will not be used.
-// Optional. Default &http.Client{Timeout: Timeout}
-HTTPClient *http.Client `json:"http_client"`
-
-// BaseURL specifies the QWen endpoint URL
-// Required. Example: https://dashscope.aliyuncs.com/compatible-mode/v1
-BaseURL string `json:"base_url"`
-
-// The following fields correspond to OpenAI's chat completion API parameters
-// Ref: https://platform.openai.com/docs/api-reference/chat/create
-
-// Model specifies the ID of the model to use
-// Required
-Model string `json:"model"`
-
-// MaxTokens limits the maximum number of tokens that can be generated in the chat completion
-// Optional. Default: model's maximum
-MaxTokens *int `json:"max_tokens,omitempty"`
-
-// Temperature specifies what sampling temperature to use
-// Generally recommend altering this or TopP but not both.
-// Range: 0.0 to 2.0. Higher values make output more random
-// Optional. Default: 1.0
-Temperature *float32 `json:"temperature,omitempty"`
-
-// TopP controls diversity via nucleus sampling
-// Generally recommend altering this or Temperature but not both.
-// Range: 0.0 to 1.0. Lower values make output more focused
-// Optional. Default: 1.0
-TopP *float32 `json:"top_p,omitempty"`
-
-// Stop sequences where the API will stop generating further tokens
-// Optional. Example: []string{"\n", "User:"}
-Stop []string `json:"stop,omitempty"`
-
-// PresencePenalty prevents repetition by penalizing tokens based on presence
-// Range: -2.0 to 2.0. Positive values increase likelihood of new topics
-// Optional. Default: 0
-PresencePenalty *float32 `json:"presence_penalty,omitempty"`
-
-// ResponseFormat specifies the format of the model's response
-// Optional. Use for structured outputs
-ResponseFormat *openai.ChatCompletionResponseFormat `json:"response_format,omitempty"`
-
-// Seed enables deterministic sampling for consistent outputs
-// Optional. Set for reproducible results
-Seed *int `json:"seed,omitempty"`
-
-// FrequencyPenalty prevents repetition by penalizing tokens based on frequency
-// Range: -2.0 to 2.0. Positive values decrease likelihood of repetition
-// Optional. Default: 0
-FrequencyPenalty *float32 `json:"frequency_penalty,omitempty"`
-
-// LogitBias modifies likelihood of specific tokens appearing in completion
-// Optional. Map token IDs to bias values from -100 to 100
-LogitBias map[string]int `json:"logit_bias,omitempty"`
-
-// User unique identifier representing end-user
-// Optional. Helps OpenAI monitor and detect abuse
-User *string `json:"user,omitempty"`
-
-// EnableThinking enables thinking mode
-// https://help.aliyun.com/zh/model-studio/deep-thinking
-// Optional. Default: base on the Model
-EnableThinking *bool `json:"enable_thinking,omitempty"`
+ APIKey string `json:"api_key"`
+ Timeout time.Duration `json:"timeout"`
+ HTTPClient *http.Client `json:"http_client"`
+ BaseURL string `json:"base_url"`
+ Model string `json:"model"`
+ MaxTokens *int `json:"max_tokens,omitempty"`
+ Temperature *float32 `json:"temperature,omitempty"`
+ TopP *float32 `json:"top_p,omitempty"`
+ Stop []string `json:"stop,omitempty"`
+ PresencePenalty *float32 `json:"presence_penalty,omitempty"`
+ ResponseFormat *openai.ChatCompletionResponseFormat `json:"response_format,omitempty"`
+ Seed *int `json:"seed,omitempty"`
+ FrequencyPenalty *float32 `json:"frequency_penalty,omitempty"`
+ LogitBias map[string]int `json:"logit_bias,omitempty"`
+ User *string `json:"user,omitempty"`
+ EnableThinking *bool `json:"enable_thinking,omitempty"`
}
-
```
+## **Examples**
-
-
-
-## examples
-
-### generate
+### **Text Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("as a machine, how do you answer user's question?"),
- })
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("as a machine, how do you answer user's question?"),
+ })
+ if err != nil {
+ log.Fatalf("Generate of qwen failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
-func of[T any](t T) *T {
- return &t
-}
-
+func of[T any](t T) *T { return &t }
```
-### generate_with_image
+### **Multimodal Understanding (Image Understanding)**
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qwen"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(image)),
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
-
-}
-
-func of[T any](t T) *T {
- return &t
+ ctx := context.Background()
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: os.Getenv("DASHSCOPE_API_KEY"),
+ Model: os.Getenv("MODEL_NAME"),
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil { log.Fatalf("NewChatModel of qwen failed, err=%v", err) }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil { log.Fatalf("os.ReadFile failed, err=%v\n", err) }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ { Type: schema.ChatMessagePartTypeText, Text: "What do you see in this image?" },
+ { Type: schema.ChatMessagePartTypeImageURL, Image: &schema.MessageInputImage{ MessagePartCommon: schema.MessagePartCommon{ Base64Data: of(base64.StdEncoding.EncodeToString(image)), MIMEType: "image/jpeg" }, Detail: schema.ImageURLDetailAuto } },
+ },
+ } })
+ if err != nil { log.Printf("Generate error: %v", err); return }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
+func of[T any](t T) *T { return &t }
```
-### stream
+### **Streaming Generation**
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qwen"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- sr, err := cm.Stream(ctx, []*schema.Message{
- schema.UserMessage("你好"),
- })
- if err != nil {
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- var msgs []*schema.Message
- for {
- msg, err := sr.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
-
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- fmt.Println(msg)
- // assistant: 你好
- // finish_reason:
- // : !
- // finish_reason:
- // : 有什么
- // finish_reason:
- // : 可以帮助
- // finish_reason:
- // : 你的吗?
- // finish_reason:
- // :
- // finish_reason: stop
- // usage: &{9 7 16}
- msgs = append(msgs, msg)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- fmt.Println(msg)
- // assistant: 你好!有什么可以帮助你的吗?
- // finish_reason: stop
- // usage: &{9 7 16}
-}
-
-func of[T any](t T) *T {
- return &t
+ ctx := context.Background()
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: os.Getenv("DASHSCOPE_API_KEY"),
+ Model: os.Getenv("MODEL_NAME"),
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil { log.Fatalf("NewChatModel of qwen failed, err=%v", err) }
+
+ sr, err := chatModel.Stream(ctx, []*schema.Message{ schema.UserMessage("hello") })
+ if err != nil { log.Fatalf("Stream of qwen failed, err=%v", err) }
+
+ var ms []*schema.Message
+ for {
+ m, err := sr.Recv()
+ if err != nil { if err == io.EOF { break } ; log.Fatalf("Stream of qwen failed, err=%v", err) }
+ fmt.Println(m)
+ // assistant: hello
+ // finish_reason:
+ // : !
+ // finish_reason:
+ // : What
+ // finish_reason:
+ // : can I help
+ // finish_reason:
+ // : you with?
+ // finish_reason:
+ // :
+ // finish_reason: stop
+ // usage: &{9 7 16}
+ ms = append(ms, m)
+ }
+ sm, err := schema.ConcatMessages(ms)
+ if err != nil { log.Fatalf("ConcatMessages failed, err=%v", err) }
+ fmt.Println(sm)
+ // assistant: hello! What can I help you with?
+ // finish_reason: stop
+ // usage: &{9 7 16}
}
+func of[T any](t T) *T { return &t }
```
-### tool
+### **Tool Calling**
```go
-
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qwen"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "根据用户的姓名和邮箱,查询用户的公司和职位信息",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "用户的姓名",
- },
- "email": {
- Type: "string",
- Desc: "用户的邮箱",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "根据用户的姓名和邮箱,查询用户的薪酬信息",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "用户的姓名",
- },
- "email": {
- Type: "string",
- Desc: "用户的邮箱",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("BindTools of qwen failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
- },
- {
- Role: schema.User,
- Content: "我的姓名是 zhangsan,我的邮箱是 zhangsan@bytedance.com,请帮我推荐一些适合我的房子。",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Println(resp)
- // assistant:
- // tool_calls: [{0x14000275930 call_1e25169e05fc4596a55afb function {user_company {"email": "zhangsan@bytedance.com", "name": "zhangsan"}} map[]}]
- // finish_reason: tool_calls
- // usage: &{316 32 348}
-
- // ==========================
- // using stream
- fmt.Printf("\n\n======== Stream ========\n")
- sr, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
- },
- {
- Role: schema.User,
- Content: "我的姓名是 lisi,我的邮箱是 lisi@bytedance.com,请帮我推荐一些适合我的房子。",
- },
- })
- if err != nil {
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- msgs := make([]*schema.Message, 0)
- for {
- msg, err := sr.Recv()
- if err != nil {
- break
- }
- jsonMsg, err := json.Marshal(msg)
- if err != nil {
- log.Fatalf("json.Marshal failed, err=%v", err)
- }
- fmt.Printf("%s\n", jsonMsg)
- msgs = append(msgs, msg)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- jsonMsg, err := json.Marshal(msg)
- if err != nil {
- log.Fatalf("json.Marshal failed, err=%v", err)
- }
- fmt.Printf("final: %s\n", jsonMsg)
-}
-
-func of[T any](t T) *T {
- return &t
+ ctx := context.Background()
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: os.Getenv("DASHSCOPE_API_KEY"),
+ Model: os.Getenv("MODEL_NAME"),
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil { log.Fatalf("NewChatModel of qwen failed, err=%v", err) }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ { Name: "user_company", Desc: "Query the user's company and position information based on their name and email", ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ "name": {Type: "string", Desc: "The user's name"}, "email": {Type: "string", Desc: "The user's email"} }) },
+ { Name: "user_salary", Desc: "Query the user's salary information based on their name and email", ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ "name": {Type: "string", Desc: "The user's name"}, "email": {Type: "string", Desc: "The user's email"} }) },
+ })
+ if err != nil { log.Fatalf("BindTools of qwen failed, err=%v", err) }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{{ Role: schema.System, Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required" }, { Role: schema.User, Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me." }})
+ if err != nil { log.Fatalf("Generate of qwen failed, err=%v", err) }
+ fmt.Printf("output: \n%v", resp)
}
+func of[T any](t T) *T { return &t }
```
+### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/qwen/examples)
+## **Related Documentation**
-## For More Details
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Qwen Documentation](https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api)
+- `Eino: ChatModel Guide` at `/docs/eino/core_modules/components/chat_model_guide`
+- `Eino: ToolsNode Guide` at `/docs/eino/core_modules/components/tools_node_guide`
+- `ChatModel - ARK` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ark`
+- `ChatModel - Ollama` at `/docs/eino/ecosystem_integration/chat_model/chat_model_ollama`
diff --git a/content/en/docs/eino/ecosystem_integration/chat_template/_index.md b/content/en/docs/eino/ecosystem_integration/chat_template/_index.md
index 1e1c2f55ca8..bc7ff0d33bf 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_template/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_template/_index.md
@@ -1,10 +1,9 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-01-20"
lastmod: ""
tags: []
title: ChatTemplate
weight: 0
---
-
diff --git a/content/en/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md b/content/en/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
index 42fdee0f807..09718287f97 100644
--- a/content/en/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
+++ b/content/en/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
@@ -1,27 +1,25 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-11-20"
lastmod: ""
tags: []
title: ChatTemplate - MCP
weight: 0
---
-## Introduction
+## Overview
-Model Context Protocol(MCP) is a standardized open protocol for model access introduced by Anthropic. Eino provides adapters, which can directly access resources on existing MCP Servers.
+Model Context Protocol (MCP) is an open protocol from Anthropic for standardized access to resources by models. Eino provides wrappers so you can directly access resources served by an existing MCP Server.
-This section introduces the adapter of MCPPrompt, which implements the [Eino ChatTemplate interface](/docs/eino/core_modules/components/chat_template_guide).
+This section introduces the MCPPrompt wrapper, which implements Eino’s ChatTemplate interface ([Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide)).
-
+
-Other adapters:
+Also see: [Tool - MCP](/docs/eino/ecosystem_integration/tool/tool_mcp)
-[Tool - MCP](/docs/eino/ecosystem_integration/tool/tool_mcp)
+## Usage
-## HowToUse
-
-Eino MCP adapters referenced the open-source SDK [mcp-go](https://github.com/mark3labs/mcp-go), first initialize a MCP client:
+First create an MCP client. Eino leverages the open-source SDK [mark3labs/mcp-go](https://github.com/mark3labs/mcp-go):
```go
import "github.com/mark3labs/mcp-go/client"
@@ -31,12 +29,12 @@ cli, err := client.NewStdioMCPClient(myCommand, myEnvs, myArgs...)
// sse client
cli, err := client.NewSSEMCPClient(myBaseURL)
-// sse client needs to manually start asynchronous communication
+// sse client needs to manually start asynchronous communication
// while stdio does not require it.
err = cli.Start(ctx)
```
-Considering the reusability of multiple adapters for the MCP client, the adapter assumes that the client has completed initialization with the Server's [Initialize](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/), so users need to complete the client initialization themselves, for example:
+Considering client reuse, the wrapper assumes the client has finished [Initialize](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/) with the Server; you need to perform client initialization yourself:
```go
import "github.com/mark3labs/mcp-go/mcp"
@@ -50,7 +48,7 @@ initRequest.Params.ClientInfo = mcp.Implementation{
_, err = cli.Initialize(ctx, initRequest)
```
-Then use the Client to create the adapter , which implements Eino ChatTemplate:
+Then create the Eino ChatTemplate using the Client:
```go
import "github.com/cloudwego/eino-ext/components/prompt/mcp"
@@ -61,13 +59,13 @@ tpl, err := mcp.NewPromptTemplate(ctx, &mcp.Config{
})
```
-You can call this adapter directly:
+The template can be invoked directly:
```
result, err := tpl.Format(ctx, map[string]interface{}{/* input k-v */})
```
-It can also be used in any Eino Agent, taking the simplest llm chain as an example:
+Or used within any Eino Agent; for a simple LLM chain:
```
import (
@@ -89,4 +87,4 @@ runner, err := compose.NewChain[map[string]any, *schema.Message]().
## More Information
-Examples can be referred to: [https://github.com/cloudwego/eino-ext/blob/main/components/prompt/mcp/examples/mcp.go](https://github.com/cloudwego/eino-ext/blob/main/components/prompt/mcp/examples/mcp.go)
+Example: https://github.com/cloudwego/eino-ext/blob/main/components/prompt/mcp/examples/mcp.go
diff --git a/content/en/docs/eino/ecosystem_integration/document/_index.md b/content/en/docs/eino/ecosystem_integration/document/_index.md
index 424ea0233cb..0345debe7e3 100644
--- a/content/en/docs/eino/ecosystem_integration/document/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/document/_index.md
@@ -1,10 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-06"
lastmod: ""
tags: []
title: Document
weight: 0
---
-
diff --git a/content/en/docs/eino/ecosystem_integration/document/loader_amazon_s3.md b/content/en/docs/eino/ecosystem_integration/document/loader_amazon_s3.md
index e34d85bfac7..c27c9df8292 100644
--- a/content/en/docs/eino/ecosystem_integration/document/loader_amazon_s3.md
+++ b/content/en/docs/eino/ecosystem_integration/document/loader_amazon_s3.md
@@ -1,25 +1,23 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Loader - amazon s3
weight: 0
---
-## **Introduction**
+## **Overview**
-The S3 Document Loader is an implementation of the Document Loader interface, used to load document content from AWS S3 buckets. This component implements the [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide).
+S3 document loader is an implementation of the Document Loader interface that loads content from AWS S3 buckets. It follows [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide).
-### **Introduction to AWS S3 Service**
+### **AWS S3 Service Overview**
-Amazon Simple Storage Service (Amazon S3) is an object storage service offering industry-leading scalability, data availability, security, and performance. This component interacts with the S3 service using the AWS SDK for Go v2 and supports authentication through access keys or default credentials.
+Amazon S3 is an object storage service with high scalability, availability, security, and performance. This component interacts with S3 via AWS SDK for Go v2 and supports authentication via access keys or default credentials.
## **Usage**
-### **Component Initialization**
-
-The S3 Document Loader is initialized via the `NewS3Loader` function with the following main configuration parameters:
+### **Initialization**
```go
import (
@@ -28,54 +26,48 @@ import (
func main() {
loader, err := s3.NewS3Loader(ctx, &s3.LoaderConfig{
- Region: aws.String("us-east-1"), // AWS Region
- AWSAccessKey: aws.String("your-access-key"), // AWS Access Key ID
- AWSSecretKey: aws.String("your-secret-key"), // AWS Secret Access Key
- UseObjectKeyAsID: true, // Whether to use the object key as the document ID
- Parser: &parser.TextParser{}, // Document parser, defaults to TextParser
+ Region: aws.String("us-east-1"), // AWS region
+ AWSAccessKey: aws.String("your-access-key"), // AWS access key ID
+ AWSSecretKey: aws.String("your-secret-key"), // AWS secret key
+ UseObjectKeyAsID: true, // use object key as document ID
+ Parser: &parser.TextParser{}, // document parser (default TextParser)
})
}
```
-Configuration parameter descriptions:
-
-- `Region`: The AWS region where the S3 bucket is located
-- `AWSAccessKey` and `AWSSecretKey`: AWS access credentials; if not provided, the default credential chain will be used
-- `UseObjectKeyAsID`: Whether to use the S3 object's key as the document ID
-- `Parser`: The parser used for parsing document content, defaults to TextParser to directly convert content to a string
+Parameters:
-### **Loading Documents**
+- `Region`: AWS region of the bucket
+- `AWSAccessKey` and `AWSSecretKey`: credentials; use default credential chain if not provided
+- `UseObjectKeyAsID`: use S3 object key as document ID
+- `Parser`: parser to convert content to text; default TextParser
-Documents are loaded through the `Load` method:
+### **Load Documents**
```go
-docs, err := loader.Load(ctx, document.Source{
- URI: "s3://bucket-name/path/to/document.txt",
-})
+docs, err := loader.Load(ctx, document.Source{ URI: "s3://bucket-name/path/to/document.txt" })
```
-URI format description:
+URI format:
- Must start with `s3://`
-- Followed by the bucket name and object key
+- Followed by bucket name and object key
- Example: `s3://my-bucket/folder/document.pdf`
-Precautions:
-
-- Currently, batch loading of documents via prefix is not supported
-- The URI must point to a specific object and cannot end with `/`
-- Ensure sufficient permissions to access the specified bucket and object
+Notes:
-### **Complete Usage Example**
+- Prefix-based bulk loading is not supported
+- URI must point to a concrete object (not ending with `/`)
+- Ensure sufficient permissions to access the bucket and object
-#### **Standalone Usage**
+### **Complete Example**
```go
package main
import (
"context"
-
+
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/cloudwego/eino-ext/components/document/loader/s3"
"github.com/cloudwego/eino/components/document"
@@ -90,26 +82,15 @@ func main() {
AWSSecretKey: aws.String("your-secret-key"),
UseObjectKeyAsID: true,
})
- if err != nil {
- panic(err)
- }
-
- // Loading documents
- docs, err := loader.Load(ctx, document.Source{
- URI: "s3://my-bucket/documents/sample.txt",
- })
- if err != nil {
- panic(err)
- }
-
- // Using document content
- for _, doc := range docs {
- println(doc.Content)
- }
+ if err != nil { panic(err) }
+
+ docs, err := loader.Load(ctx, document.Source{ URI: "s3://my-bucket/documents/sample.txt" })
+ if err != nil { panic(err) }
+ for _, doc := range docs { println(doc.Content) }
}
```
-## **Related Documentation**
+## **References**
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
+- [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
diff --git a/content/en/docs/eino/ecosystem_integration/document/loader_local_file.md b/content/en/docs/eino/ecosystem_integration/document/loader_local_file.md
index efefc19a66f..1ea666185de 100644
--- a/content/en/docs/eino/ecosystem_integration/document/loader_local_file.md
+++ b/content/en/docs/eino/ecosystem_integration/document/loader_local_file.md
@@ -1,108 +1,86 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Loader - local file
weight: 0
---
-## **Introduction**
+## **Overview**
-The local file loader is an implementation of the Document Loader interface used for loading document content from the local file system. This component implements the [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide).
+Local file loader is an implementation of the Document Loader interface that loads content from the local filesystem. It follows [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide).
### **Features**
-The local file loader has the following features:
-
-- Supports loading documents directly through file paths
-- Automatically detects file types and selects the appropriate parser (ExtParser must be set)
-- Retains the metadata information of the files
-- Supports using the file name as the document ID
+- Load documents by file path
+- Auto-detect file type and choose a suitable parser (requires ExtParser)
+- Preserve file metadata
+- Support using filename as document ID
## **Usage**
-### **Component Initialization**
-
-The local file loader is initialized via the `NewFileLoader` function, with the main configuration parameters as follows:
+### **Initialization**
```go
import (
- "github.com/cloudwego/eino-ext/components/document/loader/file"
+ "github.com/cloudwego/eino/components/document/loader/file"
)
func main() {
- loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{
- UseNameAsID: true, // Whether to use the file name as the document ID
- Parser: &parser.TextParser{}, // Optional: specify a custom parser
+ loader, err := file.NewFileLoader(ctx, &FileLoaderConfig{
+ UseNameAsID: true, // whether to use filename as document ID
+ Parser: &parser.TextParser{}, // optional: custom parser
})
}
```
-Configuration parameters explanation:
-
-- `UseNameAsID`: Whether to use the file name as the document ID
-- `Parser`: Document parser; if not specified, the default extension parser (ExtParser, currently only implemented TextParser) is used
+Parameters:
-### **Loading Documents**
+- `UseNameAsID`: whether to use filename as document ID
+- `Parser`: document parser; if not set, default extension parser (ExtParser, currently TextParser) is used
-Documents are loaded via the `Load` method:
+### **Load Documents**
```go
-docs, err := loader.Load(ctx, document.Source{
- URI: "./path/to/document.txt",
-})
+docs, err := loader.Load(ctx, document.Source{ URI: "./path/to/document.txt" })
```
-After loading, the following metadata will be automatically added to the documents:
+After load, the following metadata is added:
-- `_file_name`: File name
-- `_extension`: File extension
-- `_source`: Full file path
+- `_file_name`: filename
+- `_extension`: file extension
+- `_source`: full file path
Notes:
-- The path must point to a file, not a directory
-- The file must be readable
-- If `UseNameAsID` is true, the file name is used as the ID for a single file; for multiple documents, `fileName_index` is used as the ID
+- Path must point to a file, not a directory
+- File must be readable
+- If `UseNameAsID` is true: single file uses filename; multiple docs use `filename_index`
-### **Full Example Usage**
-
-#### **Standalone Usage**
+### **Complete Example**
```go
package main
import (
"context"
-
+
file "github.com/cloudwego/eino-ext/components/document/loader/file"
"github.com/cloudwego/eino/components/document"
)
func main() {
ctx := context.Background()
-
- // Initialize the loader
- loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{
- UseNameAsID: true,
- })
- if err != nil {
- panic(err)
- }
-
- // Load document
- docs, err := loader.Load(ctx, document.Source{
- URI: "./documents/sample.txt",
- })
- if err != nil {
- panic(err)
- }
-
- // Use document content
+
+ loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{ UseNameAsID: true })
+ if err != nil { panic(err) }
+
+ docs, err := loader.Load(ctx, document.Source{ URI: "./documents/sample.txt" })
+ if err != nil { panic(err) }
+
for _, doc := range docs {
println(doc.Content)
- // Access metadata
fileName := doc.MetaData[file.MetaKeyFileName]
extension := doc.MetaData[file.MetaKeyExtension]
source := doc.MetaData[file.MetaKeySource]
@@ -110,7 +88,7 @@ func main() {
}
```
-## **Related Documentation**
+## **References**
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
+- [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
diff --git a/content/en/docs/eino/ecosystem_integration/document/loader_web_url.md b/content/en/docs/eino/ecosystem_integration/document/loader_web_url.md
index db87b4f00c6..f08a860dd86 100644
--- a/content/en/docs/eino/ecosystem_integration/document/loader_web_url.md
+++ b/content/en/docs/eino/ecosystem_integration/document/loader_web_url.md
@@ -1,29 +1,25 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Loader - web url
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The URL Document Loader is an implementation of the Document Loader interface, used to load document content from web URLs. This component implements the [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide).
+URL document loader is an implementation of the Document Loader interface that loads content from web URLs. It follows [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide).
-### **Feature Introduction**
+### **Features**
-The URL Document Loader has the following features:
-
-- Default support for HTML web content parsing
-- Customizable HTTP client configurations (e.g., custom proxies, etc.)
-- Supports custom content parsers (e.g., body, or other specific containers)
+- HTML page parsing by default
+- Customizable HTTP client configuration (e.g., proxy)
+- Custom content parser (e.g., body or specific container)
## **Usage**
-### **Component Initialization**
-
-The URL Document Loader is initialized using the `NewLoader` function with the main configuration parameters as follows:
+### **Initialization**
```go
import (
@@ -39,29 +35,25 @@ func main() {
}
```
-Explanation of configuration parameters:
-
-- `Parser`: Document parser, defaults to the HTML parser, which extracts the main content of the web page
-- `Client`: HTTP client which can be customized with timeout, proxy, and other configurations
-- `RequestBuilder`: Request builder used to customize request methods, headers, etc.
+Parameters:
-### **Loading Documents**
+- `Parser`: document parser; default HTML parser extracts main body content
+- `Client`: HTTP client; customize timeout, proxy, etc.
+- `RequestBuilder`: request builder for method/headers customization
-Documents are loaded through the `Load` method:
+### **Load Documents**
```go
-docs, err := loader.Load(ctx, document.Source{
- URI: "https://example.com/document",
-})
+docs, err := loader.Load(ctx, document.Source{ URI: "https://example.com/document" })
```
-Note:
+Notes:
-- The URI must be a valid HTTP/HTTPS URL
-- The default request method is GET
-- If other HTTP methods or custom headers are needed, configure the RequestBuilder, for example in authentication scenarios
+- URI must be a valid HTTP/HTTPS URL
+- Default method is GET
+- For other methods or custom headers (e.g., auth), configure `RequestBuilder`
-### **Complete Usage Example**
+### **Complete Examples**
#### **Basic Usage**
@@ -70,36 +62,24 @@ package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/loader/url"
"github.com/cloudwego/eino/components/document"
)
func main() {
ctx := context.Background()
-
- // Initialize the loader with default configuration
+
loader, err := url.NewLoader(ctx, nil)
- if (err != nil) {
- panic(err)
- }
-
- // Load documents
- docs, err := loader.Load(ctx, document.Source{
- URI: "https://example.com/article",
- })
- if (err != nil) {
- panic(err)
- }
-
- // Use document content
- for _, doc := range docs {
- println(doc.Content)
- }
+ if err != nil { panic(err) }
+
+ docs, err := loader.Load(ctx, document.Source{ URI: "https://example.com/article" })
+ if err != nil { panic(err) }
+ for _, doc := range docs { println(doc.Content) }
}
```
-#### **Custom Configuration Example**
+#### **Custom Configuration**
```go
package main
@@ -108,55 +88,33 @@ import (
"context"
"net/http"
"time"
-
+
"github.com/cloudwego/eino-ext/components/document/loader/url"
"github.com/cloudwego/eino/components/document"
)
func main() {
ctx := context.Background()
-
- // Custom HTTP client
- client := &http.Client{
- Timeout: 10 * time.Second,
- }
-
- // Custom request builder
+
+ client := &http.Client{ Timeout: 10 * time.Second }
+
requestBuilder := func(ctx context.Context, src document.Source, opts ...document.LoaderOption) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, "GET", src.URI, nil)
- if err != nil {
- return nil, err
- }
- // Add custom headers
+ if err != nil { return nil, err }
req.Header.Add("User-Agent", "MyBot/1.0")
return req, nil
}
-
- // Initialize the loader
- loader, err := url.NewLoader(ctx, &url.LoaderConfig{
- Client: client,
- RequestBuilder: requestBuilder,
- })
- if (err != nil) {
- panic(err)
- }
-
- // Load documents
- docs, err := loader.Load(ctx, document.Source{
- URI: "https://example.com/article",
- })
- if (err != nil) {
- panic(err)
- }
-
- // Use document content
- for _, doc := range docs {
- println(doc.Content)
- }
+
+ loader, err := url.NewLoader(ctx, &url.LoaderConfig{ Client: client, RequestBuilder: requestBuilder })
+ if err != nil { panic(err) }
+
+ docs, err := loader.Load(ctx, document.Source{ URI: "https://example.com/article" })
+ if err != nil { panic(err) }
+ for _, doc := range docs { println(doc.Content) }
}
```
-## **Related Documentation**
+## **References**
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
+- [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
diff --git a/content/en/docs/eino/ecosystem_integration/document/parser_html.md b/content/en/docs/eino/ecosystem_integration/document/parser_html.md
index b6a2be0f93f..0b2cabd8a2f 100644
--- a/content/en/docs/eino/ecosystem_integration/document/parser_html.md
+++ b/content/en/docs/eino/ecosystem_integration/document/parser_html.md
@@ -1,32 +1,32 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Parser - html
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The HTML Document Parser is an implementation of the Document Parser interface, used to parse the content of HTML web pages into plain text. This component implements the [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide), mainly used in the following scenarios:
+The HTML document parser is an implementation of the Document Parser interface that parses HTML page content into plain text. It follows [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) and is used for:
-- When plain text content needs to be extracted from web pages
-- When metadata of web pages (title, description, etc.) needs to be retrieved
+- extracting plain text from web pages
+- retrieving page metadata (title, description, etc.)
-### **Feature Introduction**
+### **Features**
-The HTML parser has the following features:
+HTML parser provides:
-- Supports selective extraction of page content with flexible content selector configuration (html selector)
-- Automatically extracts web page metadata (metadata)
-- Secure HTML parsing
+- selective content extraction with flexible selectors (html selector)
+- automatic metadata extraction
+- safe HTML parsing
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The HTML parser is initialized using the `NewParser` function, with the main configuration parameters listed below:
+Initialize via `NewParser` with configuration:
```go
import (
@@ -34,27 +34,27 @@ import (
)
parser, err := html.NewParser(ctx, &html.Config{
- Selector: &selector, // Optional: content selector, defaults to body
+ Selector: &selector, // optional: content selector, defaults to body
})
```
-Configuration parameter description:
+Config:
-- `Selector`: Optional parameter, specifies the content area to extract, using goquery selector syntax
- - For example: `body` indicates extracting the content of the `` tag
- - `#content` indicates extracting the content of the element with id "content"
+- `Selector`: optional, the region to extract using goquery selector syntax
+ - e.g., `body` extracts `` content
+ - `#content` extracts the element with id "content"
-### **Metadata Description**
+### **Metadata Keys**
-The parser will automatically extract the following metadata:
+Parser auto‑extracts:
-- `html.MetaKeyTitle` ("_title"): Webpage title
-- `html.MetaKeyDesc` ("_description"): Webpage description
-- `html.MetaKeyLang` ("_language"): Webpage language
-- `html.MetaKeyCharset` ("_charset"): Character encoding
-- `html.MetaKeySource` ("_source"): Document source URI
+- `html.MetaKeyTitle` ("_title"): page title
+- `html.MetaKeyDesc` ("_description"): page description
+- `html.MetaKeyLang` ("_language"): page language
+- `html.MetaKeyCharset` ("_charset"): charset
+- `html.MetaKeySource` ("_source"): document source URI
-### **Complete Usage Example**
+### **Complete Example**
#### **Basic Usage**
@@ -64,23 +64,23 @@ package main
import (
"context"
"strings"
-
+
"github.com/cloudwego/eino-ext/components/document/parser/html"
"github.com/cloudwego/eino/components/document/parser"
)
func main() {
ctx := context.Background()
-
- // Initialize parser
- p, err := html.NewParser(ctx, nil) // Use default configuration
- if (err != nil) {
+
+ // init parser
+ p, err := html.NewParser(ctx, nil) // default config
+ if err != nil {
panic(err)
}
-
+
// HTML content
- html := `
-
+ htmlContent := `
+
Sample Page
@@ -89,65 +89,62 @@ func main() {
Welcome
-
This is the main content.
+
Main body.
`
-
- // Parse the document
- docs, err := p.Parse(ctx, strings.NewReader(html),
+
+ // parse
+ docs, err := p.Parse(ctx, strings.NewReader(htmlContent),
parser.WithURI("https://example.com"),
parser.WithExtraMeta(map[string]any{
"custom": "value",
}),
)
- if (err != nil) {
+ if err != nil {
panic(err)
}
-
- // Use the parsing results
+
doc := docs[0]
- println("Content:", doc.Content)
- println("Title:", doc.MetaData[html.MetaKeyTitle])
- println("Description:", doc.MetaData[html.MetaKeyDesc])
- println("Language:", doc.MetaData[html.MetaKeyLang])
+ println("content:", doc.Content)
+ println("title:", doc.MetaData[html.MetaKeyTitle])
+ println("desc:", doc.MetaData[html.MetaKeyDesc])
+ println("lang:", doc.MetaData[html.MetaKeyLang])
}
```
-#### **Using Selector**
+#### **Using Selectors**
```go
package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/parser/html"
)
func main() {
ctx := context.Background()
-
- // Specify to only extract the content of the element with id "content"
+
+ // only extract element with id content
selector := "#content"
- p, err := html.NewParser(ctx, &html.Config{
- Selector: &selector,
- })
- if (err != nil) {
+ p, err := html.NewParser(ctx, &html.Config{ Selector: &selector })
+ if err != nil {
panic(err)
}
-
- // ... code to parse the document ...
+
+ // ... parsing code ...
}
```
-#### **Using Loader**
+#### In loader
-Refer to the [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) for examples.
+See examples in [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-## **Related Documents**
+## **References**
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
+- [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
- [Parser - pdf](/docs/eino/ecosystem_integration/document/parser_pdf)
diff --git a/content/en/docs/eino/ecosystem_integration/document/parser_pdf.md b/content/en/docs/eino/ecosystem_integration/document/parser_pdf.md
index 4d003d1f2df..8e7719d2c29 100644
--- a/content/en/docs/eino/ecosystem_integration/document/parser_pdf.md
+++ b/content/en/docs/eino/ecosystem_integration/document/parser_pdf.md
@@ -1,39 +1,39 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Parser - pdf
weight: 0
---
-## **Introduction**
+## **Overview**
-The PDF Document Parser is an implementation of the Document Parser interface used to parse the contents of PDF files into plain text. This component implements the [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide) and is mainly used for the following scenarios:
+The PDF document parser is an implementation of the Document Parser interface that parses PDF file content into plain text. It follows [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) and is used for:
-- When you need to convert PDF documents into a processable plain text format
-- When you need to split the contents of a PDF document by page
+- converting PDF documents into plain text
+- splitting PDF content by pages
### **Features**
-The PDF parser has the following features:
+PDF parser provides:
-- Supports basic PDF text extraction
-- Optionally splits documents by page
-- Automatically handles PDF fonts and encoding
-- Supports multi-page PDF documents
+- basic text extraction
+- optional page‑based splitting
+- automatic handling of fonts and encodings
+- support for multi‑page PDFs
Notes:
-- May not fully support all PDF formats currently
-- Will not retain formatting like spaces and line breaks
-- Complex PDF layouts may affect extraction results
+- may not fully support all PDF formats
+- does not preserve whitespace/newline formatting
+- complex layouts may affect extraction quality
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The PDF parser is initialized using the `NewPDFParser` function, with the main configuration parameters as follows:
+Initialize via `NewPDFParser` with configuration:
```go
import (
@@ -42,29 +42,27 @@ import (
func main() {
parser, err := pdf.NewPDFParser(ctx, &pdf.Config{
- ToPages: true, // Whether to split the document by page
+ ToPages: true, // split by pages
})
}
```
-Configuration parameters description:
+Config:
-- `ToPages`: Whether to split the PDF into multiple documents by page, default is false
+- `ToPages`: whether to split the PDF into multiple documents by pages, default false
-### **Parsing Documents**
-
-Document parsing is done using the `Parse` method:
+### **Parse Documents**
```go
docs, err := parser.Parse(ctx, reader, opts...)
```
-Parsing options:
+Options:
-- Supports setting the document URI using `parser.WithURI`
-- Supports adding extra metadata using `parser.WithExtraMeta`
+- `parser.WithURI` to set document URI
+- `parser.WithExtraMeta` to add extra metadata
-### **Complete Usage Example**
+### **Complete Example**
#### **Basic Usage**
@@ -74,53 +72,47 @@ package main
import (
"context"
"os"
-
+
"github.com/cloudwego/eino-ext/components/document/parser/pdf"
"github.com/cloudwego/eino/components/document/parser"
)
func main() {
ctx := context.Background()
-
- // Initialize the parser
- p, err := pdf.NewPDFParser(ctx, &pdf.Config{
- ToPages: false, // Do not split by page
- })
+
+ // init parser
+ p, err := pdf.NewPDFParser(ctx, &pdf.Config{ ToPages: false })
if err != nil {
panic(err)
}
-
- // Open the PDF file
+
+ // open file
file, err := os.Open("document.pdf")
if err != nil {
panic(err)
}
defer file.Close()
-
- // Parse the document
- docs, err := p.Parse(ctx, file,
+
+ // parse
+ docs, err := p.Parse(ctx, file,
parser.WithURI("document.pdf"),
- parser.WithExtraMeta(map[string]any{
- "source": "./document.pdf",
- }),
+ parser.WithExtraMeta(map[string]any{ "source": "./document.pdf" }),
)
if err != nil {
panic(err)
}
-
- // Use the parsed results
+
for _, doc := range docs {
println(doc.Content)
}
}
```
-#### **Using loader**
+#### In loader
-Refer to the example in the [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
+See examples in [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-## **Related Documents**
+## **References**
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Parser - html](/docs/eino/ecosystem_integration/document/parser_html)
+- [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
+- [Eino: Document Loader Guide](/docs/eino/core_modules/components/document_loader_guide)
diff --git a/content/en/docs/eino/ecosystem_integration/document/splitter_markdown.md b/content/en/docs/eino/ecosystem_integration/document/splitter_markdown.md
index c12c039e82c..b676353c241 100644
--- a/content/en/docs/eino/ecosystem_integration/document/splitter_markdown.md
+++ b/content/en/docs/eino/ecosystem_integration/document/splitter_markdown.md
@@ -1,115 +1,101 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-01-07"
lastmod: ""
tags: []
title: Splitter - markdown
weight: 0
---
-## **Introduction**
+## **Overview**
-The Markdown Splitter is an implementation of the Document Transformer interface, used to split a Markdown document based on the document's header hierarchy. This component implements the [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide).
+Markdown splitter is an implementation of the Document Transformer interface that splits by Markdown heading levels. It follows [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide).
-### **Working Principle**
+### **How It Works**
-The Markdown Header Splitter works through the following steps:
-
-1. Identify Markdown headers in the document (`#`, `##`, `###`, etc.)
-2. Construct a document structure tree based on the header hierarchy
-3. Split the document into independent segments based on the headers
+1. Detect Markdown headings (`#`, `##`, `###`, etc.)
+2. Build a document structure tree by levels
+3. Split the document into fragments by headings
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The Markdown Header Splitter is initialized using the `NewHeaderSplitter` function. The main configuration parameters are as follows:
+Initialize via `NewHeaderSplitter` with configuration:
```go
splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{
Headers: map[string]string{
- "#": "h1", // Level 1 header
- "##": "h2", // Level 2 header
- "###": "h3", // Level 3 header
+ "#": "h1", // H1
+ "##": "h2", // H2
+ "###": "h3", // H3
},
- TrimHeaders: false, // Whether to keep header lines in the output
+ TrimHeaders: false, // keep heading lines in output
})
```
-Explanation of configuration parameters:
+Parameters:
-- `Headers`: Required parameter, defines the mapping between header tags and corresponding metadata key names
-- `TrimHeaders`: Whether to remove header lines from the output content
+- `Headers`: required, map heading tokens to metadata keys
+- `TrimHeaders`: whether to remove heading lines in output
-### **Full Usage Example**
+### **Complete Example**
```go
package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
-
- // Initialize the splitter
+
+ // Initialize splitter
splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{
- Headers: map[string]string{
- "#": "h1",
- "##": "h2",
- "###": "h3",
- },
+ Headers: map[string]string{ "#": "h1", "##": "h2", "###": "h3" },
TrimHeaders: false,
})
- if err != nil {
- panic(err)
- }
-
- // Prepare the document to be split
- docs := []*schema.Document{
- {
- ID: "doc1",
- Content: `# Document Title
+ if err != nil { panic(err) }
+
+ // Prepare documents to split
+ docs := []*schema.Document{{
+ ID: "doc1",
+ Content: `# Document Title
-This is the content of the introduction section.
+Intro section.
-## Chapter 1
+## Chapter One
-This is the content of Chapter 1.
+Chapter one content.
### Section 1.1
-This is the content of Section 1.1.
+Section 1.1 content.
-## Chapter 2
+## Chapter Two
-This is the content of Chapter 2.
+Chapter two content.
\`\`\`
-# This is a comment inside a code block and will not be recognized as a header
+# This is a comment in a code block and will not be detected as a heading
\`\`\`
`,
- },
- }
-
- // Execute the split
+ }}
+
+ // Execute splitting
results, err := splitter.Transform(ctx, docs)
- if err != nil {
- panic(err)
- }
-
- // Process the split results
+ if err != nil { panic(err) }
+
+ // Process split results
for i, doc := range results {
- println("Segment", i+1, ":", doc.Content)
- println("Header Hierarchy:")
+ println("fragment", i+1, ":", doc.Content)
+ println("heading levels:")
for k, v := range doc.MetaData {
- if k == "h1" || k == "h2" || k == "h3" {
- println(" ", k, ":", v)
- }
+ if k == "h1" || k == "h2" || k == "h3" { println(" ", k, ":", v) }
}
}
}
@@ -117,15 +103,13 @@ This is the content of Chapter 2.
## **Features**
-- Supports both ````` and `~~~` style code blocks
-- Automatically maintains the header hierarchy
- - New headers of the same level reset the subheaders
- - Header hierarchy information is passed through metadata
+- Supports fenced code blocks ``` and ~~~
+- Automatically maintains heading hierarchy
+ - New peer headings reset lower-level headings
+ - Heading level info is passed via metadata
-## **Related Documents**
+## **References**
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide)
+- [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide)
- [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive)
- [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
diff --git a/content/en/docs/eino/ecosystem_integration/document/splitter_recursive.md b/content/en/docs/eino/ecosystem_integration/document/splitter_recursive.md
index 899350b016d..352f536d3d1 100644
--- a/content/en/docs/eino/ecosystem_integration/document/splitter_recursive.md
+++ b/content/en/docs/eino/ecosystem_integration/document/splitter_recursive.md
@@ -1,126 +1,110 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Splitter - recursive
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The Recursive Splitter is an implementation of the Document Transformer interface, used to recursively split long documents into smaller segments according to a specified size. This component implements the [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide).
+Recursive splitter is an implementation of the Document Transformer interface that recursively splits long documents into smaller chunks by target size. It follows [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide).
-### **Working Principle**
+### **How It Works**
-The Recursive Splitter works through the following steps:
-
-1. Try to split the document in the order of the separator list
-2. If the current separator cannot split the document into segments smaller than the target size, use the next separator
-3. Merge the resulting segments to ensure the segment size is close to the target size
-4. Maintain an overlapping region of the specified size during the merging process
+1. Try splitting the document using separators in order
+2. If the current separator cannot produce chunks under the target size, use the next separator
+3. Merge split fragments to ensure sizes are close to the target
+4. Maintain a specified overlap area during merging
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The recursive splitter is initialized via the `NewSplitter` function, with the main configuration parameters as follows:
+Initialize via `NewSplitter` with configuration:
```go
splitter, err := recursive.NewSplitter(ctx, &recursive.Config{
- ChunkSize: 1000, // Required: Target chunk size
- OverlapSize: 200, // Optional: Chunk overlap size
- Separators: []string{"\n", ".", "?", "!"}, // Optional: List of separators
- LenFunc: nil, // Optional: Custom length calculation function
- KeepType: recursive.KeepTypeNone, // Optional: Separator retention strategy
+ ChunkSize: 1000, // required: target chunk size
+ OverlapSize: 200, // optional: overlap size
+ Separators: []string{"\n\n", "\n", "。", "!", "?"}, // optional: separator list
+ LenFunc: nil, // optional: custom length func
+ KeepType: recursive.KeepTypeEnd, // optional: keep-type strategy
})
```
-Configuration parameters explanation:
+Parameters:
-- `ChunkSize`: Required parameter, specifies the target chunk size
-- `OverlapSize`: Overlap size between chunks, used to maintain context coherence
-- `Separators`: List of separators, used in order of priority
-- `LenFunc`: Custom text length calculation function, defaults to `len()`
-- `KeepType`: Separator retention strategy, optional values:
- - `KeepTypeNone`: Do not retain separators
- - `KeepTypeStart`: Retain separators at the start of the chunk
- - `KeepTypeEnd`: Retain separators at the end of the chunk
+- `ChunkSize`: required, target chunk size
+- `OverlapSize`: overlap between chunks to keep context
+- `Separators`: ordered list of separators by priority
+- `LenFunc`: custom length function, default `len()`
+- `KeepType`: separator keep strategy, values:
+ - `KeepTypeNone`: do not keep separators
+ - `KeepTypeStart`: keep at the start
+ - `KeepTypeEnd`: keep at the end
-### **Complete Usage Example**
+### **Complete Example**
```go
package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/recursive"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
-
- // Initialize the splitter
+
splitter, err := recursive.NewSplitter(ctx, &recursive.Config{
ChunkSize: 1000,
OverlapSize: 200,
Separators: []string{"\n\n", "\n", "。", "!", "?"},
KeepType: recursive.KeepTypeEnd,
})
- if err != nil {
- panic(err)
- }
-
- // Prepare the documents to be split
- docs := []*schema.Document{
- {
- ID: "doc1",
- Content: `这是第一个段落,包含了一些内容。
-
- 这是第二个段落。这个段落有多个句子!这些句子通过标点符号分隔。
-
- 这是第三个段落。这里有更多的内容。`,
- },
- }
-
- // Perform splitting
+ if err != nil { panic(err) }
+
+ docs := []*schema.Document{{
+ ID: "doc1",
+ Content: `This is the first paragraph, with some content.
+
+This is the second paragraph. This paragraph has multiple sentences! These sentences are separated by punctuation.
+
+This is the third paragraph. Here is more content.`,
+ }}
+
results, err := splitter.Transform(ctx, docs)
- if err != nil {
- panic(err)
- }
-
- // Handle the split results
- for i, doc := range results {
- println("Chunk", i+1, ":", doc.Content)
- }
+ if err != nil { panic(err) }
+
+ for i, doc := range results { println("fragment", i+1, ":", doc.Content) }
}
```
### **Advanced Usage**
-Custom length calculation:
+Custom length function:
```go
splitter, err := recursive.NewSplitter(ctx, &recursive.Config{
ChunkSize: 1000,
LenFunc: func(s string) int {
- // e.g.: Use the number of Unicode characters instead of bytes
+ // use unicode rune count instead of byte length
return len([]rune(s))
},
})
```
-Adjusting overlap strategy:
+Adjust overlap strategy:
```go
splitter, err := recursive.NewSplitter(ctx, &recursive.Config{
ChunkSize: 1000,
- // Increase overlap area to retain more context
- OverlapSize: 300,
- // Retain separator at the end of the chunk
- KeepType: recursive.KeepTypeEnd,
+ OverlapSize: 300, // larger overlap to keep more context
+ KeepType: recursive.KeepTypeEnd, // keep separator at end of fragments
})
```
@@ -129,19 +113,16 @@ Custom separators:
```go
splitter, err := recursive.NewSplitter(ctx, &recursive.Config{
ChunkSize: 1000,
- // List of separators sorted by priority
Separators: []string{
- "\n\n", // Empty line (paragraph separator)
- "\n", // Line break
- "。", // Period
+ "\n\n", // blank line (paragraph)
+ "\n", // newline
+ "。", // period
},
})
```
-## **Related Documents**
+## **References**
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide)
-- [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
+- [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide)
- [Splitter - markdown](/docs/eino/ecosystem_integration/document/splitter_markdown)
+- [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
diff --git a/content/en/docs/eino/ecosystem_integration/document/splitter_semantic.md b/content/en/docs/eino/ecosystem_integration/document/splitter_semantic.md
index 2e74d9a00ca..8ec6e3adeae 100644
--- a/content/en/docs/eino/ecosystem_integration/document/splitter_semantic.md
+++ b/content/en/docs/eino/ecosystem_integration/document/splitter_semantic.md
@@ -1,60 +1,58 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Splitter - semantic
weight: 0
---
-## **Introduction**
+## **Overview**
-The semantic segmenter is an implementation of the Document Transformer interface, used to segment long documents into smaller fragments based on semantic similarity. This component is implemented according to the [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide).
+Semantic splitter is an implementation of the Document Transformer interface that splits long documents based on semantic similarity. It follows [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide).
-### **Working Principle**
+### **How It Works**
-The semantic segmenter operates through the following steps:
-
-1. First, it uses basic delimiters (such as newline characters, periods, etc.) to split the document into initial segments.
-2. It generates semantic vectors for each segment using a vector embedding model.
-3. It calculates the cosine similarity between adjacent segments.
-4. It decides whether to separate two segments based on a similarity threshold.
-5. It merges segments smaller than the minimum size.
+1. First split the document into initial fragments using basic separators (newline, period, etc.)
+2. Generate an embedding vector for each fragment
+3. Compute cosine similarity between adjacent fragments
+4. Decide split points by a similarity threshold percentile
+5. Merge fragments smaller than the minimum size
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The semantic splitter initializes through the `NewSplitter` function with the main configuration parameters as follows:
+Initialize via `NewSplitter` with configuration:
```go
splitter, err := semantic.NewSplitter(ctx, &semantic.Config{
- Embedding: embedder, // Required: Embedding instance used to generate text vectors
- BufferSize: 2, // Optional: Context buffer size
- MinChunkSize: 100, // Optional: Minimum chunk size
- Separators: []string{"\n", ".", "?", "!"}, // Optional: List of separators
- Percentile: 0.9, // Optional: Percentile for splitting threshold
- LenFunc: nil, // Optional: Custom length calculation function
+ Embedding: embedder, // required: embedder to generate vectors
+ BufferSize: 2, // optional: context buffer size
+ MinChunkSize: 100, // optional: minimum chunk size
+ Separators: []string{"\n", ".", "?", "!"}, // optional: separator list
+ Percentile: 0.9, // optional: split threshold percentile
+ LenFunc: nil, // optional: custom length func
})
```
-Explanation of configuration parameters:
+Parameters:
-- `Embedding`: Required parameter, instance of the embedder used to generate text vectors
-- `BufferSize`: Context buffer size to include more context information when calculating semantic similarity
-- `MinChunkSize`: Minimum chunk size, chunks smaller than this size will be merged
-- `Separators`: List of separators used for initial splitting, used sequentially
-- `Percentile`: Percentile of the splitting threshold, range 0-1, the larger it is, the fewer splits there are
-- `LenFunc`: Custom text length calculation function, by default uses `len()`
+- `Embedding`: required embedder instance
+- `BufferSize`: include more context for similarity computation
+- `MinChunkSize`: merge fragments smaller than this size
+- `Separators`: ordered list used for initial split
+- `Percentile`: 0–1; higher means fewer splits
+- `LenFunc`: custom length function, default `len()`
-### **Full Usage Example**
+### **Complete Example**
```go
package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/semantic"
"github.com/cloudwego/eino/components/embedding"
"github.com/cloudwego/eino/schema"
@@ -62,11 +60,9 @@ import (
func main() {
ctx := context.Background()
-
- // Initialize embedder (example usage)
+
embedder := &embedding.SomeEmbeddingImpl{} // eg: openai embedding
-
- // Initialize splitter
+
splitter, err := semantic.NewSplitter(ctx, &semantic.Config{
Embedding: embedder,
BufferSize: 2,
@@ -74,57 +70,40 @@ func main() {
Separators: []string{"\n", ".", "?", "!"},
Percentile: 0.9,
})
- if err != nil {
- panic(err)
- }
-
- // Prepare the document to be split
- docs := []*schema.Document{
- {
- ID: "doc1",
- Content: `This is the first paragraph, containing some important information.
- This is the second paragraph, semantically related to the first.
- This is the third paragraph, the topic has changed.
- This is the fourth paragraph, continuing the new topic.`,
- },
- }
-
- // Execute the split
+ if err != nil { panic(err) }
+
+ docs := []*schema.Document{{
+ ID: "doc1",
+ Content: `This is the first paragraph with important info.
+This is the second paragraph, semantically related to the first.
+This is the third paragraph, the topic has changed.
+This is the fourth paragraph, continuing the new topic.`,
+ }}
+
results, err := splitter.Transform(ctx, docs)
- if err != nil {
- panic(err)
- }
-
- // Process the split results
- for i, doc := range results {
- println("Segment", i+1, ":", doc.Content)
- }
+ if err != nil { panic(err) }
+ for i, doc := range results { println("fragment", i+1, ":", doc.Content) }
}
```
### **Advanced Usage**
-Custom length calculation:
+Custom length function:
```go
splitter, err := semantic.NewSplitter(ctx, &semantic.Config{
Embedding: embedder,
- LenFunc: func(s string) int {
- // Use the number of unicode characters instead of bytes
- return len([]rune(s))
- },
+ LenFunc: func(s string) int { return len([]rune(s)) }, // unicode length
})
```
-Adjust splitting granularity:
+Adjust granularity:
```go
splitter, err := semantic.NewSplitter(ctx, &semantic.Config{
- Embedding: embedder,
- // Increase percentile to reduce split points
- Percentile: 0.95,
- // Increase minimum chunk size to avoid too small chunks
- MinChunkSize: 200,
+ Embedding: embedder,
+ Percentile: 0.95, // fewer split points
+ MinChunkSize: 200, // avoid too-small fragments
})
```
@@ -132,18 +111,14 @@ Optimize semantic judgment:
```go
splitter, err := semantic.NewSplitter(ctx, &semantic.Config{
- Embedding: embedder,
- // Increase buffer size to include more context
- BufferSize: 10,
- // Custom separator priority
- Separators: []string{"\n\n", "\n", ".", "!", "?", ","},
+ Embedding: embedder,
+ BufferSize: 10, // more context
+ Separators: []string{"\n\n", "\n", "。", "!", "?", ","}, // custom priority
})
```
-## **Related Documents**
+## **References**
-- [Eino: Document Parser guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide)
-- [Eino: Document Loader guide](/docs/eino/core_modules/components/document_loader_guide)
-- [Eino: Document Transformer guide](/docs/eino/core_modules/components/document_transformer_guide)
-- [Splitter - semantic](/docs/eino/ecosystem_integration/document/splitter_semantic)
+- [Eino: Document Transformer Guide](/docs/eino/core_modules/components/document_transformer_guide)
+- [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive)
- [Splitter - markdown](/docs/eino/ecosystem_integration/document/splitter_markdown)
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/_index.md b/content/en/docs/eino/ecosystem_integration/embedding/_index.md
index 57dde8dfafd..a655c18ab7c 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/_index.md
@@ -1,10 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-06"
lastmod: ""
tags: []
title: Embedding
weight: 0
---
-
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md
index 5e23a27b09b..1ca938291d6 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md
@@ -1,48 +1,48 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Embedding - ARK
weight: 0
---
-## **Introduction**
+## **Overview**
-Ark Embedding is an implementation of the Eino Embedding interface used to convert text into vector representations. Volcengine Ark is a platform that provides machine learning model inference services, including text vectorization services. This component implements the [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide).
+Ark Embedding is an implementation of Eino’s Embedding interface that converts text to vectors. Volcengine Ark provides model inference services, including text embedding. This component follows [[🚧]Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide).
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The Ark vector embedder is initialized using the `NewEmbedder` function with the main configuration parameters as follows:
+Initialize via `NewEmbedder` with key configuration options:
```go
import "github.com/cloudwego/eino-ext/components/embedding/ark"
embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
- // Authentication configuration (choose one)
- APIKey: "your-api-key", // Use API Key for authentication
- // Or use AK/SK authentication
+ // authentication (choose one)
+ APIKey: "your-api-key", // API Key auth
+ // or AK/SK auth
AccessKey: "your-access-key",
SecretKey: "your-secret-key",
-
- // Service configuration
- Model: "ep-xxxxxxx-xxxxx", // Endpoint ID for the Ark platform
- BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // Optional, default is the Beijing region
- Region: "cn-beijing", // Optional, default is the Beijing region
-
- // Advanced configuration
- Timeout: &timeout, // Request timeout
- RetryTimes: &retryTimes, // Number of retries
- Dimensions: &dimensions, // Output vector dimensions
- User: &user, // User identifier
+
+ // service config
+ Model: "ep-xxxxxxx-xxxxx", // Ark endpoint ID
+ BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // optional, defaults to Beijing
+ Region: "cn-beijing", // optional, defaults to Beijing
+
+ // advanced
+ Timeout: &timeout, // request timeout
+ RetryTimes: &retryTimes, // retry times
+ Dimensions: &dimensions, // output vector dimension
+ User: &user, // user identifier
})
```
-### **Generate Vector Embeddings**
+### **Generate Embeddings**
-Text vectorization is achieved through the `EmbedStrings` method:
+Text vectorization is done via `EmbedStrings`:
```go
embeddings, err := embedder.EmbedStrings(ctx, []string{
@@ -51,7 +51,7 @@ embeddings, err := embedder.EmbedStrings(ctx, []string{
})
```
-### **Complete Usage Example**
+### **Complete Example**
#### **Basic Usage**
@@ -61,14 +61,14 @@ package main
import (
"context"
"time"
-
+
"github.com/cloudwego/eino-ext/components/embedding/ark"
)
func main() {
ctx := context.Background()
-
- // Initialize embedder
+
+ // init embedder
timeout := 30 * time.Second
embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
APIKey: "your-api-key",
@@ -78,27 +78,27 @@ func main() {
if err != nil {
panic(err)
}
-
- // Generate text vectors
+
+ // generate embeddings
texts := []string{
- "This is an example text segment one",
- "This is an example text segment two",
+ "This is the first sample text",
+ "This is the second sample text",
}
-
+
embeddings, err := embedder.EmbedStrings(ctx, texts)
if err != nil {
panic(err)
}
-
- // Use generated vectors
+
+ // use generated vectors
for i, embedding := range embeddings {
- println("Text", i+1, "vector dimension:", len(embedding))
+ println("text", i+1, "vector dim:", len(embedding))
}
}
```
-## **Related Documentation**
+## **References**
-- [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide)
+- [Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide)
- [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai)
-- [Volcengine Ark Services](https://www.volcengine.com/product/ark)
+- Volcengine Ark: https://www.volcengine.com/product/ark
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
index 2d2a00e200e..fd36d2b8996 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Embedding - dashscope
weight: 0
---
-you can use embedding services served by dashscope (from aliyun).
+You can use Alibaba Cloud’s DashScope platform embedding service.
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ollama.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
index 9ba8e20fcea..31cdf87601b 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
@@ -1,85 +1,87 @@
---
Description: ""
-date: "2025-07-30"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Embedding - Ollama
weight: 0
---
-## Introduction
-This is a Ollama Embedding Component implemented for [Eino](https://github.com/cloudwego/eino),which implements `Embedder` interface,seamlessly integrating into Eino's embedding system to provide text vectorization capabilities.
+## **Overview**
-## Features
-- Implements `github.com/cloudwego/eino/components/embedding.Embedder`
-- Easy to integrate into Eino's workflow
-- Support custom Ollama service endpoints and models
-- Eino's Built-in callback support
+This is an Ollama Embedding component for [Eino](https://github.com/cloudwego/eino) that implements the `Embedder` interface. It integrates seamlessly into Eino's embedding system to provide text vectorization.
+
+## **Features**
+
+- Implements `github.com/cloudwego/eino/components/embedding.Embedder` interface
+- Easy integration into Eino workflows
+- Supports custom Ollama service endpoint and model
+- Built-in Eino callback support
+
+## **Installation**
-## Installation
```bash
- go get github.com/cloudwego/eino-ext/components/embedding/ollama
+go get github.com/cloudwego/eino-ext/components/embedding/ollama
```
-
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "log"
- "os"
- "time"
+ "context"
+ "log"
+ "os"
+ "time"
- "github.com/cloudwego/eino-ext/components/embedding/ollama"
+ "github.com/cloudwego/eino-ext/components/embedding/ollama"
)
func main() {
- ctx := context.Background()
-
- baseURL := os.Getenv("OLLAMA_BASE_URL")
- if baseURL == "" {
- baseURL = "http://localhost:11434" // Default localhost
- }
- model := os.Getenv("OLLAMA_EMBED_MODEL")
- if model == "" {
- model = "nomic-embed-text"
- }
-
- embedder, err := ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
- BaseURL: baseURL,
- Model: model,
- Timeout: 10 * time.Second,
- })
- if err != nil {
- log.Fatalf("NewEmbedder of ollama error: %v", err)
- return
- }
-
- log.Printf("===== call Embedder directly =====")
-
- vectors, err := embedder.EmbedStrings(ctx, []string{"hello", "how are you"})
- if err != nil {
- log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
- }
-
- log.Printf("vectors : %v", vectors)
-
- // you can use WithModel to specify the model
- vectors, err = embedder.EmbedStrings(ctx, []string{"hello", "how are you"}, embedding.WithModel(model))
- if err != nil {
- log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
- }
-
- log.Printf("vectors : %v", vectors)
+ ctx := context.Background()
+
+ baseURL := os.Getenv("OLLAMA_BASE_URL")
+ if baseURL == "" {
+ baseURL = "http://localhost:11434" // default localhost
+ }
+ model := os.Getenv("OLLAMA_EMBED_MODEL")
+ if model == "" {
+ model = "nomic-embed-text"
+ }
+
+ embedder, err := ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
+ BaseURL: baseURL,
+ Model: model,
+ Timeout: 10 * time.Second,
+ })
+ if err != nil {
+ log.Fatalf("NewEmbedder of ollama error: %v", err)
+ return
+ }
+
+ log.Printf("===== call Embedder directly =====")
+
+ vectors, err := embedder.EmbedStrings(ctx, []string{"hello", "how are you"})
+ if err != nil {
+ log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
+ }
+
+ log.Printf("vectors : %v", vectors)
+
+ // you can use WithModel to specify the model
+ vectors, err = embedder.EmbedStrings(ctx, []string{"hello", "how are you"}, embedding.WithModel(model))
+ if err != nil {
+ log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
+ }
+
+ log.Printf("vectors : %v", vectors)
}
```
-## Configuration
+## **Configuration**
-The embedder can be configured through the `EmbeddingConfig` struct:
+The embedder can be configured via the `EmbeddingConfig` struct:
```go
type EmbeddingConfig struct {
@@ -87,31 +89,31 @@ type EmbeddingConfig struct {
// If HTTPClient is set, Timeout will not be used.
// Optional. Default: no timeout
Timeout time.Duration `json:"timeout"`
-
+
// HTTPClient specifies the client to send HTTP requests.
// If HTTPClient is set, Timeout will not be used.
// Optional. Default &http.Client{Timeout: Timeout}
HTTPClient *http.Client `json:"http_client"`
-
+
// BaseURL specifies the Ollama service endpoint URL
// Format: http(s)://host:port
// Optional. Default: "http://localhost:11434"
BaseURL string `json:"base_url"`
-
+
// Model specifies the ID of the model to use for embedding generation
// Required. You can also set it when calling the EmbedStrings with `embedding.WithModel(model)`
Model string `json:"model"`
-
+
// Truncate specifies whether to truncate text to model's maximum context length
// When set to true, if text to embed exceeds the model's maximum context length,
// a call to EmbedStrings will return an error
// Optional.
Truncate *bool `json:"truncate,omitempty"`
-
+
// KeepAlive controls how long the model will stay loaded in memory following this request.
// Optional. Default 5 minutes
KeepAlive *time.Duration `json:"keep_alive,omitempty"`
-
+
// Options lists model-specific options.
// Optional
Options map[string]any `json:"options,omitempty"`
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_openai.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_openai.md
index 1e553676682..b132706816c 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_openai.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_openai.md
@@ -1,49 +1,49 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Embedding - OpenAI
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The OpenAI Vector Embedder is an implementation of the Eino Embedding interface used to convert text into vector representations. This component implements [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide) and is primarily used in the following scenarios:
+OpenAI embedder is an implementation of Eino’s Embedding interface that converts text into vector representations. It follows [[🚧]Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide) and is typically used for:
-- When text needs to be converted into high-dimensional vector representations
-- Using OpenAI's embedding models
-- Using Azure OpenAI Service's embedding models
+- Converting text into high‑dimensional vectors
+- Using OpenAI’s embedding models
+- Using Azure OpenAI Service embedding models
-## **Usage Instructions**
+## **Usage**
-### **Component Initialization**
+### **Initialization**
-The OpenAI Vector Embedder is initialized through the `NewEmbedder` function, with the main configuration parameters as follows:
+Initialize via `NewEmbedder` with key configuration options:
```go
import "github.com/cloudwego/eino-ext/components/embedding/openai"
embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{
- // OpenAI API Configuration
+ // OpenAI API config
APIKey: "your-api-key",
Model: "text-embedding-ada-002",
Timeout: 30 * time.Second,
-
- // Optional: Azure OpenAI Service Configuration
+
+ // Optional: Azure OpenAI Service config
ByAzure: true,
BaseURL: "https://your-resource.openai.azure.com",
APIVersion: "2023-05-15",
- EncodingFormat: &format, // Encoding format
- Dimensions: &dimension, // Vector dimensions
- User: &user, // User identification
+ EncodingFormat: &format, // encoding format
+ Dimensions: &dimension, // vector dimension
+ User: &user, // user identifier
})
```
-### **Generating Vector Embeddings**
+### **Generate Embeddings**
-Text vectorization is achieved through the `EmbedStrings` method:
+Vectorization is done via `EmbedStrings`:
```go
embeddings, err := embedder.EmbedStrings(ctx, []string{
@@ -52,7 +52,7 @@ embeddings, err := embedder.EmbedStrings(ctx, []string{
})
```
-### **Complete Usage Example**
+### **Complete Example**
#### **Basic Usage**
@@ -62,14 +62,14 @@ package main
import (
"context"
"time"
-
+
"github.com/cloudwego/eino-ext/components/embedding/openai"
)
func main() {
ctx := context.Background()
-
- // Initializing the embedder
+
+ // init embedder
embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{
APIKey: "your-api-key",
Model: "text-embedding-ada-002",
@@ -78,28 +78,28 @@ func main() {
if err != nil {
panic(err)
}
-
- // Generating text vectors
+
+ // generate embeddings
texts := []string{
"This is the first sample text",
"This is the second sample text",
}
-
+
embeddings, err := embedder.EmbedStrings(ctx, texts)
if err != nil {
panic(err)
}
-
- // Using the generated vectors
+
+ // use vectors
for i, embedding := range embeddings {
- println("Text", i+1, "vector dimensions:", len(embedding))
+ println("text", i+1, "vector dim:", len(embedding))
}
}
```
-## **Related Documentation**
+## **References**
-- [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide)
-- [Embedding - ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark)
-- __OpenAI Embedding API Documentation__
-- [Azure OpenAI Service Documentation](https://learn.microsoft.com/azure/cognitive-services/openai/)
+- [Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide)
+- [Embedding - Ark](/docs/eino/ecosystem_integration/embedding/embedding_ark)
+- OpenAI Embedding API: https://platform.openai.com/docs/guides/embeddings
+- Azure OpenAI Service: https://learn.microsoft.com/azure/cognitive-services/openai/
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
index 5baeb8d1d35..0d0a0c8556b 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Embedding - qianfan
+title: Embedding - Qianfan
weight: 0
---
-you can use embedding services served by qianfan (from baidu).
+You can use Qianfan (Baidu) platform embedding service.
diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
index afae93c6b46..81c5a8ea119 100644
--- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
+++ b/content/en/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
@@ -1,31 +1,31 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Embedding - tencentcloud
+title: Embedding - TencentCloud
weight: 0
---
-## Tencent Cloud Hunyuan Embedding
+## **Tencent Cloud Hunyuan Embedding**
-A Tencent Cloud Hunyuan embedding implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Embedder` interface. This enables seamless integration with Eino's embedding system for text embedding capabilities.
+This is a Tencent Cloud Hunyuan Embedding component for [Eino](https://github.com/cloudwego/eino) implementing the `Embedder` interface. It integrates seamlessly with Eino’s embedding system and provides text vectorization.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/embedding.Embedder`
-- Easy integration with Eino's rag workflow
-- Built-in token usage tracking
-- Automatic batch processing for large text arrays
-- Built-in callback support
+- Easy to integrate into Eino RAG workflows
+- Built‑in token usage tracking
+- Automatically batches large text arrays
+- Built‑in callback support
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/embedding/tencentcloud
```
-## Quick Start
+## **Quick Start**
```go
package main
@@ -34,27 +34,27 @@ import (
"context"
"fmt"
"os"
-
+
"github.com/cloudwego/eino-ext/components/embedding/tencentcloud"
)
func main() {
ctx := context.Background()
-
- // Create embedder config
+
+ // config
cfg := &tencentcloud.EmbeddingConfig{
SecretID: os.Getenv("TENCENTCLOUD_SECRET_ID"),
SecretKey: os.Getenv("TENCENTCLOUD_SECRET_KEY"),
Region: "ap-guangzhou",
}
- // Create the embedder
+ // create embedder
embedder, err := tencentcloud.NewEmbedder(ctx, cfg)
if err != nil {
panic(err)
}
- // Get embeddings for texts
+ // get embeddings
embeddings, err := embedder.EmbedStrings(ctx, []string{"hello world", "bye world"})
if err != nil {
panic(err)
@@ -64,39 +64,38 @@ func main() {
}
```
-## Configuration
+## **Configuration**
-The embedder can be configured using the `EmbeddingConfig` struct:
+Configure via `EmbeddingConfig`:
```go
type EmbeddingConfig struct {
SecretID string // Tencent Cloud Secret ID
SecretKey string // Tencent Cloud Secret Key
- Region string // Tencent Cloud Region (e.g. "ap-hongkong")
+ Region string // Tencent Cloud region (e.g., "ap-guangzhou")
}
```
-## Features Details
-
-### Automatic Batch Processing
+## **Details**
-The embedder automatically handles batch processing for large text arrays. According to Tencent Cloud's API limitations, each request can process up to 200 texts. The embedder will automatically split larger arrays into appropriate batches.
+### **Auto Batching**
-### Token Usage Tracking
+Automatically handles large text arrays. Per Tencent Cloud API limits, a single request can process up to 200 texts; the embedder splits larger arrays into proper batches.
-The embedder tracks token usage through Eino's callback system. Token usage information includes:
-- Prompt tokens
-- Total tokens
+### **Token Usage Tracking**
-### Callbacks Support
+Tracks token usage via Eino’s callback system:
+- input token count
+- total token count
-The embedder fully supports Eino's callback system, enabling:
-- Error tracking
-- Start/End event monitoring
-- Token usage statistics
+### **Callback Support**
-## For More Details
+Fully supports Eino callbacks:
+- error tracking
+- start/end event monitoring
+- token usage statistics
-- [Tencent Cloud Hunyuan API Documentation](https://cloud.tencent.com/document/product/1729/102832)
-- [Eino Documentation](https://github.com/cloudwego/eino)
+## **More Information**
+- Tencent Hunyuan API: https://cloud.tencent.com/document/product/1729/102832
+- Eino docs: https://github.com/cloudwego/eino
diff --git a/content/en/docs/eino/ecosystem_integration/indexer/_index.md b/content/en/docs/eino/ecosystem_integration/indexer/_index.md
index e62dd64493c..c84033f8cef 100644
--- a/content/en/docs/eino/ecosystem_integration/indexer/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/indexer/_index.md
@@ -1,10 +1,11 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Indexer
weight: 0
---
-To index and store text, the Indexer typically uses [Embedding](/docs/eino/ecosystem_integration/embedding) for semantic indexing, and can also perform word segmentation indexing, etc., for recall and use in the [Retriever](/docs/eino/ecosystem_integration/retriever).
+Indexers store and index text for later retrieval. Semantic indexing generally uses [Embedding](/docs/eino/ecosystem_integration/embedding), but keyword/token indexing is also supported. See retrievers under [Retriever](/docs/eino/ecosystem_integration/retriever).
+
diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md
index f9a8fff38bc..f138e921e76 100644
--- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md
+++ b/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md
@@ -1,141 +1,118 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - es8
+title: Indexer - ES8
weight: 0
---
-## ES8 Indexer
+## **ES8 Indexer**
-An Elasticsearch 8.x indexer implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Indexer` interface. This enables seamless integration with Eino's vector storage and retrieval system for enhanced semantic search capabilities.
+This is an Elasticsearch 8.x indexer implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Indexer` interface. It integrates with Eino’s vector storage and retrieval system for semantic search.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/indexer.Indexer`
-- Easy integration with Eino's indexer system
+- Easy integration with Eino indexing
- Configurable Elasticsearch parameters
-- Support for vector similarity search
-- Bulk indexing operations
-- Custom field mapping support
-- Flexible document vectorization
+- Supports vector similarity search
+- Batch indexing operations
+- Custom field mapping
+- Flexible document embedding
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/indexer/es8@latest
```
-## Quick Start
+## **Quick Start**
-Here's a quick example of how to use the indexer, you could read components/indexer/es8/examples/indexer/add_documents.go for more details:
+Example usage (see `components/indexer/es8/examples/indexer/add_documents.go` for details):
```go
import (
- "github.com/cloudwego/eino/components/embedding"
- "github.com/cloudwego/eino/schema"
- "github.com/elastic/go-elasticsearch/v8"
-
- "github.com/cloudwego/eino-ext/components/indexer/es8"
+ "github.com/cloudwego/eino/components/embedding"
+ "github.com/cloudwego/eino/schema"
+ "github.com/elastic/go-elasticsearch/v8"
+ "github.com/cloudwego/eino-ext/components/indexer/es8"
)
const (
- indexName = "eino_example"
- fieldContent = "content"
- fieldContentVector = "content_vector"
- fieldExtraLocation = "location"
- docExtraLocation = "location"
+ indexName = "eino_example"
+ fieldContent = "content"
+ fieldContentVector = "content_vector"
+ fieldExtraLocation = "location"
+ docExtraLocation = "location"
)
func main() {
- ctx := context.Background()
-
- // es supports multiple ways to connect
- username := os.Getenv("ES_USERNAME")
- password := os.Getenv("ES_PASSWORD")
- httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
-
- cert, err := os.ReadFile(httpCACertPath)
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
-
- client, err := elasticsearch.NewClient(elasticsearch.Config{
- Addresses: []string{"https://localhost:9200"},
- Username: username,
- Password: password,
- CACert: cert,
- })
- if err != nil {
- log.Panicf("connect es8 failed, err=%v", err)
- }
-
- // create embedding component
- emb := createYourEmbedding()
-
- // load docs
- docs := loadYourDocs()
-
- // create es indexer component
- indexer, err := es8.NewIndexer(ctx, &es8.IndexerConfig{
- Client: client,
- Index: indexName,
- BatchSize: 10,
- DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es8.FieldValue, err error) {
- return map[string]es8.FieldValue{
- fieldContent: {
- Value: doc.Content,
- EmbedKey: fieldContentVector, // vectorize doc content and save vector to field "content_vector"
- },
- fieldExtraLocation: {
- Value: doc.MetaData[docExtraLocation],
- },
- }, nil
- },
- Embedding: emb, // replace it with real embedding component
- })
- if err != nil {
- log.Panicf("create indexer failed, err=%v", err)
- }
-
- ids, err := indexer.Store(ctx, docs)
- if err != nil {
- log.Panicf("store docs failed, err=%v", err)
- }
-
- fmt.Println(ids)
- // Use with Eino's system
- // ... configure and use with Eino
+ ctx := context.Background()
+
+ username := os.Getenv("ES_USERNAME")
+ password := os.Getenv("ES_PASSWORD")
+ httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
+
+ cert, err := os.ReadFile(httpCACertPath)
+ if err != nil { log.Fatalf("read file failed, err=%v", err) }
+
+ client, err := elasticsearch.NewClient(elasticsearch.Config{
+ Addresses: []string{"https://localhost:9200"},
+ Username: username,
+ Password: password,
+ CACert: cert,
+ })
+ if err != nil { log.Panicf("connect es8 failed, err=%v", err) }
+
+ emb := createYourEmbedding()
+ docs := loadYourDocs()
+
+ indexer, err := es8.NewIndexer(ctx, &es8.IndexerConfig{
+ Client: client,
+ Index: indexName,
+ BatchSize: 10,
+ DocumentToFields: func(ctx context.Context, doc *schema.Document) (map[string]es8.FieldValue, error) {
+ return map[string]es8.FieldValue{
+ fieldContent: { Value: doc.Content, EmbedKey: fieldContentVector },
+ fieldExtraLocation: { Value: doc.MetaData[docExtraLocation] },
+ }, nil
+ },
+ Embedding: emb,
+ })
+ if err != nil { log.Panicf("create indexer failed, err=%v", err) }
+
+ ids, err := indexer.Store(ctx, docs)
+ if err != nil { log.Panicf("create docs failed, err=%v", err) }
+ fmt.Println(ids)
}
```
-## Configuration
+## **Configuration**
-The indexer can be configured using the `IndexerConfig` struct:
+Configure via `IndexerConfig`:
```go
type IndexerConfig struct {
- Client *elasticsearch.Client // Required: Elasticsearch client instance
- Index string // Required: Index name to store documents
- BatchSize int // Optional: Max texts size for embedding (default: 5)
+ Client *elasticsearch.Client // required: Elasticsearch client instance
+ Index string // required: index name to store documents
+ BatchSize int // optional: batch size (default: 5)
- // Required: Function to map Document fields to Elasticsearch fields
+ // required: map document fields to ES fields
DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error)
- // Optional: Required only if vectorization is needed
+ // optional: only needed when embedding is required
Embedding embedding.Embedder
}
-// FieldValue defines how a field should be stored and vectorized
type FieldValue struct {
- Value any // Original value to store
- EmbedKey string // If set, Value will be vectorized and saved
- Stringify func(val any) (string, error) // Optional: custom string conversion
+ Value any // raw value to store
+ EmbedKey string // if set, Value will be embedded and saved under this field
+ Stringify func(val any) (string, error) // optional: custom string conversion
}
```
-## For More Details
+## **More Details**
-- [Eino Documentation](https://github.com/cloudwego/eino)
-- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch)
+ - [Eino docs](https://github.com/cloudwego/eino)
+ - [Elasticsearch Go client](https://github.com/elastic/go-elasticsearch)
diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvus.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
index 5e42042adc4..2c130d57cf6 100644
--- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
+++ b/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
@@ -1,164 +1,101 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - milvus
+title: Indexer - Milvus
weight: 0
---
-## Milvus Indexer
+## **Milvus Storage**
-An Milvus 2.x indexer implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Indexer`
-interface. This enables seamless integration
-with Eino's vector storage and retrieval system for enhanced semantic search capabilities.
+Vector storage based on Milvus 2.x that provides an `Indexer` implementation for [Eino](https://github.com/cloudwego/eino). Integrates with Eino’s vector storage and retrieval for semantic search.
-## Quick Start
+## **Quick Start**
-### Installation
+### **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/indexer/milvus
```
-### Create the Milvus Indexer
+### **Create Milvus Storage**
```go
package main
import (
- "context"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/embedding/ark"
- "github.com/cloudwego/eino/schema"
- "github.com/milvus-io/milvus-sdk-go/v2/client"
-
- "github.com/cloudwego/eino-ext/components/indexer/milvus"
+ "context"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/embedding/ark"
+ "github.com/cloudwego/eino/schema"
+ "github.com/milvus-io/milvus-sdk-go/v2/client"
+
+ "github.com/cloudwego/eino-ext/components/indexer/milvus"
)
func main() {
- // Get the environment variables
- addr := os.Getenv("MILVUS_ADDR")
- username := os.Getenv("MILVUS_USERNAME")
- password := os.Getenv("MILVUS_PASSWORD")
- arkApiKey := os.Getenv("ARK_API_KEY")
- arkModel := os.Getenv("ARK_MODEL")
-
- // Create a client
- ctx := context.Background()
- cli, err := client.NewClient(ctx, client.Config{
- Address: addr,
- Username: username,
- Password: password,
- })
- if err != nil {
- log.Fatalf("Failed to create client: %v", err)
- return
- }
- defer cli.Close()
-
- // Create an embedding model
- emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
- APIKey: arkApiKey,
- Model: arkModel,
- })
- if err != nil {
- log.Fatalf("Failed to create embedding: %v", err)
- return
- }
-
- // Create an indexer
- indexer, err := milvus.NewIndexer(ctx, &milvus.IndexerConfig{
- Client: cli,
- Embedding: emb,
- })
- if err != nil {
- log.Fatalf("Failed to create indexer: %v", err)
- return
- }
- log.Printf("Indexer created success")
-
- // Store documents
- docs := []*schema.Document{
- {
- ID: "milvus-1",
- Content: "milvus is an open-source vector database",
- MetaData: map[string]any{
- "h1": "milvus",
- "h2": "open-source",
- "h3": "vector database",
- },
- },
- {
- ID: "milvus-2",
- Content: "milvus is a distributed vector database",
- },
- }
- ids, err := indexer.Store(ctx, docs)
- if err != nil {
- log.Fatalf("Failed to store: %v", err)
- return
- }
- log.Printf("Store success, ids: %v", ids)
+ addr := os.Getenv("MILVUS_ADDR")
+ username := os.Getenv("MILVUS_USERNAME")
+ password := os.Getenv("MILVUS_PASSWORD")
+ arkApiKey := os.Getenv("ARK_API_KEY")
+ arkModel := os.Getenv("ARK_MODEL")
+
+ ctx := context.Background()
+ cli, err := client.NewClient(ctx, client.Config{ Address: addr, Username: username, Password: password })
+ if err != nil { log.Fatalf("Failed to create client: %v", err) }
+ defer cli.Close()
+
+ emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ APIKey: arkApiKey, Model: arkModel })
+ if err != nil { log.Fatalf("Failed to create embedding: %v", err) }
+
+ indexer, err := milvus.NewIndexer(ctx, &milvus.IndexerConfig{ Client: cli, Embedding: emb })
+ if err != nil { log.Fatalf("Failed to create indexer: %v", err) }
+ log.Printf("Indexer created success")
+
+ docs := []*schema.Document{
+ { ID: "milvus-1", Content: "milvus is an open-source vector database", MetaData: map[string]any{ "h1": "milvus", "h2": "open-source", "h3": "vector database" } },
+ { ID: "milvus-2", Content: "milvus is a distributed vector database" },
+ }
+ ids, err := indexer.Store(ctx, docs)
+ if err != nil { log.Fatalf("Failed to store: %v", err) }
+ log.Printf("Store success, ids: %v", ids)
}
```
-## Configuration
+## **Configuration**
```go
type IndexerConfig struct {
- // Client is the milvus client to be called
- // Required
- Client client.Client
-
- // Default Collection config
- // Collection is the collection name in milvus database
- // Optional, and the default value is "eino_collection"
- // If you want to use this configuration, you must include the Fields configuration
+ Client client.Client // required
+
+ // Default collection config
Collection string
- // Description is the description for collection
- // Optional, and the default value is "the collection for eino"
Description string
- // PartitionNum is the collection partition number
- // Optional, and the default value is 1(disable)
- // If the partition number is larger than 1, it means use partition and must have a partition key in Fields
PartitionNum int64
- // Fields is the collection fields
- // Optional, and the default value is the default fields
- Fields []*entity.Field
- // SharedNum is the milvus required param to create collection
- // Optional, and the default value is 1
+ Fields []*entity.Field
SharedNum int32
- // ConsistencyLevel is the milvus collection consistency tactics
- // Optional, and the default level is ClBounded(bounded consistency level with default tolerance of 5 seconds)
ConsistencyLevel ConsistencyLevel
- // EnableDynamicSchema is means the collection is enabled to dynamic schema
- // Optional, and the default value is false
- // Enable to dynamic schema it could affect milvus performance
EnableDynamicSchema bool
-
- // DocumentConverter is the function to convert the schema.Document to the row data
- // Optional, and the default value is defaultDocumentConverter
+
+ // Convert schema.Document to row data
DocumentConverter func(ctx context.Context, docs []*schema.Document, vectors [][]float64) ([]interface{}, error)
-
- // Index config to the vector column
- // MetricType the metric type for vector
- // Optional and default type is HAMMING
+
+ // Vector index config
MetricType MetricType
-
- // Embedding vectorization method for values needs to be embedded from schema.Document's content.
- // Required
+
+ // Embedding method to vectorize content (required)
Embedding embedding.Embedder
}
```
-## Default Collection Schema
+## **Default Schema**
-| Field | Type | DataBase Type | Index Type | Description | Remark |
-|----------|----------------|---------------|----------------------------|-------------------------|--------------------|
-| id | string | varchar | | Document ID | Max Length: 255 |
-| content | string | varchar | | Document content | Max Length: 1024 |
-| vector | []byte | binary array | HAMMING(default) / JACCARD | Document content vector | Default Dim: 81920 |
-| metadata | map[string]any | json | | Document meta data | |
+| field | type | column type | index type | desc | note |
+|----------|-----------------|---------------|--------------------------------|-------------|--------------|
+| id | string | varchar | | unique id | max len: 255 |
+| content | string | varchar | | content | max len: 1024|
+| vector | []byte | binary array | HAMMING(default) / JACCARD | content vec | dim: 81920 |
+| metadata | map[string]any | json | | metadata | |
diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md
index 636c99bb457..e94dc64e4f3 100644
--- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md
+++ b/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - redis
+title: Indexer - Redis
weight: 0
---
-you can use redis-stack as indexer.
+You can use redis-stack as an indexer.
diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md
index 63993a9674d..c8bf0b8add1 100644
--- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md
+++ b/content/en/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md
@@ -1,80 +1,80 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
-title: Indexer - volc VikingDB
+title: Indexer - volc vikingdb
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-Volcano Engine VikingDB Vector Indexer is an implementation of the Indexer interface, used to store document content into Volcano Engine's VikingDB vector database. This component implements the instructions detailed in [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide).
+Volcengine VikingDB vector indexer is an implementation of the Indexer interface. It stores document content into Volcengine’s VikingDB vector database. This component follows the guide [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide).
-### **Volcano Engine VikingDB Service Introduction**
+### **VikingDB Service Overview**
-Volcano Engine VikingDB is a high-performance vector database service that provides vector storage, retrieval, and vectorization functionalities. This component interacts with the service via the Volcano Engine SDK and supports two vectorization methods:
+VikingDB is a high‑performance vector database service that provides vector storage, retrieval, and embedding. This component interacts with the service via the Volcengine SDK and supports two embedding approaches:
-- Using VikingDB's built-in vectorization method (Embedding V2)
-- Using custom vector embedding models
+- Use VikingDB’s built‑in embedding (Embedding V2)
+- Use a custom embedding model
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The Volcengine VikingDB indexer is initialized via the `NewIndexer` function. The main configuration parameters are as follows:
+Initialize the VikingDB indexer via `NewIndexer` with key configuration options:
```go
import "github.com/cloudwego/eino-ext/components/indexer/volc_vikingdb"
indexer, err := volc_vikingdb.NewIndexer(ctx, &volc_vikingdb.IndexerConfig{
- Host: "api-vikingdb.volces.com", // Service address
- Region: "cn-beijing", // Region
- AK: "your-ak", // Access Key
- SK: "your-sk", // Secret Key
- Scheme: "https", // Protocol
- ConnectionTimeout: 30, // Connection timeout (seconds)
-
- Collection: "your-collection", // Collection name
-
+ Host: "api-vikingdb.volces.com", // service host
+ Region: "cn-beijing", // region
+ AK: "your-ak", // Access Key
+ SK: "your-sk", // Secret Key
+ Scheme: "https", // protocol
+ ConnectionTimeout: 30, // connection timeout (seconds)
+
+ Collection: "your-collection", // collection name
+
EmbeddingConfig: volc_vikingdb.EmbeddingConfig{
- UseBuiltin: true, // Whether to use built-in vectorization
- ModelName: "text2vec-base", // Model name
- UseSparse: true, // Whether to use sparse vectors
- Embedding: embedder, // Custom vector embedder
+ UseBuiltin: true, // use built-in embedding
+ ModelName: "text2vec-base", // model name
+ UseSparse: true, // use sparse vectors
+ Embedding: embedder, // custom embedder
},
-
- AddBatchSize: 5, // Batch add size
+
+ AddBatchSize: 5, // batch add size
})
```
-### **Complete Usage Example**
+### **Complete Examples**
-#### **Using Built-In Vectorization**
+#### **Using Built‑in Embedding**
```go
package main
import (
"context"
-
+
volcvikingdb "github.com/cloudwego/eino-ext/components/indexer/volc_vikingdb"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
-
- // Initialize the indexer
+
+ // init indexer
idx, err := volcvikingdb.NewIndexer(ctx, &volcvikingdb.IndexerConfig{
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
AK: "your-ak",
SK: "your-sk",
Scheme: "https",
-
+
Collection: "test-collection",
-
+
EmbeddingConfig: volcvikingdb.EmbeddingConfig{
UseBuiltin: true,
ModelName: "text2vec-base",
@@ -84,38 +84,33 @@ func main() {
if err != nil {
panic(err)
}
-
- // Prepare documents
+
+ // documents
docs := []*schema.Document{
- {
- Content: "This is the content of the first document",
- },
- {
- Content: "This is the content of the second document",
- },
+ { Content: "This is the first document content" },
+ { Content: "This is the second document content" },
}
-
- // Store documents
+
+ // store
ids, err := idx.Store(ctx, docs)
if err != nil {
panic(err)
}
-
- // Handle returned IDs
+
for i, id := range ids {
- println("Document", i+1, "storage ID:", id)
+ println("doc", i+1, "stored ID:", id)
}
}
```
-#### **Using Custom Vector Embedding**
+#### **Using Custom Embedding**
```go
package main
import (
"context"
-
+
volcvikingdb "github.com/cloudwego/eino-ext/components/indexer/volc_vikingdb"
"github.com/cloudwego/eino/components/embedding"
"github.com/cloudwego/eino/schema"
@@ -123,23 +118,23 @@ import (
func main() {
ctx := context.Background()
-
- // Initialize vector embedder (openai example)
+
+ // init embedder (openai example)
embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{})
if err != nil {
panic(err)
}
-
- // Initialize the indexer
+
+ // init indexer
idx, err := volcvikingdb.NewIndexer(ctx, &volcvikingdb.IndexerConfig{
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
AK: "your-ak",
SK: "your-sk",
Scheme: "https",
-
+
Collection: "test-collection",
-
+
EmbeddingConfig: volcvikingdb.EmbeddingConfig{
UseBuiltin: false,
Embedding: embedder,
@@ -148,32 +143,25 @@ func main() {
if err != nil {
panic(err)
}
-
- // Prepare documents
+
docs := []*schema.Document{
- {
- Content: "Document content one",
- },
- {
- Content: "Document content two",
- },
+ { Content: "Document content one" },
+ { Content: "Document content two" },
}
-
- // Store documents
+
ids, err := idx.Store(ctx, docs)
if err != nil {
panic(err)
}
-
- // Handle returned IDs
+
for i, id := range ids {
- println("Document", i+1, "storage ID:", id)
+ println("doc", i+1, "stored ID:", id)
}
}
```
-## **Related Documents**
+## **References**
-- [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide)
-- [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
-- [Volcano Engine VikingDB User Guide](https://www.volcengine.com/docs/84313/1254617)
+- [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide)
+- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
+- VikingDB: https://www.volcengine.com/docs/84313/1254617
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/_index.md b/content/en/docs/eino/ecosystem_integration/retriever/_index.md
index 19189c42551..1651e5d9de0 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/_index.md
@@ -1,10 +1,11 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
title: Retriever
weight: 0
---
-Retriever is used to recall the contents after the [Eino: Indexer guide](/docs/eino/core_modules/components/indexer_guide) builds the index. In AI applications, [Eino: Embedding guide](/docs/eino/core_modules/components/embedding_guide) is generally used for semantic similarity recall.
+Retrievers recall content indexed by [Indexer](/docs/eino/ecosystem_integration/indexer). In AI applications, they typically use [Embedding](/docs/eino/ecosystem_integration/embedding) vectors for semantic similarity recall.
+
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md
index 6597ceefc9a..6b9c11176a2 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md
@@ -1,110 +1,97 @@
---
Description: ""
-date: "2025-01-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - dify
+title: Retriever - Dify
weight: 0
---
-## Dify Retriever
+## **Dify Retriever**
-A Dify retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. This enables seamless integration with Eino's retrieval system for retrieving relevant documents from Dify datasets.
+This is a Dify retriever for [Eino](https://github.com/cloudwego/eino) implementing the `Retriever` interface. It integrates with Eino’s retrieval system and fetches relevant documents from Dify datasets.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/retriever.Retriever`
-- Easy integration with Eino's retrieval system
-- Support for configurable retrieval parameters
-- Reranking support
+- Easy integration with Eino retrieval
+- Configurable retrieval parameters
+- Supports reranking
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/dify
```
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/retriever/dify"
+ "github.com/cloudwego/eino-ext/components/retriever/dify"
)
-
-
func main() {
- APIKey := os.Getenv("DIFY_DATASET_API_KEY")
- Endpoint := os.Getenv("DIFY_ENDPOINT")
- DatasetID := os.Getenv("DIFY_DATASET_ID")
- ctx := context.Background()
-
- // create Dify Retriever
- ret, err := dify.NewRetriever(ctx, &dify.RetrieverConfig{
- APIKey: APIKey,
- Endpoint: Endpoint,
- DatasetID: DatasetID,
- })
- if err != nil {
- log.Fatalf("Failed to create retriever: %v", err)
- }
-
- // do search
- docs, err := ret.Retrieve(ctx, "test query")
- if err != nil {
- log.Fatalf("Failed to retrieve: %v", err)
- }
-
- // print docs
- for _, doc := range docs {
- fmt.Printf("doc id: %s\n", doc.ID)
- fmt.Printf("doc content: %s\n", doc.Content)
- fmt.Printf("score: %v\n\n", doc.Score())
- }
+ APIKey := os.Getenv("DIFY_DATASET_API_KEY")
+ Endpoint := os.Getenv("DIFY_ENDPOINT")
+ DatasetID := os.Getenv("DIFY_DATASET_ID")
+ ctx := context.Background()
+
+ ret, err := dify.NewRetriever(ctx, &dify.RetrieverConfig{ APIKey: APIKey, Endpoint: Endpoint, DatasetID: DatasetID })
+ if err != nil { log.Fatalf("Failed to create retriever: %v", err) }
+
+ docs, err := ret.Retrieve(ctx, "test query")
+ if err != nil { log.Fatalf("Failed to retrieve: %v", err) }
+
+ for _, doc := range docs {
+ fmt.Printf("doc id: %s\n", doc.ID)
+ fmt.Printf("doc content: %s\n", doc.Content)
+ fmt.Printf("score: %v\n\n", doc.Score())
+ }
}
```
-## Configuration
+## **Configuration**
-The retriever can be configured using the `RetrieverConfig` struct:
+Configure via `RetrieverConfig`:
```go
type RetrieverConfig struct {
- APIKey string // Dify Datasets API key
- Endpoint string // Endpoint of the Dify API, default: https://api.dify.ai/v1
- DatasetID string // DatasetID of the Dify datasets
- RetrievalModel *RetrievalModel // Retrieval model configuration
- Timeout time.Duration // HTTP connection timeout
+ APIKey string
+ Endpoint string // default: https://api.dify.ai/v1
+ DatasetID string
+ RetrievalModel *RetrievalModel
+ Timeout time.Duration
}
type RetrievalModel struct {
- SearchMethod SearchMethod // Search method
- RerankingEnable *bool // Enable reranking
- RerankingMode *string // Reranking mode
- RerankingModel *RerankingModel // Reranking model settings
- Weights *float64 // Search weights
- TopK *int // Number of documents to retrieve
- ScoreThresholdEnabled *bool // Enable score threshold
- ScoreThreshold *float64 // Minimum score threshold
+ SearchMethod SearchMethod
+ RerankingEnable *bool
+ RerankingMode *string
+ RerankingModel *RerankingModel
+ Weights *float64
+ TopK *int
+ ScoreThresholdEnabled *bool
+ ScoreThreshold *float64
}
```
-## Document Metadata
+## **Document Metadata**
-The retriever adds the following metadata to retrieved documents:
+Adds the following metadata on retrieved docs:
-- `orig_doc_id`: Original document ID in Dify
-- `orig_doc_name`: Original document name in Dify
-- `keywords`: Keywords extracted from the document
+- `orig_doc_id`: original doc ID in Dify
+- `orig_doc_name`: original doc name in Dify
+- `keywords`: extracted keywords
-You can access these metadata using the helper functions:
+Helpers:
```go
docID := dify.GetOrgDocID(doc)
@@ -112,7 +99,7 @@ docName := dify.GetOrgDocName(doc)
keywords := dify.GetKeywords(doc)
```
-## For More Details
+## **More Details**
-- [Dify API Documentation](https://github.com/langgenius/dify)
-- [Eino Documentation](https://github.com/cloudwego/eino)
\ No newline at end of file
+- Dify: https://github.com/langgenius/dify
+- Eino: https://github.com/cloudwego/eino
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md
index a547e5ec039..318cefe0a96 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md
@@ -1,177 +1,157 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - es8
+title: Retriever - ES8
weight: 0
---
-## ES8 Retriever
+## **ES8 Retriever**
-An Elasticsearch 8.x retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. This enables seamless integration with Eino's vector retrieval system for enhanced semantic search capabilities.
+This is an Elasticsearch 8.x retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. It integrates seamlessly with Eino’s vector retrieval system to enhance semantic search.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/retriever.Retriever`
-- Easy integration with Eino's retrieval system
+- Easy integration with Eino retrieval
- Configurable Elasticsearch parameters
-- Support for vector similarity search
-- Multiple search modes including approximate search
-- Custom result parsing support
+- Supports vector similarity search
+- Multiple search modes including approximate
+- Custom result parsing
- Flexible document filtering
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/es8@latest
```
-## Quick Start
+## **Quick Start**
-Here's a quick example of how to use the retriever with approximate search mode, you could read components/retriever/es8/examples/approximate/approximate.go for more details:
+Approximate search example (see `components/retriever/es8/examples/approximate/approximate.go` for details):
```go
import (
- "github.com/cloudwego/eino/components/embedding"
- "github.com/cloudwego/eino/schema"
- "github.com/elastic/go-elasticsearch/v8"
- "github.com/elastic/go-elasticsearch/v8/typedapi/types"
+ "github.com/cloudwego/eino/components/embedding"
+ "github.com/cloudwego/eino/schema"
+ "github.com/elastic/go-elasticsearch/v8"
+ "github.com/elastic/go-elasticsearch/v8/typedapi/types"
- "github.com/cloudwego/eino-ext/components/retriever/es8"
- "github.com/cloudwego/eino-ext/components/retriever/es8/search_mode"
+ "github.com/cloudwego/eino-ext/components/retriever/es8"
+ "github.com/cloudwego/eino-ext/components/retriever/es8/search_mode"
)
const (
- indexName = "eino_example"
- fieldContent = "content"
- fieldContentVector = "content_vector"
- fieldExtraLocation = "location"
- docExtraLocation = "location"
+ indexName = "eino_example"
+ fieldContent = "content"
+ fieldContentVector = "content_vector"
+ fieldExtraLocation = "location"
+ docExtraLocation = "location"
)
func main() {
- ctx := context.Background()
-
- // es supports multiple ways to connect
- username := os.Getenv("ES_USERNAME")
- password := os.Getenv("ES_PASSWORD")
- httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
-
- cert, err := os.ReadFile(httpCACertPath)
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
-
- client, err := elasticsearch.NewClient(elasticsearch.Config{
- Addresses: []string{"https://localhost:9200"},
- Username: username,
- Password: password,
- CACert: cert,
- })
- if err != nil {
- log.Panicf("connect es8 failed, err=%v", err)
- }
-
- // create retriever component
- retriever, err := es8.NewRetriever(ctx, &es8.RetrieverConfig{
- Client: client,
- Index: indexName,
- TopK: 5,
- SearchMode: search_mode.SearchModeApproximate(&search_mode.ApproximateConfig{
- QueryFieldName: fieldContent,
- VectorFieldName: fieldContentVector,
- Hybrid: true,
- // RRF only available with specific licenses
- // see: https://www.elastic.co/subscriptions
- RRF: false,
- RRFRankConstant: nil,
- RRFWindowSize: nil,
- }),
- ResultParser: func(ctx context.Context, hit types.Hit) (doc *schema.Document, err error) {
- doc = &schema.Document{
- ID: *hit.Id_,
- Content: "",
- MetaData: map[string]any{},
- }
-
- var src map[string]any
- if err = json.Unmarshal(hit.Source_, &src); err != nil {
- return nil, err
- }
-
- for field, val := range src {
- switch field {
- case fieldContent:
- doc.Content = val.(string)
- case fieldContentVector:
- var v []float64
- for _, item := range val.([]interface{}) {
- v = append(v, item.(float64))
- }
- doc.WithDenseVector(v)
- case fieldExtraLocation:
- doc.MetaData[docExtraLocation] = val.(string)
- }
- }
-
- if hit.Score_ != nil {
- doc.WithScore(float64(*hit.Score_))
- }
-
- return doc, nil
- },
- Embedding: emb, // your embedding component
- })
- if err != nil {
- log.Panicf("create retriever failed, err=%v", err)
- }
-
- // search without filter
- docs, err := retriever.Retrieve(ctx, "tourist attraction")
- if err != nil {
- log.Panicf("retrieve docs failed, err=%v", err)
- }
-
- // search with filter
- docs, err = retriever.Retrieve(ctx, "tourist attraction",
- es8.WithFilters([]types.Query{{
- Term: map[string]types.TermQuery{
- fieldExtraLocation: {
- CaseInsensitive: of(true),
- Value: "China",
- },
- },
- }}),
- )
- if err != nil {
- log.Panicf("retrieve docs failed, err=%v", err)
- }
+ ctx := context.Background()
+
+ // connections
+ username := os.Getenv("ES_USERNAME")
+ password := os.Getenv("ES_PASSWORD")
+ httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
+
+ cert, err := os.ReadFile(httpCACertPath)
+ if err != nil {
+ log.Fatalf("read file failed, err=%v", err)
+ }
+
+ client, err := elasticsearch.NewClient(elasticsearch.Config{
+ Addresses: []string{"https://localhost:9200"},
+ Username: username,
+ Password: password,
+ CACert: cert,
+ })
+ if err != nil {
+ log.Panicf("connect es8 failed, err=%v", err)
+ }
+
+ // retriever
+ retriever, err := es8.NewRetriever(ctx, &es8.RetrieverConfig{
+ Client: client,
+ Index: indexName,
+ TopK: 5,
+ SearchMode: search_mode.SearchModeApproximate(&search_mode.ApproximateConfig{
+ QueryFieldName: fieldContent,
+ VectorFieldName: fieldContentVector,
+ Hybrid: true,
+ // RRF availability depends on license
+ // see: https://www.elastic.co/subscriptions
+ RRF: false,
+ RRFRankConstant: nil,
+ RRFWindowSize: nil,
+ }),
+ ResultParser: func(ctx context.Context, hit types.Hit) (doc *schema.Document, err error) {
+ doc = &schema.Document{ ID: *hit.Id_, Content: "", MetaData: map[string]any{} }
+
+ var src map[string]any
+ if err = json.Unmarshal(hit.Source_, &src); err != nil { return nil, err }
+
+ for field, val := range src {
+ switch field {
+ case fieldContent:
+ doc.Content = val.(string)
+ case fieldContentVector:
+ var v []float64
+ for _, item := range val.([]interface{}) { v = append(v, item.(float64)) }
+ doc.WithDenseVector(v)
+ case fieldExtraLocation:
+ doc.MetaData[docExtraLocation] = val.(string)
+ }
+ }
+
+ if hit.Score_ != nil { doc.WithScore(float64(*hit.Score_)) }
+ return doc, nil
+ },
+ Embedding: emb,
+ })
+ if err != nil { log.Panicf("create retriever failed, err=%v", err) }
+
+ // search without filters
+ docs, err := retriever.Retrieve(ctx, "tourist attraction")
+ if err != nil { log.Panicf("retrieve docs failed, err=%v", err) }
+
+ // search with filters
+ docs, err = retriever.Retrieve(ctx, "tourist attraction",
+ es8.WithFilters([]types.Query{{
+ Term: map[string]types.TermQuery{
+ fieldExtraLocation: { CaseInsensitive: of(true), Value: "China" },
+ },
+ }}),
+ )
+ if err != nil { log.Panicf("retrieve docs failed, err=%v", err) }
}
```
-## Configuration
+## **Configuration**
-The retriever can be configured using the `RetrieverConfig` struct:
+Configure via `RetrieverConfig`:
```go
type RetrieverConfig struct {
- Client *elasticsearch.Client // Required: Elasticsearch client instance
- Index string // Required: Index name to retrieve documents from
- TopK int // Required: Number of results to return
+ Client *elasticsearch.Client // required: Elasticsearch client
+ Index string // required: index name
+ TopK int // required: number of results
- // Required: Search mode configuration
+ // required: search mode
SearchMode search_mode.SearchMode
- // Required: Function to parse Elasticsearch hits into Documents
+ // required: parse ES hit to Document
ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error)
- // Optional: Required only if query vectorization is needed
+ // optional: only needed if query embedding is required
Embedding embedding.Embedder
}
```
-## For More Details
+## **More Details**
-- [Eino Documentation](https://github.com/cloudwego/eino)
-- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch)
+ - [Eino docs](https://github.com/cloudwego/eino)
+ - [Elasticsearch Go client](https://github.com/elastic/go-elasticsearch)
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvus.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
index 749a14f2eec..e86ef3bf0a5 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
@@ -1,149 +1,115 @@
---
Description: ""
-date: "2025-01-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - milvus
+title: Retriever - Milvus
weight: 0
---
-## Milvus Retriever
+## **Milvus Search**
-An Milvus 2.x retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever`
-interface. This enables seamless integration
-with Eino's vector storage and retrieval system for enhanced semantic search capabilities.
+Vector search based on Milvus 2.x that provides a `Retriever` implementation for [Eino](https://github.com/cloudwego/eino). Integrates with Eino’s vector storage and retrieval for semantic search.
-## Quick Start
+## **Quick Start**
-### Installation
+### **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/milvus
```
-### Create the Milvus Retriever
+### **Create Milvus Search**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/embedding/ark"
- "github.com/milvus-io/milvus-sdk-go/v2/client"
+ "github.com/cloudwego/eino-ext/components/embedding/ark"
+ "github.com/milvus-io/milvus-sdk-go/v2/client"
- "github.com/cloudwego/eino-ext/components/retriever/milvus"
+ "github.com/cloudwego/eino-ext/components/retriever/milvus"
)
func main() {
- // Get the environment variables
- addr := os.Getenv("MILVUS_ADDR")
- username := os.Getenv("MILVUS_USERNAME")
- password := os.Getenv("MILVUS_PASSWORD")
- arkApiKey := os.Getenv("ARK_API_KEY")
- arkModel := os.Getenv("ARK_MODEL")
-
- // Create a client
- ctx := context.Background()
- cli, err := client.NewClient(ctx, client.Config{
- Address: addr,
- Username: username,
- Password: password,
- })
- if err != nil {
- log.Fatalf("Failed to create client: %v", err)
- return
- }
- defer cli.Close()
-
- // Create an embedding model
- emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
- APIKey: arkApiKey,
- Model: arkModel,
- })
-
- // Create a retriever
- retriever, err := milvus.NewRetriever(ctx, &milvus.RetrieverConfig{
- Client: cli,
- Collection: "",
- Partition: nil,
- VectorField: "",
- OutputFields: []string{
- "id",
- "content",
- "metadata",
- },
- DocumentConverter: nil,
- MetricType: "",
- TopK: 0,
- ScoreThreshold: 5,
- Sp: nil,
- Embedding: emb,
- })
- if err != nil {
- log.Fatalf("Failed to create retriever: %v", err)
- return
- }
-
- // Retrieve documents
- documents, err := retriever.Retrieve(ctx, "milvus")
- if err != nil {
- log.Fatalf("Failed to retrieve: %v", err)
- return
- }
-
- // Print the documents
- for i, doc := range documents {
- fmt.Printf("Document %d:\n", i)
- fmt.Printf("title: %s\n", doc.ID)
- fmt.Printf("content: %s\n", doc.Content)
- fmt.Printf("metadata: %v\n", doc.MetaData)
- }
+ // env vars
+ addr := os.Getenv("MILVUS_ADDR")
+ username := os.Getenv("MILVUS_USERNAME")
+ password := os.Getenv("MILVUS_PASSWORD")
+ arkApiKey := os.Getenv("ARK_API_KEY")
+ arkModel := os.Getenv("ARK_MODEL")
+
+ // client
+ ctx := context.Background()
+ cli, err := client.NewClient(ctx, client.Config{ Address: addr, Username: username, Password: password })
+ if err != nil { log.Fatalf("Failed to create client: %v", err); return }
+ defer cli.Close()
+
+ // embedder
+ emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ APIKey: arkApiKey, Model: arkModel })
+
+ // retriever
+ retriever, err := milvus.NewRetriever(ctx, &milvus.RetrieverConfig{
+ Client: cli,
+ Collection: "",
+ Partition: nil,
+ VectorField: "",
+ OutputFields: []string{ "id", "content", "metadata" },
+ DocumentConverter: nil,
+ MetricType: "",
+ TopK: 0,
+ ScoreThreshold: 5,
+ Sp: nil,
+ Embedding: emb,
+ })
+ if err != nil { log.Fatalf("Failed to create retriever: %v", err); return }
+
+ // retrieve
+ documents, err := retriever.Retrieve(ctx, "milvus")
+ if err != nil { log.Fatalf("Failed to retrieve: %v", err); return }
+
+ // print
+ for i, doc := range documents {
+ fmt.Printf("Document %d:\n", i)
+ fmt.Printf("title: %s\n", doc.ID)
+ fmt.Printf("content: %s\n", doc.Content)
+ fmt.Printf("metadata: %v\n", doc.MetaData)
+ }
}
```
-## Configuration
+## **Configuration**
```go
type RetrieverConfig struct {
- // Client is the milvus client to be called
- // Required
- Client client.Client
-
- // Default Retriever config
- // Collection is the collection name in the milvus database
- // Optional, and the default value is "eino_collection"
- Collection string
- // Partition is the collection partition name
- // Optional, and the default value is empty
- Partition []string
- // VectorField is the vector field name in the collection
- // Optional, and the default value is "vector"
- VectorField string
- // OutputFields is the fields to be returned
- // Optional, and the default value is empty
- OutputFields []string
- // DocumentConverter is the function to convert the search result to s.Document
- // Optional, and the default value is defaultDocumentConverter
- DocumentConverter func(ctx context.Context, doc client.SearchResult) ([]*s.Document, error)
- // MetricType is the metric type for vector
- // Optional, and the default value is "HAMMING"
- MetricType entity.MetricType
- // TopK is the top k results to be returned
- // Optional, and the default value is 5
- TopK int
- // ScoreThreshold is the threshold for the search result
- // Optional, and the default value is 0
- ScoreThreshold float64
- // SearchParams
- // Optional, and the default value is entity.IndexAUTOINDEXSearchParam, and the level is 1
- Sp entity.SearchParam
-
- // Embedding is the embedding vectorization method for values needs to be embedded from s.Document's content.
- // Required
- Embedding embedding.Embedder
+ // Milvus client (required)
+ Client client.Client
+
+ // Collection name (optional, default "eino_collection")
+ Collection string
+ // Partition names (optional)
+ Partition []string
+ // Vector field name (optional, default "vector")
+ VectorField string
+ // Fields to return (optional)
+ OutputFields []string
+ // Convert search result to schema.Document (optional, default converter)
+ DocumentConverter func(ctx context.Context, doc client.SearchResult) ([]*s.Document, error)
+ // Vector metric type (optional, default "HAMMING")
+ MetricType entity.MetricType
+ // Number of results (optional, default 5)
+ TopK int
+ // Score threshold (optional, default 0)
+ ScoreThreshold float64
+ // Search params (optional, default entity.IndexAUTOINDEXSearchParam, level 1)
+ Sp entity.SearchParam
+
+ // Embedding for query/content (required)
+ Embedding embedding.Embedder
}
```
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md
index 80aa49977de..aaa15a7940a 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - redis
+title: Retriever - Redis
weight: 0
---
-you can use redis-stack as retriever.
+You can use redis-stack as retriever.
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
index efe10a0051e..f418f566ddc 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - volc knowledge
+title: Retriever - volc Knowledge
weight: 0
---
-you can use volc knowledge as retriever.
+You can use volc knowledge as a retriever.
diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
index cc7790546ce..7632d75a3b0 100644
--- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
+++ b/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
@@ -1,70 +1,68 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-01-20"
lastmod: ""
tags: []
-title: Retriever - volc VikingDB
+title: Retriever - volc vikingdb
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The Volcano Engine VikingDB Retriever is an implementation of the Retriever interface. The Volcano Engine VikingDB is a vector database service provided by Volcano Engine, offering high-performance vector retrieval capabilities. This component interacts with the service through the Volcano Engine VikingDB Go SDK. The component implements the [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
+Volcengine VikingDB retriever is an implementation of the Retriever interface. VikingDB is a high‑performance vector database service that provides vector retrieval capabilities. This component interacts with the service via the Volcengine VikingDB Go SDK. It follows [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide).
## **Usage**
-### **Component Initialization**
+### **Initialization**
-The Volcengine VikingDB Retriever is initialized using the `NewRetriever` function, with the main configuration parameters as follows:
+Initialize the VikingDB retriever via `NewRetriever` with key configuration options:
```go
-import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
+import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
retriever, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{
- // Service Configuration
- Host: "api-vikingdb.volces.com", // Service address
- Region: "cn-beijing", // Region
- AK: "your-ak", // Access key ID
- SK: "your-sk", // Access key secret
- Scheme: "https", // Protocol
- ConnectionTimeout: 30, // Connection timeout (seconds)
-
- // Data Configuration
- Collection: "collection-name", // Collection name
- Index: "index-name", // Index name
-
- // Embedding Configuration
+ // service config
+ Host: "api-vikingdb.volces.com", // service host
+ Region: "cn-beijing", // region
+ AK: "your-ak", // Access Key
+ SK: "your-sk", // Secret Key
+ Scheme: "https", // protocol
+ ConnectionTimeout: 30, // connection timeout (seconds)
+
+ // data config
+ Collection: "collection-name", // collection name
+ Index: "index-name", // index name
+
+ // embedding config
EmbeddingConfig: volc_vikingdb.EmbeddingConfig{
- UseBuiltin: true, // Use built-in embedding
- ModelName: "model-name",// Model name
- UseSparse: true, // Use sparse vectors
- DenseWeight: 0.5, // Dense vector weight
- Embedding: embedder, // Custom embedder
+ UseBuiltin: true, // use built-in embedding
+ ModelName: "model-name", // model name
+ UseSparse: true, // use sparse vector
+ DenseWeight: 0.5, // dense vector weight
+ Embedding: embedder, // custom embedder
},
-
- // Retrieval Configuration
- Partition: "partition", // Partition name
- TopK: ptrOf(100), // Number of results to return
- ScoreThreshold: ptrOf(0.7), // Similarity threshold
-
- // Filter Configuration
- FilterDSL: map[string]any{ // DSL filter conditions
- "term": map[string]any{
- "field": "value",
- },
+
+ // retrieval config
+ Partition: "partition", // partition name
+ TopK: ptrOf(100), // number of results
+ ScoreThreshold: ptrOf(0.7), // similarity threshold
+
+ // filter config
+ FilterDSL: map[string]any{ // DSL filter conditions
+ "term": map[string]any{ "field": "value" },
},
})
```
### **Retrieve Documents**
-Document retrieval is implemented via the `Retrieve` method:
+Retrieve documents via `Retrieve`:
```go
docs, err := retriever.Retrieve(ctx, "query text", retriever.WithTopK(5))
```
-### **Complete Usage Example**
+### **Complete Examples**
#### **Basic Retrieval**
@@ -73,14 +71,14 @@ package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
)
func main() {
ctx := context.Background()
-
- // Initialize the retriever
+
+ // init retriever
r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
@@ -89,28 +87,24 @@ func main() {
Collection: "your-collection",
Index: "your-index",
EmbeddingConfig: volc_vikingdb.EmbeddingConfig{
- UseBuiltin: true,
- ModelName: "model-name",
- UseSparse: true,
+ UseBuiltin: true,
+ ModelName: "model-name",
+ UseSparse: true,
DenseWeight: 0.5,
},
TopK: ptrOf(5),
})
- if err != nil {
- panic(err)
- }
-
- // Execute retrieval
+ if err != nil { panic(err) }
+
+ // retrieve
docs, err := r.Retrieve(ctx, "How to use VikingDB?")
- if err != nil {
- panic(err)
- }
-
- // Process results
+ if err != nil { panic(err) }
+
+ // handle results
for _, doc := range docs {
- println("Document ID:", doc.ID)
- println("Content:", doc.Content)
- println("Similarity:", doc.MetaData["_score"])
+ println("docID:", doc.ID)
+ println("content:", doc.Content)
+ println("score:", doc.MetaData["_score"])
}
}
```
@@ -122,21 +116,19 @@ package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
"github.com/cloudwego/eino/components/embedding"
)
func main() {
ctx := context.Background()
-
- // Initialize embedder (using openai as an example)
+
+ // init embedder (openai example)
embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{})
- if err != nil {
- panic(err)
- }
-
- // Initialize the retriever
+ if err != nil { panic(err) }
+
+ // init retriever
r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{
Host: "api-vikingdb.volces.com",
Region: "cn-beijing",
@@ -149,24 +141,17 @@ func main() {
Embedding: embedder,
},
})
- if err != nil {
- panic(err)
- }
-
- // Execute retrieval
+ if err != nil { panic(err) }
+
+ // retrieve
docs, err := r.Retrieve(ctx, "query text")
- if err != nil {
- panic(err)
- }
-
- // Process results
- for _, doc := range docs {
- println(doc.Content)
- }
+ if err != nil { panic(err) }
+
+ for _, doc := range docs { println(doc.Content) }
}
```
-## **Related Documents**
+## **References**
-- [Eino: Retriever guide](/docs/eino/core_modules/components/retriever_guide)
-- [Volcano Engine VikingDB Documentation](https://www.volcengine.com/docs/84313)
+- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide)
+- VikingDB: https://www.volcengine.com/docs/84313
diff --git a/content/en/docs/eino/ecosystem_integration/tool/_index.md b/content/en/docs/eino/ecosystem_integration/tool/_index.md
index 9df36f20158..4a96c3df6e6 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/_index.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/_index.md
@@ -1,10 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-07-21"
lastmod: ""
tags: []
title: Tool
weight: 0
---
-
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md b/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
index 0bdd2e4ac02..462e5254167 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
@@ -1,89 +1,101 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - Bingsearch
weight: 0
---
-## Bing Search Tool
+## **Bing Search Tool**
-A Bing search tool implementation for [Eino](https://github.com/cloudwego/eino) that implements the `InvokableTool` interface. This enables seamless integration with Eino's ChatModel interaction system and `ToolsNode` for enhanced search capabilities.
+This is a Bing search tool for [Eino](https://github.com/cloudwego/eino) implementing the `InvokableTool` interface. It integrates seamlessly with Eino’s ChatModel interaction system and `ToolsNode`.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.InvokableTool`
-- Easy integration with Eino's tool system
+- Easy integration with Eino’s tool system
- Configurable search parameters
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/bingsearch
```
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "log"
- "os"
- "time"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/tool/bingsearch"
+ "context"
+ "log"
+ "os"
+ "time"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/tool/bingsearch"
)
func main() {
- // Set the Bing Search API key
- bingSearchAPIKey := os.Getenv("BING_SEARCH_API_KEY")
-
- // Create a context
- ctx := context.Background()
-
- // Create the Bing Search tool
- bingSearchTool, err := bingsearch.NewTool(ctx, &bingsearch.Config{
- APIKey: bingSearchAPIKey,
- Cache: 5 * time.Minute,
- })
-
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
- // ... configure and use with ToolsNode
+ // Set Bing Search API key
+ bingSearchAPIKey := os.Getenv("BING_SEARCH_API_KEY")
+
+ // Create context
+ ctx := context.Background()
+
+ // Create Bing Search tool
+ bingSearchTool, err := bingsearch.NewTool(ctx, &bingsearch.Config{
+ APIKey: bingSearchAPIKey,
+ Cache: 5 * time.Minute,
+ })
+
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
+ // ... configure and use ToolsNode
+}
```
-## Configuration
+## **Configuration**
-The tool can be configured using the `Config` struct:
+Tool is configurable via `Config`:
```go
// Config represents the Bing search tool configuration.
type Config struct {
-ToolName string `json:"tool_name"` // optional, default is "bing_search"
-ToolDesc string `json:"tool_desc"` // optional, default is "search web for information by bing"
-
-APIKey string `json:"api_key"` // required
-Region Region `json:"region"` // optional, default: ""
-MaxResults int `json:"max_results"` // optional, default: 10
-SafeSearch SafeSearch `json:"safe_search"` // optional, default: SafeSearchModerate
-TimeRange TimeRange `json:"time_range"` // optional, default: nil
-
-Headers map[string]string `json:"headers"` // optional, default: map[string]string{}
-Timeout time.Duration `json:"timeout"` // optional, default: 30 * time.Second
-ProxyURL string `json:"proxy_url"` // optional, default: ""
-Cache time.Duration `json:"cache"` // optional, default: 0 (disabled)
-MaxRetries int `json:"max_retries"` // optional, default: 3
+ // ToolName: optional, default "bing_search"
+ ToolName string `json:"tool_name"`
+ // ToolDesc: optional, default "search web for information by bing"
+ ToolDesc string `json:"tool_desc"`
+
+ // APIKey: required
+ APIKey string `json:"api_key"`
+ // Region: optional, default ""
+ Region Region `json:"region"`
+ // MaxResults: optional, default 10
+ MaxResults int `json:"max_results"`
+ // SafeSearch: optional, default SafeSearchModerate
+ SafeSearch SafeSearch `json:"safe_search"`
+ // TimeRange: optional, default nil
+ TimeRange TimeRange `json:"time_range"`
+
+ // Headers: optional, default map[string]string{}
+ Headers map[string]string `json:"headers"`
+ // Timeout: optional, default 30 * time.Second
+ Timeout time.Duration `json:"timeout"`
+ // ProxyURL: optional, default ""
+ ProxyURL string `json:"proxy_url"`
+ // Cache: optional, default 0 (disabled)
+ Cache time.Duration `json:"cache"`
+ // MaxRetries: optional, default 3
+ MaxRetries int `json:"max_retries"`
}
```
-## Search
-
-### Request Schema
+## **Search**
+### **Request Schema**
```go
type SearchRequest struct {
@@ -92,7 +104,7 @@ type SearchRequest struct {
}
```
-### Response Schema
+### **Response Schema**
```go
type SearchResponse struct {
@@ -106,7 +118,7 @@ type searchResult struct {
}
```
-## For More Details
+## **More Details**
-- [DuckDuckGo Search Library Documentation](tool_duckduckgo_search)
+- [DuckDuckGo Search Tool](tool_duckduckgo_search/)
- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_browseruse.md b/content/en/docs/eino/ecosystem_integration/tool/tool_browseruse.md
index 6b6a0da7183..89e78cbc63a 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_browseruse.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_browseruse.md
@@ -1,67 +1,57 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - Browseruse
weight: 0
---
-## BrowserUse Tool
+## **BrowserUse Tool**
A BrowserUse Tool implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Tool` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
> **Note**: This implementation is inspired by and references the [OpenManus](https://github.com/mannaandpoem/OpenManus) project.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.BaseTool`
- Easy integration with Eino's tool system
-- Support for executing browser actions
+- Supports executing browser actions
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/browseruse@latest
```
-## Quick Start
-
-Here's a quick example of how to use the browser-use tool:
+## **Quick Start**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/cloudwego/eino-ext/components/tool/browseruse"
+ "github.com/cloudwego/eino-ext/components/tool/browseruse"
)
func main() {
- ctx := context.Background()
- but, err := browseruse.NewBrowserUseTool(ctx, &browseruse.Config{})
- if err != nil {
- log.Fatal(err)
- }
-
- url := "https://www.google.com"
- result, err := but.Execute(&browseruse.Param{
- Action: browseruse.ActionGoToURL,
- URL: &url,
- })
- if err != nil {
- log.Fatal(err)
- }
- fmt.Println(result)
- time.Sleep(10 * time.Second)
- but.Cleanup()
+ ctx := context.Background()
+ but, err := browseruse.NewBrowserUseTool(ctx, &browseruse.Config{})
+ if err != nil { log.Fatal(err) }
+
+ url := "https://www.google.com"
+ result, err := but.Execute(&browseruse.Param{ Action: browseruse.ActionGoToURL, URL: &url })
+ if err != nil { log.Fatal(err) }
+ fmt.Println(result)
+ time.Sleep(10 * time.Second)
+ but.Cleanup()
}
-
```
-## For More Details
+## **For More Details**
- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_commandline.md b/content/en/docs/eino/ecosystem_integration/tool/tool_commandline.md
index 529623d21e5..da50ff58c4e 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_commandline.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_commandline.md
@@ -1,157 +1,115 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - Commandline
weight: 0
---
-## CommandLine Tool
+## **CommandLine Tool**
CommandLine Tools implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Tool` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
> **Note**: This implementation is inspired by and references the [OpenManus](https://github.com/mannaandpoem/OpenManus) project.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.InvokableTool`
- Easy integration with Eino's tool system
- Support executing command-line instructions in Docker containers
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/commandline@latest
```
-## Quick Start
-
-Here's a quick example of how to use the commandline tool:
+## **Quick Start**
Editor:
+
```go
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- "github.com/cloudwego/eino-ext/components/tool/commandline"
- "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
+ "github.com/cloudwego/eino-ext/components/tool/commandline"
+ "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
)
func main() {
- ctx := context.Background()
-
- op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
- if err != nil {
- log.Fatal(err)
- }
- // you should ensure that docker has been started before create a docker container
- err = op.Create(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer op.Cleanup(ctx)
-
- sre, err := commandline.NewStrReplaceEditor(ctx, &commandline.Config{Operator: op})
- if err != nil {
- log.Fatal(err)
- }
-
- info, err := sre.Info(ctx)
- if err != nil {
- log.Fatal(err)
- }
- log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
-
- content := "hello world"
-
- log.Println("create file[test.txt]...")
- result, err := sre.Execute(ctx, &commandline.StrReplaceEditorParams{
- Command: commandline.CreateCommand,
- Path: "./test.txt",
- FileText: &content,
- })
- if err != nil {
- log.Fatal(err)
- }
- log.Println("create file result: ", result)
-
- log.Println("view file[test.txt]...")
- result, err = sre.Execute(ctx, &commandline.StrReplaceEditorParams{
- Command: commandline.ViewCommand,
- Path: "./test.txt",
- })
- if err != nil {
- log.Fatal(err)
- }
- log.Println("view file result: ", result)
+ ctx := context.Background()
+
+ op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
+ if err != nil { log.Fatal(err) }
+ // ensure docker is running before creating a container
+ if err = op.Create(ctx); err != nil { log.Fatal(err) }
+ defer op.Cleanup(ctx)
+
+ sre, err := commandline.NewStrReplaceEditor(ctx, &commandline.Config{ Operator: op })
+ if err != nil { log.Fatal(err) }
+
+ info, err := sre.Info(ctx)
+ if err != nil { log.Fatal(err) }
+ log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
+
+ content := "hello world"
+ log.Println("create file[test.txt]...")
+ result, err := sre.Execute(ctx, &commandline.StrReplaceEditorParams{
+ Command: commandline.CreateCommand,
+ Path: "./test.txt",
+ FileText: &content,
+ })
+ if err != nil { log.Fatal(err) }
+ log.Println("create file result: ", result)
+
+ log.Println("view file[test.txt]...")
+ result, err = sre.Execute(ctx, &commandline.StrReplaceEditorParams{
+ Command: commandline.ViewCommand,
+ Path: "./test.txt",
+ })
+ if err != nil { log.Fatal(err) }
+ log.Println("view file result: ", result)
}
```
+
PyExecutor:
-```go
-/*
- * Copyright 2025 CloudWeGo Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+```go
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- "github.com/cloudwego/eino-ext/components/tool/commandline"
- "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
+ "github.com/cloudwego/eino-ext/components/tool/commandline"
+ "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
)
func main() {
- ctx := context.Background()
- op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
- if err != nil {
- log.Fatal(err)
- }
- // you should ensure that docker has been started before create a docker container
- err = op.Create(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer op.Cleanup(ctx)
-
- exec, err := commandline.NewPyExecutor(ctx, &commandline.PyExecutorConfig{Operator: op}) // use python3 by default
- if err != nil {
- log.Fatal(err)
- }
-
- info, err := exec.Info(ctx)
- if err != nil {
- log.Fatal(err)
- }
- log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
-
- code := "print(\"hello world\")"
- log.Printf("execute code:\n%s", code)
- result, err := exec.Execute(ctx, &commandline.Input{Code: code})
- if err != nil {
- log.Fatal(err)
- }
- log.Println("result:\n", result)
+ ctx := context.Background()
+ op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
+ if err != nil { log.Fatal(err) }
+ if err = op.Create(ctx); err != nil { log.Fatal(err) }
+ defer op.Cleanup(ctx)
+
+ exec, err := commandline.NewPyExecutor(ctx, &commandline.PyExecutorConfig{ Operator: op }) // use python3 by default
+ if err != nil { log.Fatal(err) }
+
+ info, err := exec.Info(ctx)
+ if err != nil { log.Fatal(err) }
+ log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
+
+ code := "print(\"hello world\")"
+ log.Printf("execute code:\n%s", code)
+ result, err := exec.Execute(ctx, &commandline.Input{ Code: code })
+ if err != nil { log.Fatal(err) }
+ log.Println("result:\n", result)
}
```
+## **For More Details**
-## For More Details
-
-- [Eino Documentation](https://github.com/cloudwego/eino)
\ No newline at end of file
+- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md b/content/en/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
index 6c914024ea5..ae9384dfc6e 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
@@ -1,37 +1,34 @@
---
Description: ""
-date: "2025-11-21"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - DuckDuckGoSearch
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-The DuckDuckGo Search Tool is an implementation of the Tool InvokableTool interface for conducting web searches through the DuckDuckGo search engine. DuckDuckGo is a privacy-focused search engine that does not track users' search behavior and can be used directly without the need for API key authentication. This component implements the [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide).
+DuckDuckGo search tool is an implementation of the Tool `InvokableTool` interface, used to perform web search via DuckDuckGo. DuckDuckGo is a privacy‑focused search engine that does not track user behavior, and more importantly it requires no API key to use directly. This component implements the [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide).
## **Usage**
+### **Initialization**
-### **Component Initialization**
-
-The DuckDuckGo Search Tool is initialized through the `NewTextSearchTool` function. The main configuration parameters are as follows:
+DuckDuckGo search tool is initialized via `NewTextSearchTool`, with core config parameters:
```go
import "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2"
tool, err := duckduckgo.NewTextSearchTool(ctx, &duckduckgo.Config{
- ToolName: "duckduckgo_search",
+ ToolName: "duckduckgo_search",
ToolDesc: "search for information by duckduckgo",
- Region: ddgsearch.RegionWT, // The geographical region for results.
- MaxResults: 3, // Limit the number of results returned.
+ Region: duckduckgo.RegionWT, // The geographical region for results.
+ MaxResults: 3, // Limit the number of results returned.
})
```
### **Search Parameters**
-The search request supports the following parameters:
-
```go
type TextSearchRequest struct {
// Query is the user's search query
@@ -42,7 +39,7 @@ type TextSearchRequest struct {
}
```
-### **Complete Usage Example**
+### **Complete Example**
```go
package main
@@ -124,8 +121,8 @@ func main() {
"title": "Eino: Overview | CloudWeGo",
"url": "https://www.cloudwego.io/docs/eino/overview/",
"summary": "Eino is a framework that simplifies and standardizes the development of LLM applications in Golang. It provides component abstractions, orchestration APIs, best practices, tools and examples for building and running LLM applications."
- }
- {
+ },
+ {
"title": "Home - Eino - AI powered network planning",
"url": "https://www.eino.ai/",
"summary": "An easy-to-use, AI powered networking planning app that helps network planners create digital twins for their projects and plan every network type."
@@ -134,8 +131,8 @@ func main() {
}
```
-## **Related Documentation**
+## **References**
-- [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)
+- [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide)
- [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch)
-- [DuckDuckGo Help Pages](https://duckduckgo.com/duckduckgo-help-pages/settings/params/)
+- [DuckDuckGo Help](https://duckduckgo.com/duckduckgo-help-pages/settings/params/)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_googlesearch.md b/content/en/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
index 1a49e7f30ee..d8bd2f696ff 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
@@ -1,21 +1,20 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-11-20"
lastmod: ""
tags: []
title: Tool - Googlesearch
weight: 0
---
-## **Basic Introduction**
+## **Overview**
-Google Search Tool is an implementation of the Eino InvokableTool interface, used for web search through the Google Custom Search API. This component implements the [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide).
+Google search tool is an implementation of Eino’s InvokableTool that performs web searches via Google Custom Search API. Follows [ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide).
## **Usage**
+### **Initialization**
-### **Component Initialization**
-
-The Google Search Tool is initialized via the `NewGoogleSearchTool` function, with the primary configuration parameters as follows:
+Google search tool is initialized via `NewGoogleSearchTool` with core parameters:
```go
import "github.com/cloudwego/eino-ext/components/tool/googlesearch"
@@ -23,28 +22,28 @@ import "github.com/cloudwego/eino-ext/components/tool/googlesearch"
tool, err := googlesearch.NewTool(ctx, &googlesearch.Config{
APIKey: "your-api-key", // Google API key
SearchEngineID: "your-engine-id", // Search engine ID
- BaseURL: "custom-base-url", // Optional: Custom API base URL, default: https://customsearch.googleapis.com
- Num: 5, // Optional: Number of results per page
- Lang: "zh-CN", // Optional: Search interface language
- ToolName: "google_search", // Optional: Tool name
- ToolDesc: "google search tool", // Optional: Tool description
+ BaseURL: "custom-base-url", // Optional: custom API base URL, default: https://customsearch.googleapis.com
+ Num: 5, // Optional: results per page
+ Lang: "zh-CN", // Optional: UI language
+ ToolName: "google_search", // Optional: tool name
+ ToolDesc: "google search tool", // Optional: tool description
})
```
-### **Search Parameters**
+Note: obtain APIKey and SearchEngineID from Google Programmable Search Engine: https://programmablesearchengine.google.com/controlpanel/all
-The search request supports the following parameters:
+### **Search Parameters**
```go
type SearchRequest struct {
- Query string `json:"query"` // Search keywords
- Num int `json:"num"` // Number of results returned
- Offset int `json:"offset"` // Start position of results
- Lang string `json:"lang"` // Search language
+ Query string `json:"query"`
+ Num int `json:"num"`
+ Offset int `json:"offset"`
+ Lang string `json:"lang"`
}
```
-### **Complete Usage Example**
+### **Complete Example**
```go
package main
@@ -82,7 +81,7 @@ func main() {
// prepare params
req := googlesearch.SearchRequest{
- Query: "Go concurrent programming",
+ Query: "Golang concurrent programming",
Num: 3,
Lang: "en",
}
@@ -123,7 +122,7 @@ func main() {
// 2. Title: Concurrency — An Introduction to Programming in Go | Go Resources
// Link: https://www.golang-book.com/books/intro/10
// Desc:
- // 3. Title: The Comprehensive Guide to Concurrency in Go | by Brandon ...
+ // 3. Title: The Comprehensive Guide to Concurrency in Golang | by Brandon ...
// Link: https://bwoff.medium.com/the-comprehensive-guide-to-concurrency-in-golang-aaa99f8bccf6
// Desc: Update (November 20, 2023) — This article has undergone a comprehensive revision for enhanced clarity and conciseness. I’ve streamlined the…
@@ -131,16 +130,16 @@ func main() {
}
```
-### **Search Results Example**
+### **Search Result Example**
```json
{
- "query": "Go concurrent programming",
+ "query": "Golang concurrent programming",
"items": [
{
"link": "https://example.com/article1",
- "title": "Go Concurrent Programming Practice",
- "snippet": "This is an article about concurrent programming in Go...",
+ "title": "Go 并发编程实践",
+ "snippet": "An article about Go concurrent programming...",
"desc": "Detailed introduction to goroutines and channels in Go..."
},
{
@@ -153,8 +152,8 @@ func main() {
}
```
-## **Related Documents**
+## **References**
-- [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)
-- [Google Custom Search API Documentation](https://developers.google.com/custom-search/v1/overview)
+- [ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)
+- [Google Custom Search API](https://developers.google.com/custom-search/v1/overview)
- [Tool - DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_httprequest.md b/content/en/docs/eino/ecosystem_integration/tool/tool_httprequest.md
index c26cc6192aa..0f90c03bdd1 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_httprequest.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_httprequest.md
@@ -1,272 +1,224 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - httprequest
weight: 0
---
-## HTTP Request Tools
+## **HTTP Request Tool**
-A set of HTTP request tools for [Eino](https://github.com/cloudwego/eino) that implement the `InvokableTool` interface. These tools allow you to perform GET, POST, PUT and DELETE requests easily and integrate them with Eino’s chat model interaction system and `ToolsNode` for enhanced functionality.
+A set of HTTP request tools for [Eino](https://github.com/cloudwego/eino) implementing the `InvokableTool` interface. These tools let you easily perform GET/POST/PUT/DELETE requests and integrate with Eino’s ChatModel interaction system and `ToolsNode` for powerful capabilities.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.InvokableTool`
-- Supports GET, POST, PUT, and DELETE requests.
-- Configurable request headers and HttpClient
+- Supports GET, POST, PUT, and DELETE requests
+- Configurable headers and `http.Client`
- Simple integration with Eino’s tool system
-## Installation
-
-Use `go get` to install the package (adjust the module path to your project structure):
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/httprequest
```
-## Quick Start
-
-Below are two examples demonstrating how to use the GET and POST tools individually.
+## **Quick Start**
+Below are standalone examples for GET and POST tools.
-### GET Request Example
+### **GET Example**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/bytedance/sonic"
- req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
+ "github.com/bytedance/sonic"
+ get "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
)
func main() {
- // Configure the GET tool
- config := &req.Config{
- // Headers is optional
- Headers: map[string]string{
- "User-Agent": "MyCustomAgent",
- },
- // HttpClient is optional
- HttpClient: &http.Client{
- Timeout: 30 * time.Second,
- Transport: &http.Transport{},
- },
- }
-
- ctx := context.Background()
-
- // Create the GET tool
- tool, err := req.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
-
- // Prepare the GET request payload
- request := &req.GetRequest{
- URL: "https://jsonplaceholder.typicode.com/posts",
- }
-
- jsonReq, err := sonic.Marshal(request)
- if err != nil {
- log.Fatalf("Error marshaling JSON: %v", err)
- }
-
- // Execute the GET request using the InvokableTool interface
- resp, err := tool.InvokableRun(ctx, string(jsonReq))
- if err != nil {
- log.Fatalf("GET request failed: %v", err)
- }
-
- fmt.Println(resp)
+ // Configure the GET tool
+ config := &get.Config{
+ // Headers is optional
+ Headers: map[string]string{
+ "User-Agent": "MyCustomAgent",
+ },
+ // HttpClient is optional
+ HttpClient: &http.Client{
+ Timeout: 30 * time.Second,
+ Transport: &http.Transport{},
+ },
+ }
+
+ ctx := context.Background()
+
+ // Create the GET tool
+ tool, err := get.NewTool(ctx, config)
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
+
+ // Prepare the GET request payload
+ request := &get.GetRequest{
+ URL: "https://jsonplaceholder.typicode.com/posts",
+ }
+
+ args, err := sonic.Marshal(request)
+ if err != nil {
+ log.Fatalf("Error marshaling JSON: %v", err)
+ }
+
+ // Execute the GET request via InvokableTool
+ resp, err := tool.InvokableRun(ctx, string(args))
+ if err != nil {
+ log.Fatalf("GET request failed: %v", err)
+ }
+
+ fmt.Println(resp)
}
```
-### POST Request Example
+### **POST Example**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/bytedance/sonic"
- post "github.com/cloudwego/eino-ext/components/tool/httprequest/post"
+ "github.com/bytedance/sonic"
+ post "github.com/cloudwego/eino-ext/components/tool/httprequest/post"
)
func main() {
- config := &post.Config{}
-
- ctx := context.Background()
+ config := &post.Config{}
- tool, err := post.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
+ ctx := context.Background()
- request := &post.PostRequest{
- URL: "https://jsonplaceholder.typicode.com/posts",
- Body: `{"title": "my title","body": "my body","userId": 1}`,
- }
+ tool, err := post.NewTool(ctx, config)
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
- jsonReq, err := sonic.Marshal(request)
+ request := &post.PostRequest{
+ URL: "https://jsonplaceholder.typicode.com/posts",
+ Body: `{"title": "my title","body": "my body","userId": 1}`,
+ }
- if err != nil {
- log.Fatalf("Error marshaling JSON: %v", err)
- }
+ args, err := sonic.Marshal(request)
+ if err != nil {
+ log.Fatalf("Error marshaling JSON: %v", err)
+ }
- resp, err := tool.InvokableRun(ctx, string(jsonReq))
- if err != nil {
- log.Fatalf("Post failed: %v", err)
- }
+ resp, err := tool.InvokableRun(ctx, string(args))
+ if err != nil {
+ log.Fatalf("Post failed: %v", err)
+ }
- fmt.Println(resp)
+ fmt.Println(resp)
}
-
```
-## Configuration
+## **Configuration**
-GET, POST, PUT and DELETE tools share similar configuration parameters defined in their respective `Config` structs. For example:
+Shared `Config` fields (example):
```go
-// Config represents the common configuration for HTTP request tools.
+// Config represents common settings for HTTP request tools.
type Config struct {
- // Inspired by the "Requests" tool from the LangChain project, specifically the RequestsGetTool.
- // For more details, visit: https://python.langchain.com/docs/integrations/tools/requests/
- // Optional. Default: "request_get".
- ToolName string `json:"tool_name"`
- // Optional. Default: "A portal to the internet. Use this tool when you need to fetch specific content from a website.
- // Input should be a URL (e.g., https://www.google.com). The output will be the text response from the GET request."
- ToolDesc string `json:"tool_desc"`
-
- // Headers is a map of HTTP header names to their corresponding values.
- // These headers will be included in every request made by the tool.
- Headers map[string]string `json:"headers"`
-
- // HttpClient is the HTTP client used to perform the requests.
- // If not provided, a default client with a 30-second timeout and a standard transport
- // will be initialized and used.
- HttpClient *http.Client
+ // Inspired by LangChain's Requests tools, especially RequestsGetTool.
+ // See: https://python.langchain.com/docs/integrations/tools/requests/
+ // Optional. Default: "request_get".
+ ToolName string `json:"tool_name"`
+ // Optional. Default:
+ // "A portal to the internet. Use this tool when you need to fetch specific content from a website.
+ // Input should be a URL (e.g., https://www.google.com). The output will be the text response from the GET request."
+ ToolDesc string `json:"tool_desc"`
+
+ // Headers maps header names to values added on each request.
+ Headers map[string]string `json:"headers"`
+
+ // HttpClient executes HTTP requests.
+ // If not provided, a default client with 30s timeout and standard transport is used.
+ HttpClient *http.Client
}
```
-For the GET tool, the request schema is defined as:
+Request Schemas:
```go
type GetRequest struct {
- URL string `json:"url" jsonschema_description:"The URL to perform the GET request"`
+ URL string `json:"url" jsonschema_description:"URL to perform GET request on"`
}
-```
-And for the POST tool, the request schema is:
-
-```go
type PostRequest struct {
- URL string `json:"url" jsonschema_description:"The URL to perform the POST request"`
- Body string `json:"body" jsonschema_description:"The request body to be sent in the POST request"`
+ URL string `json:"url" jsonschema_description:"URL to perform POST request on"`
+ Body string `json:"body" jsonschema_description:"Request body to send for POST"`
}
```
-## Example with agent
+## **Agent Integration Example**
+
+Bind tool info to a `ToolCallingChatModel` and run via `ToolsNode`. Full example:
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/model/openai"
- req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ get "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
)
-// float32Ptr is a helper to return a pointer for a float32 value.
-func float32Ptr(f float32) *float32 {
- return &f
-}
+func float32Ptr(f float32) *float32 { return &f }
func main() {
- // Load OpenAI API key from environment variables.
- openAIAPIKey := os.Getenv("OPENAI_API_KEY")
- if openAIAPIKey == "" {
- log.Fatal("OPENAI_API_KEY not set")
- }
-
- ctx := context.Background()
-
- // Setup GET tool configuration.
- config := &req.Config{
- Headers: map[string]string{
- "User-Agent": "MyCustomAgent",
- },
- }
-
- // Instantiate the GET tool.
- getTool, err := req.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create GET tool: %v", err)
- }
-
- // Retrieve the tool info to bind it to the ChatModel.
- toolInfo, err := getTool.Info(ctx)
- if err != nil {
- log.Fatalf("Failed to get tool info: %v", err)
- }
-
- // Create the ChatModel using OpenAI.
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- Model: "gpt-4o", // or another supported model
- APIKey: openAIAPIKey,
- Temperature: float32Ptr(0.7),
- })
- if err != nil {
- log.Fatalf("Failed to create ChatModel: %v", err)
- }
-
- // Bind the tool to the ChatModel.
- err = chatModel.BindTools([]*schema.ToolInfo{toolInfo})
- if err != nil {
- log.Fatalf("Failed to bind tool to ChatModel: %v", err)
- }
-
- // Create the Tools node with the GET tool.
- toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{getTool},
- })
- if err != nil {
- log.Fatalf("Failed to create ToolNode: %v", err)
- }
-
- // Build the chain with the ChatModel and the Tools node.
- chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
- chain.
- AppendChatModel(chatModel, compose.WithNodeName("chat_model")).
- AppendToolsNode(toolsNode, compose.WithNodeName("tools"))
-
- // Compile the chain to obtain the agent.
- agent, err := chain.Compile(ctx)
- if err != nil {
- log.Fatalf("Failed to compile chain: %v", err)
- }
-
- // Define the API specification (api_spec) in OpenAPI (YAML) format.
- apiSpec := `
+ openAIAPIKey := os.Getenv("OPENAI_API_KEY")
+ if openAIAPIKey == "" { log.Fatal("OPENAI_API_KEY not set") }
+
+ ctx := context.Background()
+
+ cfg := &get.Config{ Headers: map[string]string{ "User-Agent": "MyCustomAgent" } }
+ getTool, err := get.NewTool(ctx, cfg)
+ if err != nil { log.Fatalf("Failed to create GET tool: %v", err) }
+
+ info, err := getTool.Info(ctx)
+ if err != nil { log.Fatalf("Failed to get tool info: %v", err) }
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ Model: "gpt-4o", APIKey: openAIAPIKey, Temperature: float32Ptr(0.7) })
+ if err != nil { log.Fatalf("Failed to create ChatModel: %v", err) }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{info})
+ if err != nil { log.Fatalf("Failed to bind tool: %v", err) }
+
+ toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{ Tools: []tool.BaseTool{getTool} })
+ if err != nil { log.Fatalf("Failed to create ToolNode: %v", err) }
+
+ chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
+ chain.AppendChatModel(chatModel, compose.WithNodeName("chat_model")).AppendToolsNode(toolsNode, compose.WithNodeName("tools"))
+
+ agent, err := chain.Compile(ctx)
+ if err != nil { log.Fatalf("Failed to compile chain: %v", err) }
+
+ apiSpec := `
openapi: "3.0.0"
info:
title: JSONPlaceholder API
@@ -336,37 +288,17 @@ paths:
type: string
`
- // Create a system message that includes the API documentation.
- systemMessage := fmt.Sprintf(`You have access to an API to help answer user queries.
-Here is documentation on the API:
-%s`, apiSpec)
-
- // Define initial messages (system and user).
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: systemMessage,
- },
- {
- Role: schema.User,
- Content: "Fetch the top two posts. What are their titles?",
- },
- }
-
- // Invoke the agent with the messages.
- resp, err := agent.Invoke(ctx, messages)
- if err != nil {
- log.Fatalf("Failed to invoke agent: %v", err)
- }
-
- // Output the response messages.
- for idx, msg := range resp {
- fmt.Printf("Message %d: %s: %s\n", idx, msg.Role, msg.Content)
- }
+ systemMessage := fmt.Sprintf("You have access to an API to help answer user queries.\nHere is documentation on the API:\n%s", apiSpec)
+
+ messages := []*schema.Message{ { Role: schema.System, Content: systemMessage }, { Role: schema.User, Content: "Fetch the top two posts. What are their titles?" } }
+ resp, err := agent.Invoke(ctx, messages)
+ if err != nil { log.Fatalf("Failed to invoke agent: %v", err) }
+ for idx, msg := range resp { fmt.Printf("Message %d: %s: %s\n", idx, msg.Role, msg.Content) }
}
```
-## For More Details
+## **More Information**
+
- [Eino Documentation](https://github.com/cloudwego/eino)
-- [InvokableTool Interface Reference](https://pkg.go.dev/github.com/cloudwego/eino/components/tool)
-- [langchain_community Reference](https://python.langchain.com/docs/integrations/tools/requests/)
+- [InvokableTool Reference](https://pkg.go.dev/github.com/cloudwego/eino/components/tool)
+- [LangChain Requests Tool](https://python.langchain.com/docs/integrations/tools/requests/)
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_mcp.md b/content/en/docs/eino/ecosystem_integration/tool/tool_mcp.md
index 3d70d768dfb..aa1cfaa39b6 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_mcp.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_mcp.md
@@ -1,109 +1,103 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-11-20"
lastmod: ""
tags: []
title: Tool - MCP
weight: 0
---
-## Introduction
+## Overview
-Model Context Protocol(MCP) is a standardized open protocol for model access introduced by Anthropic. Eino provides adapters, which can directly access resources on existing MCP Servers.
+Model Context Protocol (MCP) standardizes model access to external resources. Eino provides wrappers so you can directly use resources exposed by an existing MCP Server.
-This section introduces the adapter of MCPTool, which implements the [Eino Tool](/docs/eino/core_modules/components/tools_node_guide).
+This section introduces the `MCPTool` wrapper, which implements Eino’s `InvokableTool` interface ([Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)).
-
+
-Other adapters:
+Also see: [ChatTemplate - MCP](/docs/eino/ecosystem_integration/chat_template/chat_template_mcp)
-[ChatTemplate - MCP](/docs/eino/ecosystem_integration/chat_template/chat_template_mcp)
+## Usage
-## HowToUse
+### QuickStart
-Eino MCP adapters referenced the open-source SDK [mcp-go](https://github.com/mark3labs/mcp-go), first initialize a MCP client:
+First create an MCP client. Eino leverages the open-source SDK `mark3labs/mcp-go`:
```go
import "github.com/mark3labs/mcp-go/client"
-// stdio client
-cli, err := client.NewStdioMCPClient(myCommand, myEnvs, myArgs...)
-
-// sse client
-cli, err := client.NewSSEMCPClient(myBaseURL)
-// sse client needs to manually start asynchronous communication
+// stdio
+cli, _ := client.NewStdioMCPClient(cmd, envs, args...)
+// sse
+cli, _ := client.NewSSEMCPClient(baseURL)
+// sse client needs to manually start asynchronous communication
// while stdio does not require it.
-err = cli.Start(ctx)
+_ = cli.Start(ctx)
```
-mcp-go also supports other methods for creating a Client (such as InProcess). For more information, please refer to: https://mcp-go.dev/transports
-Considering the reusability of multiple adapters for the MCP client, the adapter assumes that the client has completed initialization with the Server's [Initialize](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/), so users need to complete the client initialization themselves, for example:
+Considering client reuse, the wrapper assumes the client has finished `Initialize` with the Server; you need to perform client initialization yourself:
```go
import "github.com/mark3labs/mcp-go/mcp"
-initRequest := mcp.InitializeRequest{}
-initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
-initRequest.Params.ClientInfo = mcp.Implementation{
- Name: "example-client",
- Version: "1.0.0",
-}
-_, err = cli.Initialize(ctx, initRequest)
+init := mcp.InitializeRequest{}
+init.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
+init.Params.ClientInfo = mcp.Implementation{ Name: "example-client", Version: "1.0.0" }
+_, _ = cli.Initialize(ctx, init)
```
-Then use the Client to create the adapter , which implements Eino Tool:
+Then create Eino tools using the Client:
```go
import "github.com/cloudwego/eino-ext/components/tool/mcp"
-mcpTools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli})
+tools, _ := mcp.GetTools(ctx, &mcp.Config{ Cli: cli })
```
-You can call this adapter directly:
+Tools can be called directly:
```
-for i, mcpTool := range mcpTools {
+for i, mcpTool := range tools {
fmt.Println(i, ":")
info, err := mcpTool.Info(ctx)
- if err != nil {
- log.Fatal(err)
- }
+ if err != nil { log.Fatal(err) }
fmt.Println("Name:", info.Name)
fmt.Println("Desc:", info.Desc)
fmt.Println()
}
```
-It can also be used in any Eino Agent, taking the [ReAct Agent](/docs/eino/core_modules/flow_integration_components/react_agent_manual) as an example:
+You can also use tools within any Eino Agent; for example with [ReAct Agent](/docs/eino/core_modules/flow_integration_components/react_agent_manual):
```
import (
"github.com/cloudwego/eino/flow/agent/react"
- "github.com/cloudwego/eino-ext/components/tool/mcp"
+ "github.com/cloudwego/eino-ext/components/tool/mcp"
)
llm, err := /*create a chat model*/
tools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli})
agent, err := react.NewAgent(ctx, &react.AgentConfig{
- Model: llm,
- ToolsConfig: compose.ToolsNodeConfig{Tools: tools},
+ Model: llm,
+ ToolsConfig: compose.ToolsNodeConfig{Tools: tools},
})
```
-### SpecifyToolName
+### Specify Tool by Name
-You can use the field ToolNameList in config to specify the tools you want to call, avoiding calling unexpected tools:
+`GetTools` supports filtering by tool names to avoid unintended tools:
```go
-import "github.com/cloudwego/eino-ext/components/tool/mcp"
+tools, _ := mcp.GetTools(ctx, &mcp.Config{ Cli: cli, ToolNameList: []string{"name"} })
+```
-tools, err := mcp.GetTools(ctx, &mcp.Config{
- Cli: cli,
- ToolNameList: []string{"your tool name"},
-})
+Or use tools within any Eino Agent; for example with [ReAct Agent](/docs/eino/core_modules/flow_integration_components/react_agent_manual):
+
+```go
+agent, _ := react.NewAgent(ctx, &react.AgentConfig{ Model: llm, ToolsConfig: compose.ToolsNodeConfig{ Tools: tools } })
```
## More Information
-Examples can be referred to: [https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go](https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go)
+Practice example: https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md b/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
index 0caec2e9a89..49d0b99a6be 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
@@ -1,84 +1,59 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - sequentialthinking
weight: 0
---
-## Sequential Thinking Tool
+## **Sequential Thinking Tool**
-**Sequential Thinking Tool** is a tool for dynamic and reflective problem-solving through a structured thinking process.
-Inspired by [@modelcontextprotocol
-/sequentialthinking](https://github.com/modelcontextprotocol/servers/tree/HEAD/src/sequentialthinking), it guides LLM
-through a series of questions to help them think through problems step-by-step.
+Sequential Thinking Tool is for dynamic and reflective problem solving. Inspired by MCP’s `sequentialthinking`, it guides a large language model to think step by step via a series of questions.
-## Features
+## **Features**
-- Guided step-by-step thinking process.
-- Dynamic questioning and reflection.
-- Enhances problem-solving abilities.
+- Guided step-by-step thinking
+- Dynamic prompts and self-reflection
+- Improves problem-solving quality
-## Usage
+## **Use Cases**
-The Sequential Thinking tool is designed for:
+- Decompose complex problems into steps
+- Plan and iterate with room for adjustments
+- Analyze directions that may need correction
+- Handle ambiguous scope early on
+- Maintain context across multiple steps
+- Filter irrelevant information
-- Breaking down complex problems into steps
-- Planning and design with room for revision
-- Analysis that might need course correction
-- Problems where the full scope might not be clear initially
-- Tasks that need to maintain context over multiple steps
-- Situations where irrelevant information needs to be filtered out
-
-## Install
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/sequentialthinking@latest
```
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "fmt"
-
- "github.com/bytedance/sonic"
-
- "github.com/cloudwego/eino-ext/components/tool/sequentialthinking"
+ "context"
+ "fmt"
+
+ "github.com/bytedance/sonic"
+
+ "github.com/cloudwego/eino-ext/components/tool/sequentialthinking"
)
func main() {
- ctx := context.Background()
-
- // Instantiate the tool
- tool, err := sequentialthinking.NewTool()
- if err != nil {
- panic(err)
- }
-
- args := &sequentialthinking.ThoughtRequest{
- Thought: "This is a test thought",
- ThoughtNumber: 1,
- TotalThoughts: 3,
- NextThoughtNeeded: true,
- }
-
- argsStr, _ := sonic.Marshal(args)
-
- // Use the tool
- // (This is just a placeholder; actual usage will depend on the tool's functionality)
- result, err := tool.InvokableRun(ctx, string(argsStr))
- if err != nil {
- panic(err)
- }
-
- // Process the result
- // (This is just a placeholder; actual processing will depend on the tool's output)
- fmt.Println(result)
+ ctx := context.Background()
+ tool, err := sequentialthinking.NewTool()
+ if err != nil { panic(err) }
+ args := &sequentialthinking.ThoughtRequest{ Thought: "This is a test thought", ThoughtNumber: 1, TotalThoughts: 3, NextThoughtNeeded: true }
+ argsStr, _ := sonic.Marshal(args)
+ result, err := tool.InvokableRun(ctx, string(argsStr))
+ if err != nil { panic(err) }
+ fmt.Println(result)
}
-
-```
\ No newline at end of file
+```
diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md b/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
index b5d453e9513..7b1b3618319 100644
--- a/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
+++ b/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
@@ -1,136 +1,122 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - wikipedia
weight: 0
---
-## Wikipedia Search tool
+## **Overview**
-A Wikipedia search tool implementation for [Eino](https://github.com/cloudwego/eino) that implements the `InvokableTool` interface. This enables seamless integration with Eino's ChatModel interaction system and `ToolsNode` for enhanced search capabilities.
+This is a Wikipedia search tool for [Eino](https://github.com/cloudwego/eino) implementing the `InvokableTool` interface. It integrates with Eino’s ChatModel system and `ToolsNode`.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.InvokableTool`
-- Easy integration with Eino's tool system
+- Easy integration with Eino’s tool system
- Configurable search parameters
-## Installation
+## **Installation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/wikipedia
```
-## Quick Start
+## **Quick Start**
```go
package main
import (
- "context"
- "github.com/cloudwego/eino-ext/components/tool/wikipedia"
- "github.com/cloudwego/eino/components/tool"
- "log"
- "time"
+ "context"
+ "github.com/cloudwego/eino-ext/components/tool/wikipedia"
+ "github.com/cloudwego/eino/components/tool"
+ "log"
+ "time"
)
func main() {
- ctx := context.Background()
-
- // Create tool configuration
- // All of these parameters are default values, used here for demonstration purposes
- config := &wikipedia.Config{
- UserAgent: "eino (https://github.com/cloudwego/eino)",
- DocMaxChars: 2000,
- Timeout: 15 * time.Second,
- TopK: 3,
- MaxRedirect: 3,
- Language: "en",
- }
-
- // Create the search tool
- t, err := wikipedia.NewTool(ctx, config)
- if err != nil {
- log.Fatal("Failed to create tool:", err)
- }
-
- // Use with Eino's ToolsNode
- tools := []tool.BaseTool{t}
- // ... Configure and use ToolsNode
+ ctx := context.Background()
+
+ config := &wikipedia.Config{
+ UserAgent: "eino (https://github.com/cloudwego/eino)",
+ DocMaxChars: 2000,
+ Timeout: 15 * time.Second,
+ TopK: 3,
+ MaxRedirect: 3,
+ Language: "en",
+ }
+
+ t, err := wikipedia.NewTool(ctx, config)
+ if err != nil { log.Fatal("Failed to create tool:", err) }
+
+ tools := []tool.BaseTool{t}
+ // ... configure and use ToolsNode
}
```
-## Configuration
-
-The tool can be configured using the `Config` struct:
+## **Configuration**
```go
type Config struct {
- // BaseURL is the base url of the wikipedia api.
- // format: https://.wikipedia.org/w/api.php
- // The URL language depends on the settings you have set for the Language field
+ // BaseURL is the Wikipedia API base URL.
+ // Format: https://.wikipedia.org/w/api.php
+ // URL language depends on the Language field.
// Optional. Default: "https://en.wikipedia.org/w/api.php".
BaseURL string
-
- // UserAgent is the user agent to use for the http client.
- // Optional but HIGHLY RECOMMENDED to override the default with your project's info.
- // It is recommended to follow Wikipedia's robot specification:
+
+ // UserAgent used by the HTTP client.
+ // Optional, but recommended to override with project info.
+ // Follow Wikipedia bot policy:
// https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy
// Optional. Default: "eino (https://github.com/cloudwego/eino)"
UserAgent string `json:"user_agent"`
- // DocMaxChars is the maximum number of characters as extract for returning in the page content.
- // If the content is longer than this, it will be truncated.
- // Optional. Default: 15s.
+
+ // DocMaxChars caps returned page content length; longer content is truncated.
DocMaxChars int `json:"doc_max_chars"`
- // Timeout is the maximum time to wait for the http client to return a response.
- // Optional. Default: 15s.
+
+ // Timeout is the max wait time for HTTP response.
Timeout time.Duration `json:"timeout"`
- // TopK is the number of search results to return.
- // Optional. Default: 3.
+
+ // TopK number of search results to return.
TopK int `json:"top_k"`
- // MaxRedirect is the maximum number of redirects to follow.
- // Optional. Default: 3.
+
+ // MaxRedirect max allowed redirects.
MaxRedirect int `json:"max_redirect"`
- // Language is the language to use for the wikipedia search.
- // Optional. Default: "en".
+
+ // Language for Wikipedia search.
Language string `json:"language"`
- ToolName string `json:"tool_name"` // Optional. Default: "wikipedia_search".
- ToolDesc string `json:"tool_desc"` // Optional. Default: "this tool provides quick and efficient access to information from the Wikipedia"
+ // Optional. Default: "wikipedia_search".
+ ToolName string `json:"tool_name"`
+ // Optional. Default: "this tool provides quick and efficient access to information from the Wikipedia".
+ ToolDesc string `json:"tool_desc"`
}
```
-## Search
+### **Request Schema**
-### Request Schema
```go
type SearchRequest struct {
- // Query is the query to search the web for.
Query string `json:"query" jsonschema_description:"The query to search the web for"`
}
```
-### Response Schema
+### **Response Schema**
+
```go
type SearchResponse struct {
- // Results is the list of search results.
Results []*Result `json:"results" jsonschema_description:"The results of the search"`
}
-
type SearchResult struct {
- // Title is the title of the search result.
Title string `json:"title" jsonschema_description:"The title of the search result"`
- // URL is the URL of the search result.
URL string `json:"url" jsonschema_description:"The url of the search result"`
- // Extract is the summary of the search result.
Extract string `json:"extract" jsonschema_description:"The extract of the search result"`
- // Snippet is the snippet of the search result.
Snippet string `json:"snippet" jsonschema_description:"The snippet of the search result"`
}
```
-## For More Details
+## **More Details**
- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/en/docs/eino/overview/_index.md b/content/en/docs/eino/overview/_index.md
index 6c4ab6620bc..66b5a1064cc 100644
--- a/content/en/docs/eino/overview/_index.md
+++ b/content/en/docs/eino/overview/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: Overview'
@@ -9,64 +9,61 @@ weight: 1
## Introduction
-**Eino['aino]** (pronounced similarly to "I know, hoping that the framework can achieve the vision of "I know") aims to be the ultimate LLM application development framework in Go. Drawing inspiration from many excellent LLM application development frameworks in the open-source community such as LangChain & LlamaIndex, etc., as well as learning from cutting-edge research and real world applications, Eino offers an LLM application development framework that emphasizes simplicity, scalability, reliability and effectiveness that better aligns with Go programming conventions.
+**Eino ['aino]** (pronounced like “I know”, reflecting the vision we aim for) is a Golang-based framework for building modern applications powered by large language models (LLMs). Inspired by leading open-source frameworks such as LangChain and LlamaIndex, and informed by cutting-edge research and production practice, Eino emphasizes simplicity, extensibility, reliability, and effectiveness — all aligned with idiomatic Go design.
-What Eino provides are:
+Eino provides value through:
-- a carefully curated list of **component** abstractions and implementations that can be easily reused and combined to build LLM applications
-- a powerful **composition** framework that does the heavy lifting of strong type checking, stream processing, concurrency management, aspect injection, option assignment, etc. for the user.
-- a set of meticulously designed **API** that obsesses on simplicity and clarity.
-- an ever-growing collection of best practices in the form of bundled **flows** and **examples**.
-- a useful set of tools that covers the entire development cycle, from visualized development and debugging to online tracing and evaluation.
+- A carefully curated set of reusable **components** (abstractions and implementations) that you can compose to build LLM applications.
+- A powerful **orchestration** layer that takes on heavy lifting like type checking, streaming data handling, concurrency management, aspect injection, and option configuration.
+- A concise and thoughtfully designed **API**.
+- A growing collection of integrated **flows** and **examples** that capture best practices.
+- Practical **DevOps tools** supporting the full development lifecycle, from visual development and debugging to online tracing and evaluation.
-With the above capabilities and tools, Eino can standardize, simplify operations and improve efficiency at different stages of the artificial intelligence application development lifecycle:
+With these capabilities and tools, Eino standardizes, simplifies, and accelerates work across the AI application lifecycle:
-
+
-[Eino Github Repo](https://github.com/cloudwego/eino)
+[Eino on GitHub](https://github.com/cloudwego/eino)
-## **A quick walkthrough**
+## Quick Start
-Use a component directly:
+Use components directly:
```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?")}
+ UserMessage("what does the future AI App look like?")})
```
-Of course, you can do that. Eino provides lots of useful components to use out of the box. But you can do more by using orchestration, for three reasons:
+This works out of the box thanks to Eino’s ready-to-use components. However, you can achieve much more with orchestration — for three key reasons:
-- orchestration encapsulates common patterns of LLM application.
-- orchestration solves the difficult problem of processing stream response by the LLM.
-- orchestration handles type safety, concurrency management, aspect injection and option assignment for you.
+- Orchestration encapsulates common patterns in LLM applications.
+- Orchestration addresses the complexities of handling streaming responses from LLMs.
+- Orchestration takes care of type safety, concurrency management, aspect injection, and option assignment for you.
-Eino provides three sets of APIs for orchestration
+Eino offers three orchestration APIs:
-| API | Characteristics and usage |
-| Chain | Simple chained directed graph that can only go forward. |
-| Graph | Cyclic or Acyclic directed graph. Powerful and flexible. |
-| Workflow | Acyclic graph that supports data mapping at struct field level. |
+| API | Features & Use Cases |
+| Chain | A simple forward-only directed chain. |
+| Graph | A directed graph (cyclic or acyclic). Powerful and flexible. |
+| Workflow | An acyclic directed graph with field-level data mapping in structs. |
-Let's create a simple chain: a ChatTemplate followed by a ChatModel.
+Let’s build a simple chain: a **ChatTemplate** followed by a **ChatModel**.
-
+
```go
-chain, err := NewChain[map[string]any, *Message]().
+chain, _ := NewChain[map[string]any, *Message]().
AppendChatTemplate(prompt).
AppendChatModel(model).
Compile(ctx)
-if err != nil {
- return err
-}
-out, err := chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
+chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
```
-Now let's create a graph that uses a ChatModel to generate tool calls, then uses a ToolsNode to execute those tools, .
+Now, let’s create a **Graph** where a **ChatModel** either returns a result directly or makes at most one tool call.
@@ -88,40 +85,43 @@ compiledGraph, err := graph.Compile(ctx)
if err != nil {
return err
}
-out, err := r.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
+out, err := compiledGraph.Invoke(ctx, map[string]any{
+ "query":"Beijing's weather this weekend"})
```
-Now let's create a workflow that flexibly maps input & output at the field level:
+Next, let’s build a **Workflow** to flexibly map inputs and outputs at the field level:
```go
-wf := NewWorkflow[[]*Message, *Message]()
+wf := NewWorkflow[[]*schema.Message, *schema.Message]()
wf.AddChatModelNode("model", model).AddInput(START)
-wf.AddLambdaNode("l1", lambda1).AddInput(NewMapping("model").From("Content").To("Input"))
-wf.AddLambdaNode("l2", lambda2).AddInput(NewMapping("model").From("Role").To("Role"))
-wf.AddLambdaNode("l3", lambda3).AddInput(
- NewMapping("l1").From("Output").To("Query"),
- NewMapping("l2").From("Output").To("MetaData"),
-)
-wf.AddEnd([]*Mapping{NewMapping("node_l3")}
-runnable, _ := wf.Compile(ctx)
-runnable.Invoke(ctx, []*Message{UserMessage("kick start this workflow!")})
+wf.AddLambdaNode("lambda1", lambda1).AddInput("model", MapFields("Content", "Input"))
+wf.AddLambdaNode("lambda2", lambda2).AddInput("model", MapFields("Role", "Role"))
+wf.AddLambdaNode("lambda3", 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!")})
```
-Now let's create a 'ReAct' agent: A ChatModel binds to Tools. It receives input `Messages` and decides independently whether to call the `Tool` or output the final result. The execution result of the Tool will again become the input Message for the ChatModel and serve as the context for the next round of independent judgment.
+Now, let’s create a **ReAct** agent: a ChatModel bound to a set of Tools. It ingests an input message and autonomously decides whether to call a tool or to produce a final answer. Tool outputs are fed back into the chat model as input messages and used as context for subsequent reasoning.
-We provide a complete implementation of the ReAct agent out of the box in Eino's `flow` package. See the code at: [flow/agent/react](https://github.com/cloudwego/eino/blob/main/flow/agent/react/react.go)
+Eino provides a full, ready-to-use implementation of a ReAct agent in the `flow` package. See: [flow/agent/react](https://github.com/cloudwego/eino/blob/main/flow/agent/react/react.go)
-Eino automatically does important stuff behind the above code:
+Behind the scenes, Eino automatically handles several critical responsibilities:
-- **Type checking**: it makes sure the two nodes' input and output types match at compile time.
-- **Stream processing**: concatenates message stream before passing to chatModel and toolsNode if needed, and copies the stream into callback handlers.
-- **State management**: Ensure that shared state can be read and written safely.
-- **Aspect injection**: injects callback aspects before and after the execution of ChatModel if the specified ChatModel implementation hasn't injected itself.
-- **Option assignment**: call options are assigned either globally, to specific component type or to specific node.
+- **Type checking**: Ensures input/output types between nodes match at compile time.
+- **Stream handling**: Concatenates message streams before passing them to ChatModel or ToolsNode, and duplicates the stream for callback handlers when needed.
+- **State management**: Ensures shared state is safely readable and writable.
+- **Aspect injection**: Injects callbacks before/after ChatModel execution if the implementation does not manage its own callbacks.
+- **Option assignment**: Runtime options can be set globally, per component type, or per node.
-For example, you could easily extend the compiled graph with callbacks:
+For example, it’s easy to extend a compiled graph with callbacks:
```go
handler := NewHandlerBuilder().
@@ -138,7 +138,7 @@ handler := NewHandlerBuilder().
compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
```
-or you could easily assign options to different nodes:
+And similarly, you can assign options to different nodes with precision:
```go
// assign to All nodes
@@ -151,92 +151,101 @@ compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5))
compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
```
-## **Key Features**
+## Key Features
-### **Rich Components**
+### Rich Components
-- Encapsulates common building blocks into **component abstractions**, each having multiple **component implementations** that are ready to be used out of the box.
- - component abstractions such as ChatModel, Tool, PromptTemplate, Retriever, Document Loader, Lambda, etc.
- - Each component type has an interface of its own: defined Input & Output Type, defined Option type, and streaming paradigms that make sense.
- - implementations are transparent. Abstractions are all you care about when orchestrating components together.
-- Implementations can be nested and captures complex business logic.
- - React Agent, MultiQueryRetriever, Host MultiAgent, etc. They consist of multiple components and non-trivial business logic.
- - They are still transparent from the outside. A MultiQueryRetriever can be used anywhere that accepts a Retriever.
+- Common building blocks are abstracted as **components**, each with multiple ready-to-use **implementations**.
+ - Abstractions include ChatModel, Tool, PromptTemplate, Retriever, Document Loader, Lambda, and more.
+ - Each component defines its own interface, input/output types, option types, and sensible stream processing paradigms.
+ - Implementation details are transparent; when orchestrating components, you focus on abstractions.
+- Implementations can be nested and encapsulate complex business logic.
+ - Examples include ReAct Agent, MultiQueryRetriever, and Host MultiAgent. These are composed from multiple components with sophisticated logic.
+ - From the outside, details remain transparent. For instance, you can use MultiQueryRetriever anywhere a Retriever is accepted.
-### **Powerful Orchestration (Graph/Chain/Workflow)**
+### Powerful Orchestration (Graph/Chain/Workflow)
-- Data flows from Retriever / Document Loaders / Prompt Template to ChatModel, then flows to Tools and parsed as Final Answer. This directed, controlled flow of data through multiple components can be implemented through **graph orchestration**.
-- Component instances are graph nodes, and edges are data flow channels.
-- Graph orchestration is powerful and flexible enough to implement complex business logic:
- - Type checking, stream processing, concurrency management, aspect injection and option assignment are handled by the framework.
- - branch out execution at runtime, read and write global state, or do field level data mapping using workflow.
+- Data flows from Retriever / Document Loader / ChatTemplate to ChatModel, then to Tool, and ultimately becomes the final answer. This controllable, directed flow across components is realized through **graph orchestration**.
+- Component instances become **nodes**, and **edges** are the data channels between them.
+- Graph orchestration is powerful and flexible enough to encode complex business logic:
+ - The framework handles **type checking, stream management, concurrency, aspect injection, and option distribution**.
+ - At runtime, you can **branch**, read/write global **state**, or use workflows for field-level data mapping.
-### **Complete Stream Processing**
+### Robust Streaming
-- Stream processing is important because ChatModel outputs chunks of the complete message in real-time when generating messages. This is particularly crucial in orchestration scenarios as more components need to process the chunked message data.
-- For downstream nodes that only accept non-streaming input (such as the ToolsNode), Eino automatically **concatenates** the streams.
-- During the execution of the graph, when a stream is required, Eino automatically **converts** non-streaming data into a stream.
-- When multiple streams converge at a downstream node, Eino automatically **merges** these streams.
-- When a stream is sent to multiple different downstream nodes or passed to a callback handler, Eino automatically **copies** these streams.
-- Orchestration elements such as **branching** or the **StateHandler** can also detect and process streams.
-- Thanks to the above stream data processing capabilities, whether a component can handle streams and whether it will output streams becomes transparent to the user.
-- The compiled Graph can be run in four different stream input-output paradigms:
+- Stream processing is crucial because ChatModel emits fragments of the final message in real time. This matters even more under orchestration, where downstream components need to handle fragments.
+- For downstream nodes that only accept non-streaming input (e.g., ToolsNode), Eino automatically **concatenates** the stream.
+- During graph execution, Eino will automatically **convert** non-streaming to streaming when needed.
+- When multiple streams converge to a single downstream node, Eino **merges** them.
+- When a stream fans out to multiple downstream nodes or is passed to callback handlers, Eino **copies** it.
+- Orchestration elements such as **Branch** and **StateHandler** are stream-aware.
+- Compiled graphs can run in four input/output paradigms:
-| Streaming Paradigm | Explanation |
-| Invoke | Accepts non-stream type I and returns non-stream type O |
-| Stream | Accepts non-stream type I and returns stream type StreamReader[O] |
-| Collect | Accepts stream type StreamReader[I] and returns non-stream type O |
-| Transform | Accepts stream type StreamReader[I] and returns stream type StreamReader[O] |
+| Paradigm | Description |
+| Invoke | Accepts non-streaming I, returns non-streaming O |
+| Stream | Accepts non-streaming I, returns StreamReader[O] |
+| Collect | Accepts StreamReader[I], returns non-streaming O |
+| Transform | Accepts StreamReader[I], returns StreamReader[O] |
-### **Highly Extensible Aspects (Callbacks)**
+### Extensible Callbacks
-- Aspects handle cross-cutting concerns such as logging, tracing, metrics, etc., as well as exposing internal details of component implementations.
-- Five aspects are supported: **OnStart, OnEnd, OnError, OnStartWithStreamInput, OnEndWithStreamOutput**.
-- Developers can easily create custom callback handlers, add them during graph run via options, and they will be invoked during graph run.
-- Graphs can also inject aspects to those component implementations that do not support callbacks on their own.
+- Callbacks address cross-cutting concerns like logging, tracing, metrics, and they can expose internal details of component implementations.
+- Five callback types are supported: **OnStart, OnEnd, OnError, OnStartWithStreamInput, OnEndWithStreamOutput**.
+- Developers can create custom handlers and inject them at runtime via options; the graph will invoke them during execution.
+- Graphs can even inject callbacks into component implementations that do not natively support them.
-## Eino framework structure
+## Eino Architecture
-
+
The Eino framework consists of several parts:
-- [Eino](https://github.com/cloudwego/eino): It includes type definitions, streaming data processing mechanisms, component abstraction definitions, orchestration functions, aspect-oriented mechanisms, etc.
-- [EinoExt](https://github.com/cloudwego/eino-ext): Component implementations, callback handler implementations, component usage examples, and various tools such as evaluators, hint optimizers, etc.
-- [Eino Devops](https://github.com/cloudwego/eino-ext/tree/main/devops): Visual development, visual debugging, etc.
-- [EinoExamples](https://github.com/cloudwego/eino-examples): It is a code repository containing sample applications and best practices.
+- [Eino](https://github.com/cloudwego/eino): Type definitions, streaming mechanisms, component abstractions, orchestration, and callbacks.
+- [EinoExt](https://github.com/cloudwego/eino-ext): Component implementations, callback handlers, usage examples, and tooling such as evaluators and prompt optimizers.
+
+> 💡
+> For ByteDance-internal components, there is a corresponding internal repository:
+>
+> EinoBytedExt: [https://code.byted.org/search/flow/eino-byted-ext](https://code.byted.org/search/flow/eino-byted-ext)
+>
+> It includes components currently intended for internal use, such as llmgateway, bytedgpt, fornax tracing, bytees, etc.
-For details, see: [The structure of the Eino Framework](/docs/eino/overview/eino_framework_structure)
+- [Eino DevOps](https://github.com/cloudwego/eino-ext/tree/main/devops): Visual development and debugging.
+- [EinoExamples](https://github.com/cloudwego/eino-examples): Example applications and best practices.
-## Detailed Documentation
+See also: [Eino Architecture Overview](/docs/eino/overview/)
-For learning and using Eino, we provide a comprehensive Eino User Manual to help you quickly understand the concepts in Eino and master the skills of developing AI applications based on Eino. Start exploring through the [Eino User Manual](https://www.cloudwego.io/zh/docs/eino/) now!
+## Documentation
-For a quick introduction to building AI applications with Eino, we recommend starting with [Eino: Quick Start](https://www.cloudwego.io/zh/docs/eino/quick_start/)
+For learning and using Eino, we provide a comprehensive user manual to help you grasp Eino’s concepts and build AI applications with Eino effectively. Try it here: [Eino User Manual](https://www.cloudwego.io/docs/eino/)
-Full API Reference:[https://pkg.go.dev/github.com/cloudwego/eino](https://pkg.go.dev/github.com/cloudwego/eino)
+If you want to get hands-on quickly, start with: [Eino: Quick Start](https://www.cloudwego.io/docs/eino/quick_start/)
+
+Complete API reference: [https://pkg.go.dev/github.com/cloudwego/eino](https://pkg.go.dev/github.com/cloudwego/eino)
## Dependencies
-- Go 1.18 and above.
-- Eino relies on [kin-openapi](https://github.com/getkin/kin-openapi) 's OpenAPI JSONSchema implementation. In order to remain compatible with Go 1.18, we have fixed kin-openapi's version to be v0.118.0.
+- Go 1.18 or higher
+- Eino depends on [kin-openapi](https://github.com/getkin/kin-openapi) for OpenAPI JSONSchema. To remain compatible with Go 1.18, we pin kin-openapi at `v0.118.0`.
## Security
-If you discover a potential security issue in this project, or think you may have discovered a security issue, we ask that you notify Bytedance Security via our [security center](https://security.bytedance.com/src) or [vulnerability reporting email](mailto:sec@bytedance.com).
+If you discover or suspect a potential security issue in this project, please report it to ByteDance’s security team via our [Security Center](https://security.bytedance.com/src) or by email at [sec@bytedance.com](mailto:sec@bytedance.com).
-Please do **not** create a public GitHub issue.
+Please **do not** create a public GitHub issue.
-## Contact US
+## Contact
- How to become a member: [COMMUNITY MEMBERSHIP](https://github.com/cloudwego/community/blob/main/COMMUNITY_MEMBERSHIP.md)
- Issues: [Issues](https://github.com/cloudwego/eino/issues)
-- Lark: Scan the QR code below with [Register](https://www.feishu.cn/en/) Lark to join our CloudWeGo/eino user group.
+- Lark user group ([register for Lark](https://www.feishu.cn/) and scan the QR code to join)
+- ByteDance internal OnCall group
+
## License
This project is licensed under the [[Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0.txt)].
diff --git a/content/en/docs/eino/overview/bytedance_eino_practice.md b/content/en/docs/eino/overview/bytedance_eino_practice.md
index ed114721499..0e83c5fc21e 100644
--- a/content/en/docs/eino/overview/bytedance_eino_practice.md
+++ b/content/en/docs/eino/overview/bytedance_eino_practice.md
@@ -1,78 +1,75 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: Bytedance LLM Application Go Development Framework-Eino Practice
-weight: 0
+title: ByteDance LLM Application Go Framework — Eino in Practice
+weight: 2
---
-## **Preface**
+## Preface
-Developing software applications based on LLMs is like coaching a soccer team: **components** are players with various abilities, **orchestration** is the flexible and versatile tactics, and **data** is the flowing soccer ball. Eino is an open-source LLM application development framework by ByteDance. It features a stable core, flexible extensibility, comprehensive tool ecosystem, and is reliable and easy to maintain, backed by rich practical experience from applications like Doubao and TikTok. Using Eino for the first time is like taking over a powerful soccer team. Even if the coach is a promising newcomer, they can still lead high-quality and content-rich matches.
+Building LLM-powered applications is like coaching a football team: **components** are players, **orchestration** is the strategy, and **data** is the ball flowing through the team. Eino is ByteDance’s open-source framework for LLM application development — stable core, flexible extensions, full tooling, and battle-tested in real apps like Doubao and TikTok. Picking up Eino feels like inheriting a strong team: even a new coach can lead a meaningful game quickly.
-So, let's embark on this journey together!
+Let’s kick off the ramp-up journey.
-## **Meet the Players**
+## Meet the Players
-The basic building blocks of Eino applications are components with various functionalities, just like a soccer team consists of players in different positions:
+Eino applications are built from versatile components:
-| Component name | Component function |
-| ChatModel | Interacts with the large model, inputs the Message context, and obtains the model's output Message |
-| Tool | Interacts with the world, and executes the corresponding action based on the model's output |
-| Retriever | Obtains relevant context to enable the model's output to be based on high-quality facts |
-| ChatTemplate | Receives external input and converts it into a preset format prompt for the model |
-| Document Loader | Loads the specified text |
-| DocumentTransformer | Transforms the specified text according to specific rules |
-| Indexer | Stores files and builds an index for subsequent use by the Retriever |
-| Embedding | A common dependency of the Retriever and Indexer, converting text into vectors to capture text semantics |
-| Lambda | User-defined function |
+| Component | Purpose |
+| ChatModel | Interact with LLM: input `Message[]`, output `Message` |
+| Tool | Interact with the world: execute actions based on model output |
+| Retriever | Fetch context so answers are grounded |
+| ChatTemplate | Convert external input into prompt messages |
+| Document Loader | Load text |
+| Document Transformer | Transform text |
+| Indexer | Store and index documents for retrieval |
+| Embedding | Shared dependency of Retriever/Indexer: text → vector |
+| Lambda | Custom function |
-These components abstractly represent fixed input/output types, Option types, and method signatures:
+Each abstraction defines input/output types, options, and method signatures:
```go
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)
+ Stream(ctx context.Context, input []*schema.Message, opts ...Option) (*schema.StreamReader[*schema.Message], error)
BindTools(tools []*schema.ToolInfo) error
}
```
-Actual operation requires specific component **implementations**:
+Implementations make them concrete:
-| Component name | Official component implementation |
+| Component | Official implementations |
| ChatModel | OpenAI, Claude, Gemini, Ark, Ollama... |
-| Tool | Google Search, Duck Duck Go... |
-| Retriever | Elastic Search, Volc VikingDB... |
+| Tool | Google Search, DuckDuckGo... |
+| Retriever | ElasticSearch, Volc VikingDB... |
| ChatTemplate | DefaultChatTemplate... |
| Document Loader | WebURL, Amazon S3, File... |
| Document Transformer | HTMLSplitter, ScoreReranker... |
-| Indexer | Elastic Search, Volc VikingDB... |
+| Indexer | ElasticSearch, Volc VikingDB... |
| Embedding | OpenAI, Ark... |
| Lambda | JSONMessageParser... |
-In the process of developing with Eino, the first thing to do is to decide "which component abstraction do I need to use," and then decide "which specific component implementation do I need to use." It's like a soccer team first decides "I need a forward," and then selects "who will be the forward."
+Decide the abstraction first (“we need a forward”), then pick an implementation (“who plays forward”). Components can be used standalone, but Eino shines when components are orchestrated.
-Components can be used individually like any Go interface. However, to truly unleash the power of the Eino team, multiple components need to be orchestrated together, forming an interconnected whole.
+## Plan the Strategy
-## Devising Tactics
-
-In the Eino orchestration scenario, each component becomes a "Node", the one-to-one flow relationship between nodes becomes an "Edge", and the N-to-1 flow relationship becomes a "Branch". Applications developed based on Eino, through the flexible orchestration of various components, can support infinitely rich business scenarios, much like a football team can adopt various formations.
-
-Football team tactics are ever-changing but follow certain patterns; some focus on ball control, while others are straightforward. For Eino, there are also more suitable orchestration methods for different business forms:
+In Eino, each component becomes a **Node**; 1→1 connections are **Edges**; N→1 conditional flow is a **Branch**. Orchestration supports rich business logic:
-| Arrangement Method | Characteristics and Scenarios |
-| Chain | A chained directed graph that always moves forward, simple. Suitable for scenarios where data flows in one direction without complex branches. |
-| Graph | A directed graph with maximum flexibility; or a directed acyclic graph that does not support branches but has a clear ancestral relationship. |
+| Style | Use cases |
+| Chain | Simple forward-only DAG; ideal for linear flows |
+| Graph | Directed (cyclic or acyclic) graphs for maximal flexibility |
-Chain, such as the simple Chain of ChatTemplate + ChatModel:
+Chain: simple `ChatTemplate → ChatModel` chain.
+
+
```go
chain, _ := NewChain[map[string]any, *Message]().
@@ -82,200 +79,144 @@ chain, _ := NewChain[map[string]any, *Message]().
chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
```
-Graph, where an Agent executes ToolCall at most once:
+Graph: an agent that makes at most one tool call.
-
+
```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 := r.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
+out, err := compiledGraph.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
```
-## **Understanding Tools**
+## Operational Tools
-Now, imagine the soccer team you are managing uses some high-tech solutions. For instance, at the moment of receiving and passing the ball, each player's jersey can automatically record the speed and angle of the ball, and transmit this information to the server on the sidelines. This way, after the match, you can compile statistics on each player's ball-handling and decision-making time.
-
-In Eino, the start and end of each component's operation can also use the Callbacks mechanism to capture inputs, outputs, and some additional information, handling cross-cutting concerns. For example, a simple logging capability:
+Callbacks capture node start/end (and stream variants) for cross-cutting concerns like logging, tracing, metrics:
```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()
-
-// inject into graph
+ 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()
compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
```
-Now imagine this soccer team has more than one high-tech feature. For instance, the coach can create "strategy pouches" before the match and hide them in the jerseys. When a player receives the ball, the pouch plays a pre-recorded tip from the coach, such as "Don't hesitate, shoot directly!". Sounds interesting, but there's a challenge: some pouches are meant for all players, some are meant for a specific group of players (such as forwards), and some pouches are meant for individual players. How can the distribution of these strategy pouches be effectively managed?
-
-In Eino, a similar issue is the distribution of call options during the graph run:
+Call options target all nodes, nodes of a specific type, or a specific node:
```go
-// Call option effective for all nodes
compiledGraph.Invoke(ctx, input, WithCallbacks(handler))
-
-// Call option effective only for specific types of nodes
compiledGraph.Invoke(ctx, input, WithChatModelOption(model.WithTemperature(0.5)))
-
-// Call option effective only for specific nodes
compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
```
-## **Discovering the Secret Trick**
-
-Now, imagine that your team has some star players (the midfield brain ChatModel and the forward ace StreamableTool) with incredible skills. The balls they kick move so fast that there are afterimages, making it look like a whole soccer ball has been sliced into many pieces! Facing such "streamed" soccer balls, the opponent players are at a loss on how to catch them, but all the players on your team can perfectly catch the ball—either catching each piece of the "streamed" soccer ball directly and processing it instantly, or automatically assembling all the pieces into a complete soccer ball before handling it. With such a secret trick, your team can overwhelm other teams!
+## Streaming Mastery
-In Eino, developers only need to focus on whether a component can handle streamed input and generate streamed output in "real business scenarios." Based on the real scenario, the specific component implementation (including Lambda Functions) should implement methods that conform to this streaming paradigm:
+Some components output streams (fragments of a final value), others consume streams. Components implement the paradigms that match real business semantics:
```go
-// ChatModel implements the Invoke (non-stream input and output) and Stream (non-stream input, stream output) paradigms
+// ChatModel supports Invoke and 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)
+ Stream(ctx context.Context, input []*Message, opts ...Option) (*schema.StreamReader[*Message], error)
}
-// Lambda can implement any of the four streaming paradigms
-
-// 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)
+// Lambda can implement any of the four
+type Invoke[I, O, TOption any] func(ctx context.Context, input I, opts ...TOption) (O, error)
+type Stream[I, O, TOption any] func(ctx context.Context, input I, opts ...TOption) (*schema.StreamReader[O], error)
+type Collect[I, O, TOption any] func(ctx context.Context, input *schema.StreamReader[I], opts ...TOption) (O, error)
+type Transform[I, O, TOption any] func(ctx context.Context, input *schema.StreamReader[I], opts ...TOption) (*schema.StreamReader[O], error)
```
-Eino's orchestration capability automatically performs two important tasks:
-
-1. When the upstream is a stream, but the downstream can only accept non-stream, it automatically concatenates.
-2. When the upstream is non-stream, but the downstream can only accept a stream, it automatically streams (T -> StreamReader[T]).
-
-In addition, Eino's orchestration capability also automatically handles stream merging, replication, and other details, bringing the core of LLM applications—stream processing—to perfection.
-
-## A Training Match -- Eino Smart Assistant
-
-Alright, now that you have a preliminary understanding of the main capabilities of Eino, it's time to have a training match using components, orchestration, and tools (aspects, visualization) to personally experience its power.
+Orchestration automatically handles:
-### **Scenario Setup**
+1) Concat streams when downstream only accepts non-streaming.
+2) Box non-streaming values into single-frame streams when downstream expects streaming.
+3) Merge/copy streams as needed.
-Eino Smart Assistant: According to user requests, retrieve necessary information from the knowledge base and invoke various tools as needed to process the user's request. The list of tools is as follows:
+## A Scrimmage — Eino Assistant
-- DuckDuckGo: Search the internet for information using DuckDuckGo
-- EinoTool: Obtain engineering information about Eino, such as repository links, document links, etc.
-- GitClone: Clone the specified repository locally
-- TaskManager: Add, view, remove tasks
-- OpenURL: Open files, web, and other types of links using the system's default application
+Goal: retrieve from a knowledge base and use tools as needed. Tools:
-This article primarily presents a demo sample; users can replace their knowledge base and tools according to their scenarios to build the smart assistant they need.
+- DuckDuckGo search
+- EinoTool (repo/docs metadata)
+- GitClone (clone repo)
+- TaskManager (add/view/delete tasks)
+- OpenURL (open links/files)
-First, let's see what kind of effects can be achieved with an Agent assistant built on Eino.
+
-
+Two parts:
-We will construct this Eino Smart Assistant in two steps:
+- Knowledge Indexing: split/encode Markdown docs into vectors
+- Eino Agent: decide actions, call tools, iterate until the goal is met
-- Knowledge Indexing: Using various methods such as tokenization and vectorization, we construct an index from our accumulated knowledge in a specific field, so that when receiving user requests, we can index the appropriate context. This article uses vectorized indexing to build the knowledge base.
-- Eino Agent: Based on the user's request information and the pre-constructed callable tools, let the ChatModel decide the next action to be performed or output the final result. The execution results of the Tool will be inputted to the ChatModel again, allowing the ChatModel to judge the next action until the user's request is completed.
+### Workflow
-### **Task Workflow**
+#### Knowledge Indexing
-#### **Knowledge Indexing**
+
-Split and vectorize the Eino User Guide in Markdown format using appropriate strategies and store it in RedisSearch's VectorStore as the Eino knowledge base.
+#### Eino Agent
-
-
-#### **Eino Agent**
-
-Retrieve information from the Eino knowledge base in response to user requests, construct messages using the ChatTemplate, request the React Agent, and loop through the corresponding tools as needed until the user's request is processed.
-
-
+
### Required Tools
-In the practice scenario of building the "Eino Smart Assistant" from scratch, the following tools are needed:
-
-### **Index Knowledge Base**
+### Indexing the Knowledge Base
-> Example repository path: [https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant](https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant)
+> Sample repository path: https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant
>
-> In the following text, relative paths to this directory are used to identify resource locations.
+> Below, paths are relative to this directory
-Build a command-line tool that recursively traverses all Markdown files in a specified directory. Split the content of the Markdown files into different segments according to the headings, and vectorize each text segment using Volcengine's Doubao embedding model, storing them in Redis VectorStore.
+Build a CLI that recursively traverses Markdown files, splits them by headings, vectorizes each chunk with Doubao embedding, and stores vectors in Redis VectorStore.
-> Command-line tool directory: cmd/knowledge_indexing
+> Command-line tool directory: `cmd/knowledgeindexing`
>
-> Markdown files directory: cmd/knowledge_indexing/eino-dcos
-
-When developing the "Index Knowledge Base" application, first use the Goland EinoDev plugin provided by the Eino framework to visually drag and arrange the core application logic of KnowledgeIndexing, generating code into the eino_graph/knowledge_indexing directory.
-
-After the code is generated, first manually complete the construction methods of each component in this directory, and then call the BuildKnowledgeIndexing method in the business scenario to build and use the Eino Graph instance.
+> Markdown docs directory: `cmd/knowledgeindexing/eino-docs`
-Next, the development process of KnowledgeIndexing will be introduced step by step.
+Use EinoDev (GoLand/VS Code) to visually compose the core workflow, then generate code into `eino_assistant/eino/knowledgeindexing`. After generation, complete the constructors for each component and call `BuildKnowledgeIndexing` in your business code to build and use the Eino Graph instance.
-#### LLM Resource Creation
+#### Model Resources
-Volcengine is ByteDance's cloud service platform, from which you can register and call Doubao LLMs (there is a large amount of free quota).
+Volcengine Ark hosts Doubao models. Register resources (ample free quota available):
-- Create doubao-embedding-large as the vectorization model for building the knowledge base, and create doubao-pro-4k resources as the model when the agent is in conversation.
-- "Volcengine Online Inference": [https://console.volcengine.com/ark](https://console.volcengine.com/ark)
+- Create `doubao-embedding-large` for indexing
+- Create `doubao-pro-4k` for chat/agent reasoning
+- Console: https://console.volcengine.com/ark
-#### **Launch Redis Stack**
+#### Start Redis Stack
-This article will use Redis as the Vector Database. For users' convenience in building the environment, we provide Docker's quick command.
+Use Redis Stack as the vector database. The sample provides a quick Docker setup:
-- Provide docker-compose.yml in eino-examples/quickstart/eino_assistant
-- Provide Redis's initial knowledge base in the eino-examples/quickstart/eino_assistant/data directory
+- `eino-examples/quickstart/eino_assistant/docker-compose.yml`
+- Initial Redis data under `eino-examples/quickstart/eino_assistant/data`
-Start directly with the official redis stack image
+Start with the official Redis Stack image:
```bash
-# go to eino_assistant dir
+# Switch to eino_assistant
cd xxx/eino-examples/quickstart/eino_assistant
docker-compose up -d
@@ -283,38 +224,33 @@ docker-compose up -d
-- After starting, open the local port 8001 to access the Redis Stack web interface
+- After startup, open `http://127.0.0.1:8001` for the Redis Stack web UI
-> Open the link in your browser: [http://127.0.0.1:8001](http://127.0.0.1:8001)
+#### Visual Orchestration
-#### **Visual Development**
+> Visual orchestration lowers the learning curve and speeds up development. Experienced users can skip and build directly with Eino APIs.
-> "Eino Visual Development" aims to reduce the learning curve for Eino AI application development and improve development efficiency. Developers familiar with Eino can choose to skip the "Eino Visual Development" phase and directly engage in full-code development based on Eino's API.
+1) Install EinoDev and open the Eino Workflow panel
+ - Installation: `/docs/eino/core_modules/devops/ide_plugin_guide`
-1. Install the EinoDev plugin and open the Eino Workflow feature
-
- - 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
- - Leave other items empty
+ - Graph name: `KnowledgeIndexing`
+ - Node trigger mode: triggered after all predecessor nodes are executed
+ - Input type: `document.Source`
+ - Input type import path: `github.com/cloudwego/eino/components/document`
+ - Output type: `[]string`
+ - Others: empty
-2. Following the process described in "**Index Knowledge Base**", select the component library you need from Eino Workflow. The components required in this document are as follows:
- - document/loader/file
- - Load files from the specified URI, parse them into text content, and return them as a list of schema.Document.
- - document/transformer/splitter/markdown
- - Further split the text content loaded from FileLoader into suitable sizes to balance the constraints of vectorization calculation/storage and recall effectiveness.
- - indexer/redis
- - Store the original text and index fields of schema.Document in the Redis Vector Database
- - embedding/ark
- - Use Ark platform's vectorization model to perform vectorization calculation on Content and other contents in schema.Document
-3. Arrange the selected components according to the expected topology. After the arrangement is completed, click "Generate Code" to the specified directory.
+2) Select the needed components per the indexing flow:
+ - `document/loader/file`: load files from URI into `schema.Document[]`
+ - `document/transformer/splitter/markdown`: split content into suitable chunk sizes
+ - `indexer/redis`: store raw text and index fields in Redis Vector DB
+ - `embedding/ark`: compute embeddings via Ark
- - The code for "**Index Knowledge Base**" is generated to: eino_assistant/eino/knowledgeindexing
- - This example can directly copy the Graph Schema from eino/knowledge_indexing.json to quickly build the graph in the example
+3) Compose the topology and click “Generate Code” to a target directory
+ - Generate to: `eino_assistant/eino/knowledgeindexing`
+ - You can copy the graph schema from `eino/knowledge_indexing.json`
|
@@ -322,7 +258,8 @@ docker-compose up -d
|
|
-4. Complete the constructors of each component as needed, filling in the configuration content required when creating the component instance
+
+4) Complete each component’s constructor with the required configuration
|
@@ -330,159 +267,144 @@ docker-compose up -d
|
|
-5. After supplementing the configuration content of the components, you can call the BuildKnowledgeIndexing method to use it in the business scenario
-#### **Improve Code**
+5) Call `BuildKnowledgeIndexing` from your business code
-- The Eino orchestration code generated through visual development cannot be guaranteed to be directly usable; manual reading and checking of the code's integrity are required.
-- The core function generated is BuildKnowledgeIndexing(). Users can call this method where needed, create an instance, and use it.
+#### Polish the Code
-In the "Knowledge Base Indexing" scenario, BuildKnowledgeIndexing needs to be wrapped into a command, read model configuration and other information from environment variables, initialize the configuration content of BuildKnowledgeIndexing, scan the specified directory for Markdown files, and execute the indexing and storage of Markdown files.
+Generated code may need manual review. The core function is `BuildKnowledgeIndexing()`. Wrap it in a CLI that reads model configuration from environment variables, initializes the graph config, scans the Markdown directory, and performs indexing.
-> For detailed code, see: cmd/knowledgeindexing/main.go
+> See: `cmd/knowledgeindexing/main.go`
-#### **Run**
+#### Run
-> PS: In the example project, part of the eino document has been vectorized into Redis
+> The sample already ships with part of the Eino docs pre-indexed in Redis.
-1. In the .env file, obtain and fill in the values of ARK_EMBEDDING_MODEL and ARK_API_KEY according to the annotation instructions, and run the KnowledgeIndexing command with the following instructions
+1) Populate `.env` with `ARK_EMBEDDING_MODEL` and `ARK_API_KEY`, then:
- ```bash
- cd xxx/eino-examples/quickstart/eino_assistant # 进入 eino assistant 的 example 中
+```bash
+cd xxx/eino-examples/quickstart/eino_assistant
- # Modify the required environment variables in .env (LLM information, trace platform information)
- source .env
+# Load env vars (model info, tracing platform info)
+source .env
- # As the example Markdown file is stored in the cmd/knowledgeindexing/eino-docs directory, and the code specifies the relative path eino-docs, you need to run the command in the cmd/knowledgeindexing directory
- cd cmd/knowledgeindexing
- go run main.go
- ```
+# The sample Markdown lives in cmd/knowledgeindexing/eino-docs.
+# Because the code uses the relative path "eino-docs", run from cmd/knowledgeindexing
+cd cmd/knowledgeindexing
+go run main.go
+```
+
+
-
-2. Once the execution is successful, the construction of the Eino knowledge base is completed, and you can see the vectorized content in the Redis Web UI.
+2) After success, open the Redis web UI to inspect the indexed vectors:
-> Open the link in the browser: [http://127.0.0.1:8001](http://127.0.0.1:8001)
+> `http://127.0.0.1:8001`
### Eino Agent
-> Example repository path:[https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant](https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant)
->
-> Below, relative paths relative to this directory are used to identify resource locations
+> Sample repository path: https://github.com/cloudwego/eino-examples/tree/main/quickstart/eino_assistant
-Construct a ReAct Agent based on Eino that retrieves knowledge from Redis VectorStore to answer user questions and assist users in executing certain operations, i.e., a typical RAG ReAct Agent. It can automatically help users record tasks, clone repositories, open links, etc., based on the dialogue context.
+Build a typical RAG ReAct agent that retrieves context from Redis VectorStore, answers user questions, and executes actions such as task management, repo cloning, and opening links.
-#### LLM Resource Creation
+#### Model Resources
-Continue using doubao-embedding-large and doubao-pro-4k created in the "Index Knowledge Base" section
+Reuse `doubao-embedding-large` and `doubao-pro-4k` created earlier.
#### Start RedisSearch
-Continue using the Redis Stack started in the "Index Knowledge Base" section
-
-#### Visual Development
-
-
-1. Open the EinoDev plugin, navigate to the Eino Workflow page, and create a new canvas.
- - Graph Name: EinoAgent
- - Node Trigger Mode: Trigger after any predecessor node finishes
- - Input Type Name: *UserMessage
- - Input Package Path: ""
- - Output Type Name: *schema.Message
- - Output Import Path: github.com/cloudwego/eino/schema
- - Leave other fields blank
-2. According to the process description in the previous section "**Eino Agent**", select the component library needed from Eino Workflow. The following components are used in this document:
- - lambda: Converts any developer function func(ctx context.Context, input I) (output O, err error), into an orchestrable node. In EinoAgent, there are two conversion scenarios:
- - Converts *UserMessage messages into a map[string]any node for ChatTemplate
- - Converts *UserMessage into the input query for RedisRetriever
- - retriever/redis
- - Retrieves and returns context related to the user Query based on semantic relevance from the Redis Vector Database, in the form of schema.Document List.
- - prompt/chatTemplate
- - Constructs a Prompt template using string literals, supporting text and message placeholders, and converts any map[string]any input into a Message List that can be directly input into the model.
- - flow/agent/react
- - Based on the ChatModel provided by the developer and callable toolsets, automatically decides the next Action in response to user queries until a final answer can be generated.
- - model/ark
- - The Ark platform provides LLMs capable of completing conversational texts, such as the Doubao model. This acts as a dependency injection for the ReAct Agent.
- - Callable tool list
- - Internet search tool (DuckDuckGo), EinoTool, GitClone, TaskManager, OpenURL
-3. Arrange the selected components according to the desired topology. After arranging, click "Generate Code" to the specified directory.
- - In this example, the code for the "**Eino Agent**" is generated to: eino/einoagent
- - This example can quickly be constructed by copying the Graph Schema from eino/eino_agent.json
-
-
-
- |
-
- |
-
-4. Complete the construction functions for each component as needed, adding the required configuration content when creating component instances in the construction functions.
+Reuse the Redis Stack from the indexing section.
+
+#### Visual Orchestration
+
+
+
+1) In EinoDev → Eino Workflow, create a canvas:
+ - Graph name: `EinoAgent`
+ - Node trigger mode: trigger when any predecessor finishes
+ - Input type name: `*UserMessage`
+ - Input package path: ""
+ - Output type name: `*schema.Message`
+ - Output import path: `github.com/cloudwego/eino/schema`
+ - Others: empty
+
+2) Choose components:
+ - `lambda`: wrap `func(ctx context.Context, input I) (O, error)` as nodes
+ - Convert `*UserMessage` → `map[string]any` for ChatTemplate
+ - Convert `*UserMessage` → query string for RedisRetriever
+ - `retriever/redis`: retrieve `schema.Document[]` by semantic similarity
+ - `prompt/chatTemplate`: construct prompts from string templates with substitutions
+ - `flow/agent/react`: decide next actions with tools and ChatModel
+ - `model/ark`: Doubao chat model for ReAct reasoning
+ - Tools: DuckDuckGo, EinoTool, GitClone, TaskManager, OpenURL
+
+3) Generate code to `eino/einoagent`; you can copy from `eino/eino_agent.json`
-5. Once the configuration content for the components is supplemented, you can call the BuildEinoAgent method to use it in the business scenario.
-
-#### **Improve the Code**
-In the context of "Eino Agent," the Graph instance constructed by BuildEinoAgent can: recall context from the Eino knowledge base based on user requests and conversation history, and then combine a list of callable tools, with the ChatModel cyclically deciding the next step, whether to call a tool or output the final result.
+4) Complete constructors with required configuration
-The following diagram is an application of the generated BuildEinoAgent function, encapsulating the Eino Agent as an HTTP service interface:
-
-
+
+
+
+ |
+
+ |
-#### **Running**
+5) Call `BuildEinoAgent` from your business code
-1. In the .env file, obtain and fill in the values for each variable according to the comments. Then, start the Eino Agent Server with the following command:
+#### Polish the Code
- ```bash
- cd eino-examples/eino_assistant # Enter the example of Eino assistant
- # Modify the required environment variables in .env (LLM information, trace platform information)
- source .env
+`BuildEinoAgent` constructs a graph that, given user input and history, retrieves context from the knowledge base and iteratively decides whether to call a tool or produce the final answer.
- # To use the data directory, execute the command in the eino_assistant directory
- go run cmd/einoagent/*.go
+Wrap the agent as an HTTP service:
+
-
+#### Run
- 6. After starting, you can access the following link to open Eino Agent Web
+```bash
+cd eino-examples/eino_assistant
- Eino Agent Web: http://127.0.0.1:8080/agent/
- ```
+# Load env vars (model info, tracing platform info)
+source .env
-
-2. After starting, you can access the following link to open Eino Agent Web
+# Run from eino_assistant to use the embedded data directory
+go run cmd/einoagent/*.go
+```
-> Eino Agent Web:[http://127.0.0.1:8080/agent/](http://127.0.0.1:8080/agent/)
+
-#### **Observation (optional)**
+Open the web UI:
-##### APMPlus
+> `http://127.0.0.1:8080/agent/`
-If you specify `APMPLUS_APP_KEY` in the .env file during runtime,you can log in to the corresponding account on the [Volcengine APMPlus](https://console.volcengine.com/apmplus-server) platform to view the trace and metric details of the requests.
+#### Observability (Optional)
-
+- APMPlus: set `APMPLUS_APP_KEY` in `.env` and view traces/metrics at https://console.volcengine.com/apmplus-server
-##### Langfuse
+
-If you specify `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in the .env file during runtime, you can log in to the corresponding account on the Langfuse platform to view the trace details of the requests.
+- Langfuse: set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` to inspect trace details
-
+
-## **Relevant Links**
+## Related Links
-Project address:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino),[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+Project: https://github.com/cloudwego/eino, https://github.com/cloudwego/eino-ext
-Eino User Manual:[https://www.cloudwego.io/zh/docs/eino/](https://www.cloudwego.io/zh/docs/eino/)
+Eino User Manual: https://www.cloudwego.io/docs/eino/
-Project website: __[https://www.cloudwego.io](https://www.cloudwego.io)__
+Website: __https://www.cloudwego.io__
-Scan the QR code to join the Feishu community:
+Join the developer community:
-
+
diff --git a/content/en/docs/eino/overview/eino_adk0_1.md b/content/en/docs/eino/overview/eino_adk0_1.md
new file mode 100644
index 00000000000..2c8ddb19634
--- /dev/null
+++ b/content/en/docs/eino/overview/eino_adk0_1.md
@@ -0,0 +1,467 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino ADK: Master Core Agent Patterns and Build a Production-Grade Agent System'
+weight: 3
+---
+
+# Introduction
+
+As LLMs break the barrier of “understanding and generation”, agents rapidly become the mainstream form of AI applications. From smart customer service to automated office flows, agents bridge LLM capabilities with concrete actions.
+
+But pain points emerge: many teams struggle to connect LLMs with systems; agents lack state management and “forget”, while complex flows raise development complexity.
+
+**Eino ADK (Agent Development Kit)** provides Go developers with a complete, flexible, and powerful framework for agent development — addressing core challenges head-on.
+
+## What is an Agent?
+
+Agents are autonomous, executable intelligent units that can learn, adapt, and make decisions. Core capabilities include:
+
+- Reasoning: analyze data, identify patterns, use logic and available information to conclude, infer, and solve problems
+- Action: take actions or execute tasks based on decisions, plans, or external inputs to achieve goals
+- Observation: autonomously collect relevant information (e.g., computer vision, NLP, sensor data) to understand context for informed decisions
+- Planning: determine necessary steps, evaluate potential actions, and choose the best plan based on information and expected outcomes
+- Collaboration: collaborate effectively with humans or other AI agents in complex, dynamic environments
+
+Any LLM-interaction scenario can be abstracted as an agent, for example:
+
+- A weather query agent
+- A meeting scheduler agent
+- A domain-specific QA agent
+
+## What is Eino ADK?
+
+[Eino ADK](https://github.com/cloudwego/eino) is a Go-first framework for agents and multi-agent systems, aligned with the conceptualization in [Google ADK](https://google.github.io/adk-docs/agents/).
+
+It’s not just a library — it’s a complete system: unified interfaces, flexible composition, and strong collaboration primitives let you build complex agent systems like LEGO:
+
+- Minimal glue: unified interfaces and event flows make decomposition natural.
+- Fast orchestration: built-in patterns and workflows assemble pipelines quickly.
+- Controllable: interrupt/resume/audit — collaboration you can see and trust.
+
+Design philosophy: “simple things are simple; complex things are possible”. Focus on business logic without low-level complexity.
+
+# Core Building Blocks
+
+## ChatModelAgent — the Brain for Decisions
+
+`ChatModelAgent` implements the classic [ReAct](https://react-lm.github.io/) loop:
+
+1) Call LLM (Reason)
+2) LLM returns a tool call (Action)
+3) Execute the tool (Act)
+4) Feed tool results back (Observation), repeat until no tool call is requested
+
+
+
+Use cases include structured research and IT operations troubleshooting, with progressive reasoning and validation. Create a tool-enabled `ChatModelAgent` quickly:
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Create a ReAct ChatModelAgent with multiple tools
+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,
+ },
+ }
+})
+```
+
+ReAct emphasizes the closed loop of "Think → Act → Observe → Think again", avoiding blind actions and separating reasoning from execution. Example scenarios:
+
+- Industry track analysis
+ - Think-1: identify needed info — policy support, growth rate, leaders’ profitability, supply chain bottlenecks
+ - Act-1: call APIs to fetch financial reports
+ - Think-2: infer high growth and policy tailwinds; rising upstream prices may squeeze downstream margins; verify impact
+ - Act-2: fetch supply/demand and analyst reports
+ - Think-3: synthesize and generate a report with sources
+- IT incident operations
+ - Think-1: outline common root causes (CPU overload, insufficient memory, disk full, service crash); check basic metrics first
+ - Act-1: call monitoring API to query host metrics
+ - Think-2: if CPU abnormal, inspect top CPU processes
+ - Act-2: use process tooling to list top processes; check abnormal services
+ - Think-3: suspect logging service; verify config and file size (oversized logs or misconfig)
+ - Act-3: run bash commands; confirm oversized logs and missing rotation limits
+ - Think-4: propose remediation: clean logs, enable rotation, update config, restart logging and application
+
+## WorkflowAgents — Precision Pipelines
+
+Eino ADK provides controlled execution patterns to coordinate sub-agents:
+
+- **SequentialAgent** — run sub-agents in order; each gets full input plus prior outputs.
+- **ParallelAgent** — run sub-agents concurrently with shared input; aggregate outputs.
+- **LoopAgent** — repeat a sequence of sub-agents with max iterations or exit condition.
+
+Examples:
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Execute: plan → search → write
+sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ Name: "research_pipeline",
+ SubAgents: []adk.Agent{
+ planAgent, // plan research
+ searchAgent, // search information
+ writeAgent, // write report
+ },
+})
+```
+
+
+
+SequentialAgent principles:
+
+- Linear execution: strictly follows `SubAgents` order
+- Output passing: each agent gets full input plus prior outputs
+- Early exit: any sub-agent can terminate via exit/interrupt
+
+ParallelAgent principles:
+
+- Concurrency: all sub-agents start simultaneously in separate goroutines
+- Shared input: all sub-agents receive the same initial input
+- Wait and aggregate: use `sync.WaitGroup`; collect and emit results via `AsyncIterator`
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Concurrent: sentiment + keywords + summary
+parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
+ Name: "multi_analysis",
+ SubAgents: []adk.Agent{
+ sentimentAgent, // sentiment analysis
+ keywordAgent, // keyword extraction
+ summaryAgent, // summarization
+ },
+})
+```
+
+
+
+LoopAgent principles:
+
+- Iterative sequence: repeat Sequential runs
+- Accumulated context: later iterations can access historical outputs
+- Conditional exit: exit on `ExitAction` or reaching `MaxIterations`; `MaxIterations=0` means infinite loop
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Loop 5 times: analyze → improve → validate
+loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
+ Name: "iterative_optimization",
+ SubAgents: []adk.Agent{
+ analyzeAgent, // analyze current state
+ improveAgent, // propose improvements
+ validateAgent, // validate improvements
+ },
+ MaxIterations: 5,
+})
+```
+
+
+
+## Prebuilt Multi-Agent Patterns
+
+Two production-grade multi-agent paradigms:
+
+### Supervisor — Centralized Coordination
+
+One supervisor orchestrates multiple sub-agents; subs return results deterministically.
+
+
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/supervisor
+
+// Research project management: create a supervisor-pattern multi-agent
+// Sub-agents: research, experimentation, report
+supervisor, err := supervisor.New(ctx, &supervisor.Config{
+ SupervisorAgent: supervisorAgent,
+ SubAgents: []adk.Agent{
+ researchAgent,
+ experimentationAgent,
+ reportAgent,
+ },
+})
+```
+
+Supervisor characteristics:
+
+- Centralized control: supervisor assigns tasks and adjusts based on sub-agent outputs
+- Deterministic callback: sub-agents hand control/results back to the supervisor
+- Loose coupling: sub-agents are independently developable, testable, and replaceable
+
+Representative scenarios:
+
+- Research project management: assign research/experiments/report writing to specialized agents
+- Customer service: route to technical support/after-sales/sales based on issue type
+
+### Plan-Execute — Structured Problem Solving
+
+Planner creates a plan; Executor executes the current step; Replanner evaluates and adjusts.
+
+
+
+
+
+Plan-Execute characteristics:
+
+- Clear layered cognition: plan → act → reflect/replan, improving reasoning quality and generality
+- Dynamic iteration: replanner adjusts plans based on progress and results; robust to uncertainty
+- Modular responsibilities: agents are interchangeable, promoting maintainability
+- Extensible: independent of specific LLMs/tools; integrates diverse resources
+
+Representative scenarios:
+
+- Complex research analysis: multi-round retrieval/calculation with plan adjustments
+- Automated workflows: structured steps combining DB queries, API calls, compute engines
+- Multi-step problem solving: legal consulting, technical diagnosis, strategy formation
+- Assistant task execution: plan steps, call tools, adjust based on feedback to ensure completeness
+
+```go
+import github.com/cloudwego/eino/adk/prebuilt/planexecute
+
+researchAssistant := planexecute.New(ctx, &planexecute.Config{
+ Planner: adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "research_planner",
+ Instruction: "Create a detailed research plan including literature review, data collection, and analysis methods",
+ Model: gpt4Model,
+ }),
+ Executor: adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "research_executor",
+ ToolsConfig: adk.ToolsConfig{ Tools: []tool.BaseTool{ scholarSearchTool, dataAnalysisTool, citationTool } },
+ }),
+ Replanner: replannerAgent,
+})
+```
+
+### DeepAgents — Planning-Driven Centralized Collaboration
+
+DeepAgents centralizes coordination under a Main Agent that runs a ReAct loop with tool calls:
+
+- WriteTodos: break goals into structured todos and track progress
+- TaskTool: unified entry to select and call sub-agents; isolate main/sub contexts to avoid pollution
+- Aggregate results from sub-agents; update todos or re-plan as needed until completion
+
+
+
+Characteristics:
+
+- Stronger decomposition and progress management via todos and milestones
+- Context isolation for robustness; main agent aggregates without leaking intermediate chains
+- Unified delegation entry; easy to add/replace specialized sub-agents
+- Flexible plan–execute loop; skip planning for simple tasks to reduce LLM cost/time
+- Trade-offs: over-decomposition increases calls/cost; requires good sub-task boundaries and stable tool-calling/planning capabilities
+
+Representative scenarios:
+
+- Multi-role business flows across R&D/test/release/legal/ops with stage gates and retries
+- Long pipelines with staged management (cleaning/validation/lineage/QC) and isolated re-runs on failure
+- Strict isolation environments: route tasks to legal/risk/finance; audit progress and retry without affecting other stages
+
+```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,
+})
+```
+
+# Foundation Design
+
+## Unified Agent Abstraction
+
+All agents share a minimal interface, with standardized inputs and event-driven outputs:
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+This standardization enables composition, observability, and control across complex systems.
+
+## Asynchronous Event-Driven Architecture
+
+ADK uses an asynchronous event stream via `AsyncIterator[*AgentEvent]`, and runs agents with a `Runner`:
+
+- Real-time feedback: `AgentEvent` emits intermediate outputs (agent replies, tool results)
+- Execution tracing: events carry state modifications and run-path for debugging/comprehension
+- Automated control flow: `Runner` handles interrupts, jumps, and exits
+
+## Flexible Collaboration Mechanisms
+
+Agents in the same system can collaborate by sharing state or triggering runs:
+
+- Shared Session: a KV store alive during a run for cross-agent state and data sharing
+
+```go
+func GetSessionValues(ctx context.Context) map[string]any
+func GetSessionValue(ctx context.Context, key string) (any, bool)
+func AddSessionValue(ctx context.Context, key string, value any)
+func AddSessionValues(ctx context.Context, kvs map[string]any)
+```
+
+- Transfer: hand off execution to a sub-agent with current context; common with `ChatModelAgent` for dynamic routing
+
+
+
+```go
+func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error)
+func NewTransferToAgentAction(destAgentName string) *AgentAction
+```
+
+- ToolCall: call an agent as a tool when only parameters are needed; results return to the chat model. Also supports non-agent tools.
+
+
+
+```go
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+## Interrupt and Resume
+
+Agents can interrupt runs, persist state via `CheckPointStore`, and later resume from the interruption point — ideal for long waits, pauses, or human-in-the-loop inputs.
+
+- Emit an event with `Interrupt Action` to notify the `Runner`
+- `Runner` records run state into a configured `CheckPointStore`
+- Resume with additional info via `Runner.Resume` to continue from the checkpoint
+
+```go
+runner := adk.NewRunner(ctx, adk.RunnerConfig{ Agent: complexAgent, CheckPointStore: memoryStore })
+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 && event.Action.Interrupted != nil {
+ ii, _ := json.MarshalIndent(event.Action.Interrupted.Data, "", "\t")
+ fmt.Printf("action: interrupted\n")
+ fmt.Printf("interrupt snapshot: %v", string(ii))
+ }
+}
+
+scanner := bufio.NewScanner(os.Stdin)
+fmt.Print("\nyour input here: ")
+scanner.Scan()
+nInput := scanner.Text()
+
+iter, err := runner.Resume(ctx, "1", adk.WithToolOptions([]tool.Option{subagents.WithNewInput(nInput)}))
+```
+
+# Quickstart
+
+## Install
+
+```go
+go get github.com/cloudwego/eino@latest
+```
+
+## Project Manager Agent
+
+A supervisor-pattern agent coordinating research, coding, and review:
+
+- ProjectManagerAgent: routes and coordinates sub-agents based on dynamic user input
+- ResearchAgent: generates feasible plans; supports interrupt/resume with extra user context
+- CodeAgent: uses a knowledge base tool to recall relevant knowledge and produce high-quality code
+- ReviewAgent: sequential workflow (analyze → generate evaluation → validate) to review research/code and provide reasoned feedback
+
+
+
+Representative scenarios:
+
+- Build a project from scratch: research → coding → review → deliver
+- Improve an existing project: review identifies gaps → code implements → review validates
+- Conduct technical research: research produces report → review critiques → caller decides next actions
+
+Key engineering benefits vs traditional development:
+
+
+| Design | Traditional | With Eino ADK |
+| Agent abstraction | No unified definition; hard collaboration; high maintenance | Unified; clear responsibilities; clean code; parallel agent development |
+| Inputs/outputs | Unstandardized; rely on ad-hoc logs | Event-driven; iterator exposes run; WYSIWYG |
+| Agent collaboration | Manual context passing | Framework-managed context |
+| Interrupt/resume | Implement from scratch (serialize/restore/state) | Register `CheckPointStore` in Runner |
+| Agent patterns | Implement from scratch | Prebuilt, production-ready patterns |
+
+
+```go
+func main() {
+ ctx := context.Background()
+
+ tcm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool { return os.Getenv("OPENAI_BY_AZURE") == "true" }(),
+ })
+ if err != nil { log.Fatal(err) }
+
+ researchAgent, err := agents.NewResearchAgent(ctx, tcm); if err != nil { log.Fatal(err) }
+ codeAgent, err := agents.NewCodeAgent(ctx, tcm); if err != nil { log.Fatal(err) }
+ reviewAgent, err := agents.NewReviewAgent(ctx, tcm); if err != nil { log.Fatal(err) }
+ s, err := agents.NewProjectManagerAgent(ctx, tcm); if err != nil { log.Fatal(err) }
+
+ supervisorAgent, err := supervisor.New(ctx, &supervisor.Config{ Supervisor: s, SubAgents: []adk.Agent{researchAgent, codeAgent, reviewAgent} })
+ if err != nil { log.Fatal(err) }
+
+ runner := adk.NewRunner(ctx, adk.RunnerConfig{ Agent: supervisorAgent, EnableStreaming: true, CheckPointStore: newInMemoryStore() })
+
+ query := "please generate a simple ai chat project with python."
+ checkpointID := "1"
+
+ iter := runner.Query(ctx, query, adk.WithCheckPointID(checkpointID))
+ interrupted := false
+ for {
+ event, ok := iter.Next(); if !ok { break }
+ if event.Err != nil { log.Fatal(event.Err) }
+ if event.Action != nil && event.Action.Interrupted != nil { interrupted = true }
+ prints.Event(event)
+ }
+ if !interrupted { return }
+
+ scanner := bufio.NewScanner(os.Stdin)
+ fmt.Print("\ninput additional context for web search: ")
+ scanner.Scan()
+ nInput := scanner.Text()
+
+ iter, err = runner.Resume(ctx, checkpointID, adk.WithToolOptions([]tool.Option{agents.WithNewInput(nInput)}))
+ if err != nil { log.Fatal(err) }
+ for {
+ event, ok := iter.Next(); if !ok { break }
+ if event.Err != nil { log.Fatal(event.Err) }
+ prints.Event(event)
+ }
+}
+```
+
+# Conclusion
+
+Eino ADK is not just a framework — it’s a complete agent development ecosystem. Unified abstractions, flexible composition, and strong collaboration let Go developers build from simple chatbots to complex multi-agent systems.
+
+> 💡
+> **Start your agent development journey now**
+>
+> - Docs (EN): https://www.cloudwego.io/docs/eino/core_modules/eino_adk/
+> - Source: https://github.com/cloudwego/eino/tree/main/adk
+> - Examples: https://github.com/cloudwego/eino-examples/tree/main/adk
+> - Community: join other developers to share experience and best practices
+>
+> Eino ADK makes agent development simple and powerful!
+
+
diff --git a/content/en/docs/eino/overview/eino_adk_excel_agent.md b/content/en/docs/eino/overview/eino_adk_excel_agent.md
new file mode 100644
index 00000000000..1a03d0444f3
--- /dev/null
+++ b/content/en/docs/eino/overview/eino_adk_excel_agent.md
@@ -0,0 +1,465 @@
+---
+Description: ""
+date: "2025-12-02"
+lastmod: ""
+tags: []
+title: 'Build Your First AI Agent with Eino ADK: Excel Agent End-to-End'
+weight: 4
+---
+
+## From Excel Agent to Eino ADK
+
+This article shows how to build a robust multi-agent system using **Eino ADK**, via a practical Excel Agent. The Excel Agent “understands your instructions, reads your sheets, writes and runs code” — decomposing tasks, invoking tools, and validating outcomes to complete Excel data tasks reliably.
+
+Earlier Eino ADK introduction: https://www.cloudwego.io/docs/eino/core_modules/eino_adk/
+
+Full source: [eino-examples/adk/multiagent/integration-excel-agent](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent)
+
+### What is the Excel Agent?
+
+It’s an “Excel-native assistant” that decomposes problems into steps, executes, and verifies. It understands user prompts and uploaded files, proposes solutions, chooses tools (system commands, generated Python code, web search), and completes tasks.
+
+Architecture:
+
+
+
+Agents:
+
+- Planner: analyze input, generate an executable plan
+- Executor: execute the current plan step
+- CodeAgent: read/write files, run Python
+- WebSearchAgent: search the web
+- Replanner: decide continue/adjust/finish
+- ReportAgent: summarize outputs
+
+Use cases:
+
+- Data cleaning and formatting
+- Analysis and report generation
+- Budget automation
+- Data matching and merging
+
+End-to-end flow:
+
+
+
+> 💡
+> **Core Benefits**:
+>
+> - Less manual work — offload complex Excel processing to the Agent.
+> - More stable output quality — plan–execute–reflect loop reduces misses and errors.
+> - Greater extensibility — independent Agents with low coupling enable faster iteration.
+
+Excel Agent can run standalone, or act as a sub-agent inside a composite multi-expert system; external routers can hand off Excel-domain tasks to this Agent.
+
+### ChatModelAgent — ReAct at the Core
+
+`ChatModelAgent` uses the [ReAct](https://react-lm.github.io/) pattern (think–act–observe):
+
+
+
+- Call the ChatModel (Reason)
+- The LLM returns a tool call request (Action)
+- ChatModelAgent executes the tool (Act)
+- Return tool results to the LLM (Observation) and continue until no tool is needed
+
+In Excel Agent, each agent centers on a `ChatModelAgent`. Example: executor step “read header row from xlsx” via CodeAgent:
+
+1) Executor routes step to CodeAgent
+2) CodeAgent Think/Act cycle:
+ - Think-1: list working directory
+ - Act-1: bash `ls`
+ - Think-2: write Python to read xlsx header
+ - Act-2: run Python code
+ - Think-3: task done
+3) Return results to Executor
+
+
+
+### Plan-Execute — Structured Collaboration
+
+Planner creates plans; Executor executes steps via tools; Replanner evaluates progress and replans.
+
+Plans are structured lists of steps. Eino ADK provides `Plan` interfaces and default JSON serializable plan structures.
+
+```go
+// Full code: 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)
+```
+
+
+
+For Excel Agent, the trio matches the goal of solving Excel-domain problems:
+
+- Planner: clarify goals and generate executable steps
+- Executor: call tools (Excel reading, system commands, Python) to perform steps
+- Replanner: decide whether to continue, adjust, or finish based on progress
+
+`Planner` and `Replanner` turn fuzzy instructions into structured step lists (a `Plan`). Eino ADK defines a flexible `Plan` interface and you can customize the structure:
+
+```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
+}
+```
+
+By default, the framework provides a builtin plan structure. Example plan produced by Excel Agent:
+
+```sql
+### Task Plan
+- [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 — Controlled Pipelines
+
+Excel Agent needs:
+
+1) Sequential: Planner → Executor/Replanner (Planner runs once)
+2) Loop: Executor + Replanner repeated
+3) Sequential: ReportAgent at the end
+
+Use Eino ADK’s `SequentialAgent`, `LoopAgent`, and `ParallelAgent` to build controlled pipelines.
+
+
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Execute plan → search → write once
+sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
+ Name: "research_pipeline",
+ SubAgents: []adk.Agent{
+ planAgent, // plan research
+ searchAgent, // search materials
+ writeAgent, // write report
+ },
+})
+```
+
+
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Loop 5 times: analyze → improve → validate
+loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
+ Name: "iterative_optimization",
+ SubAgents: []adk.Agent{
+ analyzeAgent, // analyze current status
+ improveAgent, // propose improvements
+ validateAgent, // validate improvements
+ },
+ MaxIterations: 5,
+})
+```
+
+
+
+```go
+import github.com/cloudwego/eino/adk
+
+// Concurrent: sentiment + keywords + summary
+parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
+ Name: "multi_analysis",
+ SubAgents: []adk.Agent{
+ sentimentAgent, // sentiment analysis
+ keywordAgent, // keyword extraction
+ summaryAgent, // content summarization
+ },
+})
+```
+
+
+
+### Agent Abstraction — Unified and Extensible
+
+All agents implement a minimal interface with standardized inputs and event-driven outputs. Prebuilt agents follow this contract; you can create custom ones easily.
+
+- Unified Agent abstraction — prebuilt agents (ChatModelAgent, Plan-Execute, Workflow Agents) follow this interface; you can implement custom agents too:
+
+```go
+type Agent interface {
+ Name(ctx context.Context) string
+ Description(ctx context.Context) string
+ Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
+}
+```
+
+- Standardized input — aligned with LLM I/O:
+
+```go
+type AgentInput struct {
+ Messages []Message
+ EnableStreaming bool
+}
+
+type Message = *schema.Message
+```
+
+- Asynchronous event-driven output — an iterator of `AgentEvent` containing metadata, outputs, actions, and errors:
+
+```go
+type AgentEvent struct {
+ AgentName string // Agent name (auto-filled by framework)
+ RunPath []RunStep // Full run path to reach current Agent (auto-filled)
+ Output *AgentOutput // Agent output message content
+ Action *AgentAction // Agent action event content
+ Err error // Agent error
+}
+
+type AgentOutput struct {
+ MessageOutput *MessageVariant // Model message output
+ CustomizedOutput any // Customized output content
+}
+
+type MessageVariant struct {
+ IsStreaming bool // Whether streaming output
+ Message Message // Non-streaming message output
+ MessageStream MessageStream // Streaming message output
+ Role schema.RoleType // Message role
+ ToolName string // Tool name
+}
+
+type AgentAction struct {
+ Exit bool // Agent exit
+ Interrupted *InterruptInfo // Agent interrupted
+ TransferToAgent *TransferToAgentAction // Agent transfer
+ CustomizedAction any // Customized Agent action
+}
+```
+
+Consume the iterator in a simple loop:
+
+```go
+iter := myAgent.Run(ctx, "hello")
+for {
+ event, ok := iter.Next()
+ if !ok { break }
+ // handle event
+}
+```
+
+### Agent Collaboration — Data Passing Under the Hood
+
+Nodes are agents; edges represent data flow and task handoff. ADK provides robust data sharing mechanisms:
+
+- History: each agent’s `AgentEvent` is recorded and converted into the next agent’s input. By default, assistant/tool messages from other agents become user messages for the current agent, supplying factual context without confusing roles.
+
+
+
+- Shared Session: a KV store alive during the run; agents can read/write `SessionValues` anytime. Typical Plan-Execute flow: planner writes the initial plan; executor reads and runs; replanner updates the plan back into session.
+
+```go
+func GetSessionValues(ctx context.Context) map[string]any
+func GetSessionValue(ctx context.Context, key string) (any, bool)
+func AddSessionValue(ctx context.Context, key string, value any)
+func AddSessionValues(ctx context.Context, kvs map[string]any)
+func WithSessionValues(v map[string]any) AgentRunOption
+```
+
+
+
+Collaboration modes:
+
+- Preset workflow order (Workflow) — deterministic execution order via `Sequential`/`Loop`/`Parallel`.
+- Transfer — hand off tasks to sub-agents with context; often combined with ChatModelAgent and dynamic routing.
+
+
+
+```go
+func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error)
+func NewTransferToAgentAction(destAgentName string) *AgentAction
+```
+
+- ToolCall — call an agent as a tool when only parameters are needed; return results to the chat model. Also supports non-agent tools.
+
+
+
+```go
+func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool
+```
+
+## Excel Agent — How to Run
+
+### Environment and Paths
+
+- Environment variables — see the project README for the full list.
+- Inputs — a user request plus a set of files:
+ - In `main.go`, the first lines define the user query (modify as needed):
+
+ ```go
+ func main() {
+ // query := schema.UserMessage("统计附件文件中推荐的小说名称及推荐次数,并将结果写到文件中。凡是带有《》内容都是小说名称,形成表格,表头为小说名称和推荐次数,同名小说只列一行,推荐次数相加")
+ // query := schema.UserMessage("读取模拟出题.csv 中的内容,规范格式将题目、答案、解析、选项放在同一行,简答题只把答案写入解析即可")
+ query := schema.UserMessage("请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中")
+ }
+ ```
+
+ - Default attachment input path: `adk/multiagent/integration-excel-agent/playground/input` (configurable; see README)
+ - Sample files are under `adk/multiagent/integration-excel-agent/playground/test_data`:
+
+ ```
+ % 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
+ ```
+
+- Outputs — attachments, intermediate artifacts, and final results live under `adk/multiagent/integration-excel-agent/playground/${uuid}` (configurable).
+
+### Inspecting Results
+
+A run creates a new working directory under the output path and writes all intermediate and final artifacts into it.
+
+Example task: “extract the first column from `question.csv` into a new CSV” — artifacts include:
+
+
+
+1) Raw input: `question.csv`
+2) Planner/Replanner plan: `plan.md`
+
+```
+### Task Plan
+- [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) CodeAgent script: `$uuid.py`
+
+```python
+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) Intermediate outputs: `extracted_first_column.csv` and `first_column.csv`
+
+```
+type
+multiple-choice
+...
+short-answer
+```
+
+5) Final report: `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."
+ }
+ ]
+}
+```
+
+### Runtime Logs
+
+Key steps for the same task:
+
+- Planner emits structured JSON plan
+
+```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 tool-calls CodeAgent to run the first step
+
+```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 uses PythonRunner and auto-corrects via ReAct
+
+```yaml
+name: Executor
+tool name: PythonRunner
+arguments: {"code":"```python\nfirst_column = df.iloc[:, 0]\n```"}
+
+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
+ first_column = df.iloc[:, 0]
+ ^^
+NameError: name 'df' is not defined
+
+ name: Executor
+ answer: The error occurs because the DataFrame `df` is not defined. We need to first load the data from the existing CSV file `questions.csv` into `df`. Here's the corrected code:
+ tool name: PythonRunner
+ arguments: {"code":"```python\nimport pandas as pd\ndf = pd.read_csv('questions.csv')\nfirst_column = df.iloc[:, 0]\nprint(first_column.head()) # Verify the result\n```"}
+```
+
+- Replanner marks completion and submits to ReportAgent
+
+```yaml
+name: Replanner
+answer: { "is_success": true, "result": "Successfully extracted the first column from 'questions.csv' into the new CSV file 'extracted_first_column.csv'.", "files": [ { "desc": "A new CSV file containing the first column from the original table", "path": "extracted_first_column.csv" } ] }
+```
+
+- ReportAgent finalizes
+
+```yaml
+name: Report
+tool name: SubmitResult
+arguments: { "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." } ] }
+```
+
+## Summary
+
+Excel Agent demonstrates a Multi-Agent engineering methodology on Eino ADK:
+
+- ChatModelAgent with ReAct — the thinking and tool-using core
+- WorkflowAgents — controlled orchestration in expected order
+- Planner–Executor–Replanner — decomposition and self-correction
+- History/Session — collaboration and replayability
+
+> 💡
+> **Start your agent development journey**
+>
+> - ⌨️ View Excel Agent source: https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent
+> - 📚 Read more docs: https://www.cloudwego.io/docs/eino/core_modules/eino_adk/
+> - 🛠️ Browse ADK source: https://github.com/cloudwego/eino/tree/main/adk
+> - 💡 Explore all ADK examples: https://github.com/cloudwego/eino-examples/tree/main/adk
+> - 🤝 Join the developer community: share experiences and best practices
+
+
diff --git a/content/en/docs/eino/overview/eino_framework_structure.md b/content/en/docs/eino/overview/eino_framework_structure.md
deleted file mode 100644
index cf2957a3218..00000000000
--- a/content/en/docs/eino/overview/eino_framework_structure.md
+++ /dev/null
@@ -1,386 +0,0 @@
----
-Description: ""
-date: "2025-02-21"
-lastmod: ""
-tags: []
-title: The structure of the Eino Framework
-weight: 0
----
-
-## Overall Structure
-
-Six key concepts in Eino:
-
-- Components Abstraction
- - Each Component has a corresponding interface abstraction and multiple implementations. Can be used directly or orchestrated
- - When orchestrated, the node's input/output matches the interface abstraction
- - Similar to out-of-the-box atomic components like ChatModel, PromptTemplate, Retriever, Indexer etc.
- - The Component concept in Eino is relatively loose - anything that satisfies one of the following responsibilities can be called a Component:
- - Can be added to Graph Node as an orchestration object
- - Can be used as a dependency injection component for other orchestration objects
-- Flow Integration Components
- - Based on the framework's Components and Graph, provides pre-orchestrated integration component capabilities for common application scenarios
- - May provide capability to be orchestrated again
- - Examples: Agent, MultiAgent etc.
-- Runnable -- Low User Awareness
- - Orchestration objects and products in the orchestration framework
- - All Components must be converted to Runnable objects when being orchestrated, generally invisible to users
- - When a graph is compiled into an executable object, it is essentially a Runnable object
-- Compose Orchestration
- - Connects various Component instances as Node nodes through graph point-line relationships, where data flows along directed edges and executes in different nodes
- - Supports multiple orchestration forms like Graph, Chain, Workflow etc., all essentially expressing data flow transmission and node execution order through directed graphs
-- Aspect Capabilities
- - Aspect capabilities provided before and after each node execution in the Graph
- - Examples: Trace, metrics, logging etc.
-- Stream
- - Component instances added to Nodes may have streaming or non-streaming inputs/outputs. Compose orchestration can connect these different forms of input/output, transmit data streams and execute nodes. This capability is called streaming orchestration
- - For example, ChatModel output and ASR input/output are streaming
-
-## Component
-
-For the specific responsibilities of each Component type, please refer to their corresponding interface definitions
-
-> The following is an illustrative explanation and is not complete. Please refer to the [code repository](https://github.com/cloudwego/eino-ext/tree/main/components) for authoritative information.
-
-```
-eino/components // component root folder
-├── document
-│ ├── interface.go
-│ └── option.go
-├── embedding
-│ ├── callback_extra.go
-│ ├── interface.go // one component abstraction
-│ ├── ark // one component implementation
-│ ├── openai
-│ └── option.go
-├── indexer
-│ ├── callback_extra.go
-│ ├── interface.go
-│ ├── option.go
-│ └── volc_vikingdb
-├── model
-│ ├── callback_extra.go
-│ ├── interface.go
-│ ├── ark
-│ ├── openai
-│ └── option.go
-├── prompt
-│ ├── callback_extra.go
-│ ├── chat_template.go
-│ ├── chat_template_test.go
-│ └── interface.go
-├── retriever
-│ ├── callback_extra.go
-│ ├── interface.go
-│ ├── option.go
-│ └── volc_vikingdb
-├── tool
-│ ├── duckduckgo
-│ ├── interface.go
-│ └── option.go
-├── types.go
-```
-
-## 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)
-}
-```
-
-- The Runnable abstraction is divided into 4 Lambda operators based on whether the input/output is streaming
-- In Compose orchestration, component instances added to Nodes are uniformly converted into the above Runnable abstractions
-- When a Component is converted to Runnable, based on any Lambda operator it provides, combined with Streaming and Concat capabilities, the remaining unprovided Lambda operators are completed
- - Conversion between streaming and non-streaming: (using StreamReader[T] and T to represent streaming and non-streaming respectively)
- - Concat
- - Receives T-Frames from StreamReader[T] completely and merges them into a complete T
- - Streaming
- - Converts T into a StreamReader[T] with only one T-Frame for streaming transmission
-- Based on these two conversion relationships, Eino can encapsulate and convert any interface with N(N<=4) interaction modes provided by users into a complete Runnable[I, O]
-- The actual streaming capability in the programming output depends on the following orchestration paradigms
-
-
-
-## Stream
-
-Notice: Stream handling logic is quite complex in scenarios like **production**, **consumption**, **copying**, **merging**, and **transformation**. Any oversight in implementation can lead to issues such as producer/consumer deadlocks, goroutine leaks or overflow, memory leaks or overflow, and high CPU load. To reduce stability issues, Eino strictly requires the use of Eino-provided Streams, which is why Stream is implemented as a Struct rather than an interface.
-
-Complex streaming operation scenarios:
-
-- Conversion between streaming and non-streaming interfaces
- - When converting from stream to non-stream, all data frames in the stream need to be Concatenated into a complete data structure
- - When converting from non-stream to stream, a data structure needs to be converted into a stream with only one data frame
-- The same data stream may need to be read and consumed multiple times, such as by multiple aspects. Since a stream can only be read once completely, it needs to be copied based on the number of consumers
- - When copying streams, both consumption coordination and 'Close' coordination need to be considered. If any stream isn't properly closed, it may prevent resources from being properly released
-- Merging multiple streams into one stream
-
-To make the Stream API interface clearer and easier to use, it aligns with Go's built-in io.Pipe() method definition.
-
-- API interface is defined as: `schema.Pipe[T any](cap int) (*StreamReader[T], *StreamWriter[T])`
- - where cap indicates the stream's buffer size, i.e., how many chunks the sender can send without blocking when there's no consumption
- - `StreamWriter` is similar to PipeWriter in io.Pipe
- - `StreamReader` is similar to PipeReader in io.Pipe, but with an additional `Copy(n int) []*StreamReader[T]` method
-- **WARNING**: Whenever you see `*StreamReader[T]` or `*StreamWriter[T]`, don't forget to Close(), otherwise the stream may not be properly released. Generally, stream production and consumption are in separate Goroutines, which could lead to Goroutine leaks.
-
-For Stream API design, see source code: [eino/schema/stream.go](https://github.com/cloudwego/eino/blob/main/schema/stream.go)
-
-```go
-// Pipe creates a new stream with the given capacity that represented with StreamWriter and StreamReader.
-// The capacity is the maximum number of items that can be buffered in the stream.
-// e.g.
-//
-// sr, sw := schema.Pipe[string](3)
-// go func() { // send data
-// defer sw.Close()
-// for i := 0; i < 10; i++ {
-// sw.send(i, nil)
-// }
-// }
-//
-// defer sr.Close()
-// for chunk, err := sr.Recv() {
-// if errors.Is(err, io.EOF) {
-// break
-// }
-// fmt.Println(chunk)
-// }
-func Pipe[T any](cap int) (*StreamReader[T], *StreamWriter[T]) {
- stm := newStream[T](cap)
- return stm.asReader(), &StreamWriter[T]{stm: stm}
-}
-
-// StreamWriter the sender of a stream.
-type StreamWriter[T any] struct {
- stm *stream[T]
-}
-
-func (sw *StreamWriter[T]) Send(chunk T, err error) (closed bool) {
- return sw.stm.send(chunk, err)
-}
-
-// Close notify the receiver that the stream sender has finished.
-// The stream receiver will get an error of io.EOF from StreamReader.Recv().
-func (sw *StreamWriter[T]) Close() {
- sw.stm.closeSend()
-}
-
-// StreamReader the receiver of a stream.
-type StreamReader[T any] struct {}
-
-func (sr *StreamReader[T]) Recv() (T, error) {}
-
-// Close notify the sender that the stream receiver has finished.
-// AKA: CloseRecv.
-func (sr *StreamReader[T]) Close() {}
-
-// Copy creates a slice of new StreamReader.
-// The number of copies, indicated by the parameter n, should be a non-zero positive integer.
-// The original StreamReader will become unusable after Copy.
-func (sr *StreamReader[T]) Copy(n int) []*StreamReader[T] {}
-```
-
-## Compose
-
-### Graph
-
-#### Node
-
-- Adding a Component instance to a Graph forms a Node
-- Components can be used independently or orchestrated by Graph
-- Add{Component}Node() interface listing. Only a few interfaces are listed here; for a more detailed interface list, please check the latest Eino SDK
- - For common component types, a standard behavioral semantic is abstracted and different implementations are provided
- - Business logic can add any custom function as a node using AddLambdaNode
-
-```go
-// AddChatModelNode add node that implements model.ChatModel.
-func (g *graph) AddChatModelNode(key string, node model.ChatModel, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toChatModelNode(key, node, opts...))
-}
-
-// AddChatTemplateNode add node that implements prompt.ChatTemplate.
-func (g *graph) AddChatTemplateNode(key string, node prompt.ChatTemplate, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toChatTemplateNode(key, node, opts...))
-}
-
-func (g *graph) AddToolsNode(key string, node *ToolsNode, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toToolsNode(key, node, opts...))
-}
-
-// AddLambdaNode add node that implements at least one of Invoke[I, O], Stream[I, O], Collect[I, O], Transform[I, O].
-// due to the lack of supporting method generics, we need to use function generics to generate Lambda run as Runnable[I, O].
-// for Invoke[I, O], use compose.InvokableLambda()
-// for Stream[I, O], use compose.StreamableLambda()
-// for Collect[I, O], use compose.CollectableLambda()
-// for Transform[I, O], use compose.TransformableLambda()
-// for arbitrary combinations of 4 kinds of lambda, use compose.AnyLambda()
-func (g *graph) AddLambdaNode(key string, node *Lambda, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toLambdaNode(key, node, opts...))
-}
-
-// AddGraphNode add one kind of Graph[I, O]、Chain[I, O]、StateChain[I, O, S] as a node.
-// for Graph[I, O], comes from NewGraph[I, O]()
-// for Chain[I, O], comes from NewChain[I, O]()
-// for StateGraph[I, O, S], comes from NewStateGraph[I, O, S]()
-func (g *graph) AddGraphNode(key string, node AnyGraph, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toAnyGraphNode(key, node, opts...))
-}
-
-func (g *graph) AddRetrieverNode(key string, node retriever.Retriever, opts ...GraphAddNodeOpt) error {
- return g.addNode(key, toRetrieverNode(key, node, opts...))
-}
-```
-
-#### Edge
-
-Eino provides multiple ways to add edges
-
-##### Add**Edge**
-
-```go
-// AddEdge adds an edge to the graph, edge means a data flow from startNode to endNode.
-// the previous node's output type must be set to the next node's input type.
-// NOTE: startNode and endNode must have been added to the graph before adding edge.
-// e.g.
-//
-// graph.AddNode("start_node_key", compose.NewPassthroughNode())
-// graph.AddNode("end_node_key", compose.NewPassthroughNode())
-//
-// err := graph.AddEdge("start_node_key", "end_node_key")
-func (g *graph) AddEdge(startNode, endNode string) (err error) {}
-```
-
-- Add a directed data transmission link between two nodes to control the direction of data flow and the execution order of nodes
-
-
-
-##### **AddBranch**
-
-```go
-// AddBranch adds a branch to the graph.
-// e.g.
-//
-// condition := func(ctx context.Context, in string) (string, error) {
-// return "next_node_key", nil
-// }
-// endNodes := map[string]bool{"path01": true, "path02": true}
-// branch := compose.NewGraphBranch(condition, endNodes)
-//
-// graph.AddBranch("start_node_key", branch)
-func (g *graph) AddBranch(startNode string, branch *GraphBranch) (err error) {}
-```
-
-- Based on the provided custom selection function, select and execute the matching Node from multiple Nodes according to computed conditions at runtime
-
-
-
-##### **Parallel**
-
-- Connect multiple nodes in parallel to form nodes that execute concurrently
-- There is no AddParallel method; **Parallel** is formed by using AddEdge to build multiple parallel topological paths
-
-
-
-#### Graph
-
-- Create a graph instance using NewGraph, and draw nodes and edges through graph.AddXXXNode, graph.AddEdge, and graph.AddBranch to ultimately form a compilable and executable graph
-
-```go
-// stateless graph
-g := NewGraph[map[string]any, *schema.Message]()
-
-type testState struct {
- ms []string
-}
-
-genFn := func(ctx context.Context) *testState {
- return &testState{}
-}
-
-// stateful graph
-sg := NewGraph[string, string](WithGenLocalState(genFn))
-
-// a chain is a simplified version of graph
-chain := NewChain[map[string]any, string]()
-```
-
-### Chain
-
-> Chain - A simplified Graph that connects different types of Nodes in sequence, forming head-to-tail data flow transmission and sequential execution.
-
-#### **AppendXXX**
-
-> XXX can be various component types such as ChatModel, Prompt, Indexer, Retriever, Graph, etc.
->
-> Since Chain is a simplified Graph, Chain and Graph can be nested through AppendGraph
-
-- Connects multiple Nodes in series according to the input order, where data is passed and executed sequentially through the connected Nodes
-
-
-
-#### **AppendParallel**
-
-> Add a node that has multiple concurrently executing sub-nodes
-
-```go
-// Parallel run multiple nodes in parallel
-//
-// use `NewParallel()` to create a new parallel type
-// Example:
-
-parallel := NewParallel()
-parallel.AddChatModel("output_key01", chat01)
-parallel.AddChatModel("output_key01", chat02)
-
-chain := NewChain[any,any]()
-chain.AppendParallel(parallel)
-```
-
-- Create a Parallel to accommodate multiple sub-nodes that execute concurrently
-
-
-
-#### **AppendBranch**
-
-> Add a node that selects one from multiple sub-nodes to execute based on a condition calculation method
-
-```go
-// NewChainBranch creates a new ChainBranch instance based on a given condition.
-// It takes a generic type T and a GraphBranchCondition function for that type.
-// The returned ChainBranch will have an empty key2BranchNode map and a condition function
-// that wraps the provided cond to handle type assertions and error checking.
-// eg.
-
-condition := func(ctx context.Context, in string, opts ...any) (endNode string, err error) {
- // logic to determine the next node
- return "some_next_node_key", nil
-}
-
-cb := NewChainBranch[string](condition)
-cb.AddPassthrough("next_node_key_01", xxx) // node in branch, represent one path of branch
-cb.AddPassthrough("next_node_key_02", xxx) // node in branch
-
-chain := NewChain[string, string]()
-chain.AppendBranch(cb)
-```
-
-
-
-### Workflow
-
-A directed acyclic graph that allows field-level upstream and downstream data mapping.
-
-## Aspects (Callbacks)
-
-Components (including Lambda) and Graph orchestration together solve the problem of "defining business logic." However, cross-cutting functionalities like logging, tracing, metrics, and screen display need a mechanism to be injected into Components (including Lambda) and Graph.
-
-Additionally, users might want to access intermediate information during the execution of specific Component implementations, such as VikingDBRetriever providing additional DB Name information, or ArkChatModel providing temperature parameters. A mechanism is needed to expose these intermediate states.
-
-Callbacks support both '**cross-cutting functionality injection'** and '**intermediate state exposure'**. Specifically: users provide and register "functions" (Callback Handlers), which Components and Graph call back at fixed "timings" (or aspects, points) to provide corresponding information.
-
-**Entities** in Eino, such as Components and Graph, at fixed **timings** (Callback Timing), call back user-provided **functions** (Callback Handlers), and pass information about **who they are** (RunInfo) and **what happened** (Callback Input & Output).
-
-For details, see: [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)
diff --git a/content/en/docs/eino/overview/eino_open_source.md b/content/en/docs/eino/overview/eino_open_source.md
index 7bd899a3789..a50c42bfc3a 100644
--- a/content/en/docs/eino/overview/eino_open_source.md
+++ b/content/en/docs/eino/overview/eino_open_source.md
@@ -1,187 +1,158 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: Large Language Model Application Development Framework — Eino is Now Open Source!
-weight: 0
+title: LLM Application Development Framework — Eino Open Source Release!
+weight: 1
---
-Today, after more than six months of internal use and iteration at ByteDance, the Go-based comprehensive LLM application development framework — Eino, has been officially open-sourced on CloudWeGo!
+Today, after over half a year of internal use and iteration at ByteDance, the Go-based comprehensive LLM application development framework — Eino — is officially open-sourced under CloudWeGo.
-Based on clear "component" definitions, Eino provides powerful process "orchestration" covering the entire development lifecycle, aiming to help developers create the most sophisticated LLM applications in the shortest time possible.
+Eino defines clear “components” and provides powerful “orchestration”, covering the full development lifecycle, helping developers build deep LLM applications quickly.
-Have you ever felt this way: wanting to add LLM capabilities to your application but not knowing where to start in this relatively new field; wanting to stay at the forefront of research and apply the latest industry achievements, but using an application development framework that hasn't been updated for months; trying to understand Python code in your project, needing to repeatedly check context to confirm a variable or parameter type; unsure whether the model-generated results are good enough and hesitant to use them; needing to explore and learn additional tools for necessary aspects beyond development like debugging, tracing, and evaluation. If so, welcome to Eino! As a LLM application development framework aimed at covering the entire devops process, Eino has the following characteristics:
+If you’ve felt any of these pains — wanting to add LLM capabilities but unsure where to start; your framework falls behind industry progress; weak typing makes code hard to reason about; model outputs feel risky without strong tracing/observability/tooling — Eino is built to address exactly these concerns with a devops-spanning framework:
-- Stable core, simple and understandable API, clear onboarding path, and smooth learning curve.
-- Ultimate extensibility, highly active development work, long-term sustainability.
-- Based on strongly-typed Go, readable code, easy maintenance, high reliability.
-- Backed by extensive practical experience from ByteDance's core business lines.
-- Provides out-of-the-box supporting tools.
+- Stable core, simple APIs, clear onboarding path, smooth learning curve
+- Extreme extensibility, highly active development, long-term sustainability
+- Strong typing with Go, readable code, maintainable and reliable
+- Backed by extensive ByteDance practices (Doubao, TikTok, etc.)
+- Out-of-the-box tooling ecosystem
-Eino has become the preferred full-code development framework for LLM applications within ByteDance, with hundreds of services across multiple business lines including Doubao, Douyin, Coze and more already using it.
+Eino is ByteDance’s preferred full-code framework for LLM applications, adopted across multiple business lines and hundreds of services.
-Project repositories: [https://github.com/cloudwego/eino](https://github.com/cloudwego/eino), [https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+Project: `https://github.com/cloudwego/eino`, `https://github.com/cloudwego/eino-ext`
-Going forward, we will maintain Eino's open-source repository as the core codebase, adhering to the principle of **using the same code internally and externally**, working with the community to build the best LLM application development framework.
+We will build around the open-source Eino repository, keeping one unified codebase for both internal and external usage.
-## Quick Introduction to Eino
+## Quick Look at Eino
-Eino is a LLM application development framework covering the entire devops process, from best practice examples in Eino Examples to toolchains for various stages:
+Eino is a devops-spanning framework — from best-practice samples (Eino Examples) to tooling for each stage:
-So what exactly can Eino do? First, Eino consists of "**components**" from the LLM domain, with the core being the Chat Model for interacting with large language models:
+Eino organizes LLM applications around reusable “components”. For example, the core `ChatModel`:
```go
-model, _ := ark.NewChatModel(ctx, config) // create a doubao chat model
+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?")}
+ UserMessage("what does the future AI App look like?")})
```
-Using individual components directly like above is certainly fine, and Eino provides many useful component implementations to choose from. However, LLM applications have their own characteristics and patterns, such as:
+Direct component usage is fine — Eino provides many implementations. But LLM apps share patterns:
-- The core is the large language model, with business logic centered around "how to provide sufficient and effective context to the model" and "how to reliably let the model's output affect the environment". The core component types, data types, and interaction patterns are enumerable, and the whole can be described by a directed graph.
-- LLM output is characterized by streaming output, meaning that all downstream components need to effectively handle streaming data, including real-time stream processing, stream copying, merging multiple streams, concatenating multiple items within a single stream, etc.
-- Based on directed graphs, a series of sub-problems arise, including concurrent processing, fan-in/fan-out, general cross-cutting aspects, option allocation, etc.
+- The model is central; logic focuses on providing rich context to the model and reliably affecting the environment with outputs. Components, data, and interactions are enumerable and describable as directed graphs.
+- Model output is streaming; downstream components must handle streams: real-time processing, copying, merging, and concatenation.
+- Graphs involve concurrency, fan-in/out, cross-cutting callbacks, and option dispatch.
-Eino's orchestration capabilities provide a comprehensive solution to these common problems.
+Eino’s orchestration addresses these general problems.
-Taking ReAct Agent as an example: a ChatModel (large language model) is "bound" to Tools, receives input Messages, and autonomously decides whether to call Tools or output final results. The Tool execution results become Messages fed back to the ChatModel, serving as context for the next round of autonomous decision-making.
+Example: a ReAct Agent — a `ChatModel` “binds” tools, receives `Message` input, reasons whether to call a tool or produce a final answer; tool results are fed back as `Message` for the next decision.
-The above ReAct Agent, which makes autonomous decisions and route selections based on ChatModel, is implemented using Eino's Components and Graph orchestration, with clear and concise code that directly corresponds to the flowchart.
+Implemented via components plus graph orchestration; concise code cleanly maps to the diagram.
-- For code implementation details, see: [flow/agent/react](https://github.com/cloudwego/eino/blob/main/flow/agent/react/react.go)
-- For ReAct Agent user manual, see: [react_agent_manual](https://www.cloudwego.io/docs/eino/core_modules/flow_integration_components/react_agent_manual/)
+- Implementation: `flow/agent/react` in the repo
+- User manual: `/docs/eino/core_modules/flow_integration_components/react_agent_manual/`
-In Eino, this is a graph orchestration of just dozens of lines of code:
+This orchestration is just a few dozen lines:
```go
-// create a ReAct Agent,compile it into a Runnable, with []*Message as input and *Message as output
-
-// create a Graph with state. The state is used for storing message context
graph = NewGraph[[]*Message, *Message](
WithGenLocalState(func(ctx context.Context) *state {
return &state{Messages: make([]*Message, 0, config.MaxStep+1)}
}))
-// store the context and response per round into Graph's local state
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 may output a stream of multiple chunks
-// this StreamGraphBranch could potentially decide only use the first chunk, reducing latency
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
- }
-
+ if msg, err := sr.Recv(); err != nil { return "", err }
+ if len(msg.ToolCalls) == 0 { return END, nil }
return nodeKeyTools, nil
}, map[string]bool{nodeKeyTools: true, END: true})
-_ = graph.AddBranch(nodeKeyModel, modelPostBranch)
-
-// toolsNode feeds back to chatModel
+_ = graph.AddBranch(nodeKeyModel, modelPostBranch)
_ = graph.AddEdge(nodeKeyTools, nodeKeyModel)
-// compile the Graph: type checking、callback injection、stream conversion、generate runner
agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))
```
-Behind these dozens of lines of code, Eino automatically handles several things:
+Behind these lines, Eino automatically:
-- Type checking, ensuring type alignment between adjacent nodes at compile time.
-- Stream encapsulation, the compiled Runnable can be called via both Invoke and Stream, regardless of whether the internal Tool supports streaming.
-- Concurrency management, ensuring thread-safe read/write operations on the shared state.
-- Cross-cutting aspect injection, automatically injecting callbacks if a component (like a tool) hasn't implemented them.
-- Option allocation, the compiled Runnable can flexibly receive and distribute options to specified nodes.
+- Performs type checking at compile-time to ensure neighbor node compatibility
+- Boxes streams so the compiled runnable supports `Invoke` and `Stream`, regardless of internal streaming support
+- Manages concurrency; `state` reads/writes are safe
+- Injects callbacks when components lack them
+- Dispatches call options across nodes
-## Eino's Unique Advantages
+## Eino’s Unique Advantages
-LLM-based software applications are in a rapid development phase, with new technologies, ideas, and practices constantly emerging. As application developers, we need to efficiently and reliably implement industry-consensus best practices while continuously learning and improving our understanding to comprehend the possibilities in this new field. Therefore, an excellent LLM application development framework needs to both **encapsulate the "unchanging" universal core elements** in the domain and **enable agile horizontal and vertical expansion** based on latest developments.
+LLM applications evolve rapidly; a great framework must encapsulate “stable” domain abstractions while expanding horizontally and vertically as research progresses.
-On the other hand, current mainstream frameworks like LangChain and LlamaIndex are Python-based. While they can quickly implement diverse functionalities by leveraging Python's rich ecosystem, they also inherit issues like "weak type checking" and "high long-term maintenance costs" that come with Python being a dynamic language. As LLM applications rapidly enter large-scale online operation phase, the **high reliability** and **high maintainability** achieved through Go as a strongly-typed language are becoming increasingly valuable.
+Python-first stacks like LangChain/LlamaIndex move quickly but inherit dynamic-typing maintenance challenges. As LLM apps reach production scale, Go’s strong types bring reliability and maintainability.
-LLM-based application development is a relatively new field, sometimes requiring exploration and validation through practice. Leveraging ByteDance's high-frequency applications like Doubao and Douyin with their diverse scenarios, rapid iteration, and massive feedback, Eino has unique advantages in **practice-driven design**.
+Eino is practice-driven — built with feedback and iteration across Doubao/TikTok and other high-frequency, diverse scenarios.
-Finally, production-grade frameworks need to handle real, complex business scenarios. Therefore, besides intuitive and easy-to-use API design, providing purposefully designed development **tools** can effectively help developers understand and handle complexity while accelerating the development process.
+Beyond APIs, production frameworks need tooling for real complex scenarios.
### Stable Core
-We believe there exists a common component list that collectively forms the common parts of LLM applications. Each component type, as an interface, has a complete and stable definition: specific input/output types, clear runtime options, and explicit stream processing paradigms.
-
-Based on clear component definitions, we believe LLM application development has universal foundational capabilities, including but not limited to: stream programming capabilities for handling model output; Callback capabilities supporting cross-cutting functionality and exposing component internal states; and option extension capabilities for component implementations beyond the component interface definition.
-
-Building on component definitions and universal foundational capabilities, we believe LLM application development has relatively fixed data flow and process orchestration paradigms: centered around ChatModel (LLM), injecting user input and system prompts through ChatTemplate, injecting context through Retriever, Document Loader & Transformer, etc., generating through ChatModel, outputting Tool Calls for execution or producing final results. Based on this, Eino provides different orchestration paradigms for these components: Chain, a chain-like directed acyclic graph; Graph, a directed graph or directed acyclic graph; Workflow, a directed acyclic graph with field mapping capabilities.
-
-These designs and functionalities together form Eino's stable core:
+- Common component interfaces with clear IO types, options, and streaming paradigms
+- Base capabilities: streaming, callbacks, option extensions beyond interface boundaries
+- Orchestration paradigms: Chain (DAG), Graph (directed), Workflow (DAG with field mapping)
-### Agile Extension
-
-Each component type can be horizontally extended with different implementations. For example, the ChatModel component can have different implementations like OpenAI, Gemini, Claude, etc. These specific implementations, while implementing the component interface to participate in orchestration, can implement and continuously extend their special functionalities.
-
-When actual business scenarios require functionality that needs to be orchestrated but doesn't correspond to any component definition, Eino supports declaring custom functions as Lambda types. Lambda has user-declared input/output and option types, supports all stream processing paradigms, has complete Callback capabilities, and is equivalent to official components from an orchestration perspective.
-
-In the LLM application development field, there are and will continue to emerge specific orchestration paradigms for multiple components. These paradigms encapsulate validated research results or practical experiences, such as ReAct Agent, Host Multi-Agent, etc. These out-of-the-box encapsulations, which distill the best practices in LLM application development, will continue to expand vertically as our understanding improves.
+### Agile Extensibility
-During component and graph execution, developers can embed custom callback logic at fixed timings to inject cross-cutting functionalities.
-
-In summary, the Eino framework possesses comprehensive extensibility:
+- Horizontal expansion: multiple implementations per component (OpenAI, Gemini, Claude, etc.) with special features
+- `Lambda`: declare custom functions as first-class nodes with full streaming paradigms and callbacks
+- Prebuilt paradigms: ReAct Agent, Host Multi-Agent, and more — encapsulating proven practice
-### High Reliability and Maintainability
-
-When writing Eino code in Go, developers can fully utilize Go's strong typing features to declare specific types for all components, Lambdas, and orchestration products. This is like drawing a precise map for the code, allowing developers to maintain and extend along clear paths. Even as the project scale grows and functionality continues to iterate, high maintainability can still be preserved.
-
-At the same time, Eino's orchestration capabilities also fully utilize the compile-time verification capabilities of the strong type system, exposing type matching issues at graph compilation time rather than runtime whenever possible. Early and clear exposure of type matching issues helps developers quickly locate and fix problems, reducing hard-to-diagnose failures and performance issues caused by type errors at runtime.
-
-Additionally, Eino follows a modular design, with the core library and various component implementations as separate go modules, each go module achieving minimal dependencies. Meanwhile, the API design follows principles of "simplicity", "intuitiveness", and "isomorphism", supplemented by comprehensive documentation that progresses from basic to advanced, making the learning curve as smooth as possible. Most importantly, Eino adopts a clear layered design, with each layer having clear responsibilities and cohesive functionality, improving maintainability while better ensuring stability.
+### Reliable and Maintainable
-Eino Framework Structure Diagram:
+- Strong typing provides a “map” for maintainers; explicit types for components/lambdas/orchestrations
+- Compile-time graph type checks surface issues early, avoiding runtime surprises
+- Modular design, minimal dependencies per module; APIs are simple, intuitive, and consistent; layered architecture for stability and maintainability
### Practice-Driven
-The design and development process of the Eino framework is rooted in two cornerstones: "meeting real needs" and "practice-driven design". The evolution of functionality is closely integrated with the adoption process across ByteDance's business lines, always listening to developers' voices and validating design rationality through practical usage. For example, we received a requirement from Douyin "hoping to map and transmit data at field granularity within the graph", based on which we designed Workflow; listening to pain points from Doubao, we enhanced the Message struct that serves as model input/output types. In the future process of building an open-source ecosystem, we will continue to adhere to these principles, meeting the real needs of a broader user and developer base, and practicing and refining diligently on a larger scale.
+Design evolves from real needs and usage. Examples: Workflow designed for field-granularity mapping; `Message` structure enhanced based on Doubao feedback.
-
+
-### Tool Ecosystem
+### Tooling Ecosystem
-Link tracing, debugging, and visualization are three important auxiliary tools for orchestration engines. Eino has built-in tracing callbacks and integrates with the APMPlus and Langfuse platform. It also provides IDE plugins that allow real-time visualization of orchestrated graphs while coding, debugging execution, and even quickly building graphs through UI drag-and-drop, which can be exported as Eino code.
+Tracing, debugging, and visualization are first-class. Eino ships tracing callbacks and integrates with APMPlus and Langfuse. IDE plugins visualize graphs from code, enable debugging, and even generate Eino code from drag-and-drop orchestration.
## Quick Start
-For learning and using Eino, we provide a comprehensive Eino user manual to help everyone quickly understand the concepts in Eino and master the skills of developing AI applications based on Eino. Start trying it out through "[Eino: Quick Start](https://www.cloudwego.io/docs/eino/quick_start/)"!
+Explore the Eino User Manual to learn concepts and build AI apps with Eino:
-If you have any questions, you can communicate and provide feedback through the Lark group below or [Eino Issues](https://github.com/cloudwego/eino/issues)
+- Quick Start: `/docs/eino/quick_start/`
-## Related Links
+For questions, reach us via the community or [Eino Issues](https://github.com/cloudwego/eino/issues).
-Project repositories: [https://github.com/cloudwego/eino](https://github.com/cloudwego/eino), [https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext)
+## Links
-Project website: [https://www.cloudwego.io](https://www.cloudwego.io)
+- Project: https://github.com/cloudwego/eino, https://github.com/cloudwego/eino-ext
+- Website: https://www.cloudwego.io
-Scan the QR code to join the Lark community:
+Join the community:
diff --git a/content/en/docs/eino/overview/graph_or_agent.md b/content/en/docs/eino/overview/graph_or_agent.md
new file mode 100644
index 00000000000..404386ac3cf
--- /dev/null
+++ b/content/en/docs/eino/overview/graph_or_agent.md
@@ -0,0 +1,289 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: Agent or Graph? AI Application Path Analysis
+weight: 5
+---
+
+## Introduction: Two Coexisting AI Interaction Paradigms
+
+Many applications integrate different forms of AI capabilities, as shown below:
+
+
+
+
+
+
+```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 Application Forms"])
+ D{"Task Characteristics"}
+ A("Agent")
+ G("Graph")
+ A1("LLM decision center")
+ A2("Multi-turn interaction")
+ G1("Preset topology")
+ G2("Deterministic output")
+
+ S --> D
+ D -->|"Open or uncertain"| A
+ D -->|"Closed and deterministic"| 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
+```
+
+This article explores the differences and connections between Agent and Graph and proposes the best integration point: encapsulate Graphs as Agent Tools. It also offers recommended usage for Eino developers: https://github.com/cloudwego/eino.
+
+## Core Concepts Clarification
+
+### Basic Definitions
+
+- Graph: a developer-predefined flowchart with a clear topology. Nodes can be code functions, API calls, or LLMs; inputs and outputs are typically structured. The core trait is determinism — given the same input, the execution path and final result are predictable.
+- Agent: an entity centered on an LLM that can autonomously plan, decide, and execute tasks. It completes goals through dynamic interaction with the environment (Tools, users, other Agents) and exhibits uncertainty in behavior. The core trait is autonomy.
+- Tool: any external capability an Agent can call, typically a function or API encapsulating a specific capability. Tools themselves can be sync or async, stateful or stateless; they execute but do not make autonomous decisions.
+- Orchestration: the process of organizing and coordinating multiple compute units (nodes, Agents) to work together. In this article, it refers to predefined static flows via Graphs.
+
+### Deep Comparison
+
+
+| Dimension | Agent | Graph |
+| Core driver | LLM autonomy | Preset flow |
+| Input | Unstructured (text/images) | Structured data |
+| Deliverable | Process + result | Focused final result |
+| State | Long-lived across runs | Typically stateless per run |
+| Mode | Often async | Often sync |
+
+
+```mermaid
+flowchart TD
+ subgraph AIApp["AI Application"]
+ Agent["Agent (autonomy)"]
+ Graph["Graph (determinism)"]
+ end
+
+ subgraph CoreDrive["Source of Intelligence"]
+ LLM["LLM (decision/generation)"]
+ end
+
+ subgraph ExternalCap["External Capacity
(function/API)"]
+ Tool["External Capacity
(function/API)"]
+ end
+
+ Agent -- "Source of drive" --> LLM
+ Graph -- "Contains node" --> LLM
+ Agent -- "Tool call" --> Tool
+ Graph -- "Contains node" --> Tool
+
+ classDef agent fill:#EAE2FE,stroke:#000000
+ classDef graphClass fill:#F0F4FC,stroke:#000000
+ classDef llm fill:#FEF1CE,stroke:#000000
+ classDef tool fill:#DFF5E5,stroke:#000000
+
+ class Agent agent
+ class Graph graphClass
+ class LLM llm
+ class Tool tool
+```
+
+## Historical Perspective: From Determinism to Autonomy
+
+When LangChain first launched in 2022, the LLM world’s API paradigm was OpenAI’s Completions API — a simple “text in, text out” API. LangChain’s early slogan was “connect LLMs to external sources of computation and data”. A typical Chain looked like:
+
+### Pain Points from Mismatch
+
+- Deliverable mismatch: Orchestrated ReAct Agents output only the final result, while applications often care about intermediate process data. Callbacks can extract it — complete enough, but still a patch.
+
+```mermaid
+flowchart LR
+ A[ReAct Agent]
+ P@{ shape: processes, label: "Full-process data" }
+ A--o|needs|P
+
+ G[Graph]
+ F[Final result]
+ G-->|mainline output,
but overshadowed by side-channel|F
+
+ G-.->|side-channel extraction|P
+```
+
+- Runtime mode mismatch: Since orchestration runs synchronously, to “quickly render LLM replies”, nodes inside ReAct Agent orchestration are pushed to be “fast”, mainly within the branching that checks whether the LLM output contains a ToolCall — ideally decide from the first frames. This logic can be customized (e.g., “read streaming output until Content appears, then decide no ToolCall”), but sometimes it still fails, and callbacks are used to manually switch sync→async.
+
+```mermaid
+flowchart LR
+ L[LLM node]
+ S@{ shape: processes, label: "Streaming content"}
+ L-->|emits|S
+
+ B{Contains
ToolCall?}
+ D@{ shape: processes, label: "Streaming to screen"}
+
+ B-->|No, render|D
+ S-->|frame-wise check|B
+```
+
+## Exploring Integration Paths: Agent and Graph in Eino
+
+### Multi-Agent and Orchestration
+
+- Hierarchical invocation (Agent as Tool): the most common pattern (see Google ADK’s definitions and examples: https://google.github.io/adk-docs/agents/multi-agents/#c-explicit-invocation-agenttool and https://google.github.io/adk-docs/agents/multi-agents/#hierarchical-task-decomposition). A top-level Agent delegates specific sub-tasks to a special Tool Agent. For example, the main Agent talks to the user; when code execution is needed, it calls a “code executor Agent”. Sub-Agents are usually stateless, do not share memory with the main Agent, and the interaction is a simple function call. There is only one relationship: caller and callee. Conclusion: the Agent-as-Tool multi-agent mode is not the “node handoff” relationship in Graph orchestration.
+
+```mermaid
+flowchart LR
+ subgraph Main Agent
+ L[Main Agent LLM]
+ T1[Sub Agent 1]
+ T2[Sub Agent 2]
+
+ L-->|Tool Call|T1
+ L-->|Tool Call|T2
+ end
+```
+
+- Prebuilt flows: for mature collaboration modes such as Plan–Execute–Replan (see LangGraph tutorial: https://langchain-ai.github.io/langgraph/tutorials/plan-and-execute/plan-and-execute/), Agent roles and interaction order are fixed. Frameworks (e.g., Eino adk) expose these patterns as prebuilt multi-agent modes; developers use them directly without wiring sub-Agents manually. Conclusion: Graph orchestration is an implementation detail inside the prebuilt mode and not visible to developers.
+
+```mermaid
+flowchart LR
+ subgraph Plan-Execute-Replan
+ P[Planner]
+ E[Executor]
+ R[Replanner]
+ P-->E
+ E-->R
+ R-->E
+ end
+
+ user -->|Use as a whole| Plan-Execute-Replan
+```
+
+- Dynamic collaboration: in more complex scenarios, collaboration is dynamic (see Google ADK definitions and examples: https://google.github.io/adk-docs/agents/multi-agents/#b-llm-driven-delegation-agent-transfer and https://google.github.io/adk-docs/agents/multi-agents/#coordinatordispatcher-pattern), possibly involving bidding, voting, or runtime decisions by a coordinator Agent. The relationship is Agent transfer — full handoff of control from A to B — similar to node handoff in Graphs. But here it can be fully dynamic: not only which Agent to transfer to, but how that decision is made is not preset by developers; it is runtime LLM behavior. This contrasts sharply with the static determinism of Graph orchestration. Conclusion: dynamic multi-agent collaboration is fundamentally different from static Graph orchestration and is better solved at the Agent framework layer.
+
+```mermaid
+flowchart LR
+ A[Agent 1]
+ B[Agent 2]
+ C[Agent 3]
+
+ A-.->|Dynamic handoff|B-.->|Dynamic handoff|C
+```
+
+### Agent as a Graph Node
+
+Agents rely heavily on conversation history (memory) and emit asynchronous, whole-process outputs — this makes them ill-suited as strict Graph nodes that depend solely on upstream structured outputs and synchronous execution.
+
+```mermaid
+flowchart LR
+ U[Upstream Node]
+ A[Agent Node]
+ D[Downstream Node]
+ M[Memory]
+
+ U-->|Not all inputs
come from upstream|A
+ M-.->|External state injection|A
+ A-->|Whole-process data
(to user or LLM)|D
+```
+
+Conclusion: treating an Agent as a Graph node is inefficient; prefer LLM nodes or plugin business logic into Agents.
+
+### The Integration Path: Encapsulate Graphs as Agent Tools
+
+The sweet spot: encapsulate structured Graphs as high-quality Tools for Agents. Most Graphs fit Tool semantics well — structured inputs/outputs, stateless per run, synchronous call surface. Expose Graphs as Tools so agents can call deterministic capabilities at the right time, gaining graph benefits (rich component ecosystem, orchestration, streaming, callbacks, interrupt/resume) within agent flows.
+
+Thus, “Agent” and “Graph” achieve dialectical unity.
+
+```go
+// NewInvokableGraphTool converts ANY Graph to the `InvokableTool` interface.
+func NewInvokableGraphTool[I, O any](graph compose.Graph[I, O],
+ name, desc string,
+ opts ...compose.GraphCompileOption,
+) (*InvokableGraphTool[I, O], error) {
+ tInfo, err := utils.GoStruct2ToolInfo[I](name, desc)
+ if err != nil { return nil, err }
+ return &InvokableGraphTool[I, O]{ graph: graph, compileOptions: opts, tInfo: tInfo }
+}
+
+func (g *InvokableGraphTool[I, O]) InvokableRun(ctx context.Context, input string,
+ opts ...tool.Option) (output string, err error) {
+ // trigger callbacks where needed
+ // compile the graph
+ // convert input string to I
+ // run the graph
+ // handle interrupt
+ // convert output O to string
+}
+
+func (g *InvokableGraphTool[I, O]) Info(_ context.Context) (*schema.ToolInfo, error) {
+ return g.tInfo, nil
+}
+```
+
+### Graph vs Tool Traits
+
+
+| Dimension | Graph | Tool |
+| Input | Structured | Structured |
+| Deliverable | Focused final result | Focused final result |
+| State | Stateless per run | Stateless (from LLM’s view) |
+| Mode | Synchronous as a whole | Synchronous from LLM call semantics |
+
+
+### Graph–Tool–Agent Relationship
+
+```mermaid
+flowchart TD
+ subgraph Agent ["Agent"]
+ A["LLM decision"] --> B{"Call a tool?"}
+ B -- "Yes" --> C["Tool: my_graph_tool"]
+ end
+
+ subgraph Tool ["Tool"]
+ C -- "Encapsulate" --> D["Graph: my_graph"]
+ end
+
+ subgraph Graph ["Graph"]
+ D -- "Execute" --> E["Node 1"]
+ E --> F["Node 2"]
+ F --> G["Return result"]
+ end
+
+ G -- "Output" --> C
+ C -- "Result" --> A
+
+ classDef agent fill:#EAE2FE,stroke:#000000
+ classDef tool fill:#DFF5E5,stroke:#000000
+ classDef graphGroup fill:#F0F4FC,stroke:#000000
+ class A,B agent
+ class C tool
+ class D,E,F,G graphGroup
+```
+
+Graph–Tool–Agent relationship diagram
+
+## Conclusion
+
+Agents and Graphs are complementary paradigms:
+
+- Graphs provide reliable, deterministic AI functionality — ideal for “feature buttons” and stable backend services.
+- Agents deliver autonomy and interactive intelligence — ideal for assistants that plan, act, and collaborate.
+
+The best integration is Graph-as-Tool: build structured Graph capabilities (e.g., full RAG pipeline, analytics flows) and expose them as atomic Agent tools that the agent calls at the right time.
+
+Recommended posture for Eino developers:
+
+- Use eino-compose to write Graphs and encapsulate deterministic business logic.
+- Use eino-adk to build Agents with thinking/planning/interaction.
+- Expose the former as tools to the latter to achieve 1+1>2.
diff --git a/content/en/docs/eino/quick_start/_index.md b/content/en/docs/eino/quick_start/_index.md
index 00a454068d3..70e94488be6 100644
--- a/content/en/docs/eino/quick_start/_index.md
+++ b/content/en/docs/eino/quick_start/_index.md
@@ -1,50 +1,40 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: 'Eino: Quick start'
+title: 'Eino: Quick Start'
weight: 2
---
-## **Brief Description**
+## Overview
-Eino offers various component abstractions for AI application development scenarios and provides multiple implementations, making it **very simple** to quickly develop an application using Eino. This directory provides several of the most common AI-built application examples to help you get started with Eino quickly.
+Eino offers component abstractions tailored for common AI application scenarios, with multiple implementations available. Getting a simple application up and running with Eino is **very straightforward**. This section presents a few of the most typical AI application examples to help you get productive quickly.
-These small applications are only for getting started quickly. For a more detailed introduction and examples of individual capabilities, please refer to specialized documents such as [Eino: Components](/docs/eino/core_modules/components) and [Eino: Chain & Graph Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration).
+These small examples are meant for quick onboarding. For deeper dives into specific capabilities and extended samples, see [Components](/docs/eino/core_modules/components) and [Orchestration](/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction).
-> 💡
-> Usage instructions for Fornax Trace (Fornax Observation):
->
-> Eino's observation capability is implemented by default based on [Fornax Trace](https://fornax.bytedance.net/space) (the following examples do not include the code for enabling observation)
->
-> - When using Chain/Graph orchestration, a global EinoCallback aspect can be registered to report the input and output related information of the node in the form of Trace. For details on the usage, please refer to: [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual).
-> - When only using Eino components without using Eino orchestration, it is necessary to manually inject Callback Manager information through callbacks.InitCallbacks when calling the component to enable the Trace reporting capability of the component aspect (only for components that have implemented the component aspect). For details on the usage, please refer to: [Eino: Callback Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual).
+## Quick Start Examples
-## **Quick Start Examples**
+### Example: Minimal LLM Application
-### **Example: Simplest LLM Application**
+The most basic pattern in LLM applications is a `prompt + chat model`, which is also the primary capability offered by many AI platforms. You can define a `System Prompt` to constrain the model’s behavior (for example, “You are acting as role XXX”). In this example, you can combine Eino’s `PromptTemplate` and `ChatModel` components to build a role-playing application.
-In AI applications, the most basic scenario is the prompt + chat model scenario, which is also the most important feature provided by various AI application platforms on the Internet. You can define a `System Prompt` to constrain LLM's response logic, such as "You are playing the role of XXX". In this example, you can use Eino's `PromptTemplate` component and `ChatModel` component to build a role-playing application.
+- [Implement a minimal LLM application — ChatModel](/docs/eino/quick_start/simple_llm_application)
-- [Implement an easy LLM application](/docs/eino/quick_start/simple_llm_application)
+### Example: Build an Agent
-### **Example: Create an Agent**
+The LLM is the brain of an AI application: it understands natural language and produces responses. A text-only LLM accepts text and returns text. When you want the model to fetch information or perform actions, you introduce `Tools`. With tools, the model gains “hands” that can interact with existing IT infrastructure. For example, “call an HTTP API to check the weather, then suggest what to wear” requires the model to call a “search tool”.
-An LLM is the brain of AI, whose core is understanding natural language and responding to it. A (text) LLM itself can only receive a piece of text and then output a piece of text. When you want the LLM to use some tools to obtain the required information or perform some actions on its own, you need to use `Tool`. An LLM with Tool is like having hands and feet, which can interact with the existing IT infrastructure, such as "calling an HTTP interface to check the weather and then reminding you what clothes to wear based on the weather". This requires the LLM to call a "search tool" to query information.
+We call the overall system that decides when to call specific tools based on model outputs an “agent”.
-We usually refer to the system that can call relevant tools based on the LLM's output as an "Agent".
+In Eino, you can implement an agent with `ChatModel + ToolsNode`, or use the built-in `react agent` and `multi agent` packages.
-In Eino, you can implement an Agent using ChatModel + ToolsNode alone, or you can use packaged `react agent` and `multi agent`.
+In this example, we’ll use the ReAct agent to build an agent that interacts with the real world.
-In this example, we will use a react agent to build an agent that can interact with the real world.
+- [Agent — Give your LLM hands](/docs/eino/quick_start/agent_llm_with_tools)
-- [Agent-Enable LLM to have hands](/docs/eino/quick_start/agent_llm_with_tools)
+## Next Steps
-## **Next Steps**
+- Understand Eino’s core modules and concepts: [Eino: Core Modules](/docs/eino/core_modules). This is the key knowledge to fluently develop applications with Eino.
+- Eino embraces an open ecosystem and provides numerous integration components: [Eino: Ecosystem Integration](/docs/eino/ecosystem_integration). Use these components to quickly assemble your business applications.
-- Understand the core modules and concepts of Eino: [Eino: Core Modules](/docs/eino/core_modules), which is crucial information for mastering the use of Eino for application development.
-- Eino maintains an open ecosystem stance and provides a large number of ecosystem integration components: [Eino: ecosystem](/docs/eino/ecosystem_integration/). You can use these components to quickly build your own business applications.
-
-If you find this project is good and helpful, please give [Eino](https://github.com/cloudwego/eino) a star! Your support will be our greatest encouragement!
-If you have any questions or suggestions, feel free to leave a message in [GitHub Issues](https://github.com/cloudwego/eino/issues).
diff --git a/content/en/docs/eino/quick_start/agent_llm_with_tools.md b/content/en/docs/eino/quick_start/agent_llm_with_tools.md
index 447e0ef19b2..2bbc29f001f 100644
--- a/content/en/docs/eino/quick_start/agent_llm_with_tools.md
+++ b/content/en/docs/eino/quick_start/agent_llm_with_tools.md
@@ -1,42 +1,42 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: Agent-Enable LLM to have hands
+title: Agent — Give Your LLM Hands
weight: 2
---
-## **What is an Agent**
+## What Is an Agent?
-An Agent (intelligent agent) is a system that can perceive the environment and take actions to achieve specific goals. In AI applications, an Agent can autonomously complete complex tasks by combining the understanding capabilities of large language models (LLMs) with the execution capabilities of predefined tools. This will be the main form in which AI is applied to daily life and production in the future.
+An Agent is a system that perceives its environment and takes actions to achieve a goal. In AI applications, agents combine the language understanding of LLMs with tool execution, enabling them to autonomously complete complex tasks — a key form factor for how AI integrates into everyday work and life.
> 💡
-> For code snippets exemplified in this article, see: [eino-examples/quickstart/todoagent](https://github.com/cloudwego/eino-examples/blob/master/quickstart/todoagent/main.go)
+> Example code snippets: [eino-examples/quickstart/todoagent](https://github.com/cloudwego/eino-examples/blob/master/quickstart/todoagent/main.go)
-## **Core Components of an Agent**
+## Core Components of an Agent
-In Eino, to implement an Agent, you mainly need two core components: ChatModel and Tool.
+In Eino, an agent typically consists of two core parts: a `ChatModel` and one or more `Tools`.
-### **ChatModel**
+### ChatModel
-ChatModel is the brain of the Agent, processing the user's natural language input through powerful language understanding capabilities. When a user makes a request, ChatModel deeply understands the user's intent, analyzes the task requirements, and decides whether specific tools need to be called to complete the task. When tools need to be used, it can accurately choose the appropriate tools and generate the correct parameters. Moreover, ChatModel can convert the results of tool execution into natural language responses that are easy for users to understand, achieving smooth human-computer interaction.
+`ChatModel` is the agent’s brain. It processes the user’s natural language input, understands intent, analyzes requirements, and decides whether a tool is needed. When tools are required, it selects the right tool with the right parameters and converts tool outputs back into natural-language responses.
-> For more detailed information on ChatModel, refer to: [Eino: ChatModel guide](/docs/eino/core_modules/components/chat_model_guide)
+> More about ChatModel: [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide)
-### **Tool**
+### Tool
-Tool is the executor of the Agent, providing specific functionality implementations. Each Tool has clear functional definitions and parameter specifications, enabling ChatModel to call them accurately. Tools can achieve various functions, encapsulating everything from simple data operations to complex external service calls.
+`Tool` is the agent’s executor. Each tool has a clear function definition and parameter schema, allowing the `ChatModel` to call it accurately. Tools can wrap anything from simple data ops to sophisticated external service calls.
-> For more detailed information on Tool and ToolsNode, refer to: [Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)
+> More about tools and ToolsNode: [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide)
-## **Implementation of Tools**
+## Implementing Tools
-In Eino, we provide multiple ways to implement a tool. Below, we'll illustrate this with an example of a Todo management system.
+Eino offers multiple ways to implement tools. We illustrate with a simple Todo management system.
-### Way 1: Using NewTool to Construct
+### Approach 1: Build with `NewTool`
-This method is suitable for simple tool implementations, where a tool is created by defining the tool information and handling function:
+This is ideal for simpler tools: define tool metadata and a handler function.
```go
import (
@@ -47,14 +47,14 @@ import (
"github.com/cloudwego/eino/schema"
)
-// Handling function
+// Handler
func AddTodoFunc(_ context.Context, params *TodoAddParams) (string, error) {
- // Mock processing logic
+ // Mock
return `{"msg": "add todo success"}`, nil
}
func getAddTodoTool() tool.InvokableTool {
- // Tool information
+ // Tool metadata
info := &schema.ToolInfo{
Name: "add_todo",
Desc: "Add a todo item",
@@ -75,16 +75,16 @@ func getAddTodoTool() tool.InvokableTool {
}),
}
- // Use NewTool to create the tool
+ // Build with NewTool
return utils.NewTool(info, AddTodoFunc)
}
```
-While this method is straightforward, it has a notable disadvantage: the parameter information (ParamsOneOf) is manually defined in the ToolInfo and is separate from the actual parameter structure (TodoAddParams). This not only causes code redundancy but also requires simultaneous modifications in two places when parameters change, which can easily lead to inconsistencies and make maintenance more cumbersome.
+This approach is straightforward but has a drawback: parameter descriptions (`ParamsOneOf`) are separate from the actual parameter struct (`TodoAddParams`). Changes require updating both, risking inconsistency.
-### Way 2: Build Using InferTool
+### Approach 2: Build with `InferTool`
-This method is more concise, defining parameter information through the struct's tag, allowing the parameter struct and description information to be in sync without maintaining two sets of information:
+This is more concise. Use struct tags to define parameter metadata so the description and struct share the same source.
```go
import (
@@ -102,22 +102,22 @@ type TodoUpdateParams struct {
Done *bool `json:"done,omitempty" jsonschema:"description=done status"`
}
-// Handler function
+// Handler
func UpdateTodoFunc(_ context.Context, params *TodoUpdateParams) (string, error) {
- // Mock processing logic
+ // Mock
return `{"msg": "update todo success"}`, nil
}
-// Create tool using InferTool
+// Build tool with InferTool
updateTool, err := utils.InferTool(
"update_todo", // tool name
- "Update a todo item, eg: content, deadline...", // tool description
+ "Update a todo item, eg: content,deadline...", // description
UpdateTodoFunc)
```
-### Way 3: Implementing the Tool Interface
+### Approach 3: Implement the Tool Interface
-For scenarios requiring more custom logic, you can create by implementing the Tool interface:
+For advanced scenarios, implement the `Tool` interface.
```go
import (
@@ -144,31 +144,32 @@ func (lt *ListTodoTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
}
func (lt *ListTodoTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
- // Mock invocation logic
- return `{"todos": [{"id": "1", "content": "Prepare the Eino project presentation by December 10, 2024", "started_at": 1717401600, "deadline": 1717488000, "done": false}]}`, nil
+ // Mock
+ return `{"todos": [{"id": "1", "content": "Prepare Eino demo slides before 2024-12-10", "started_at": 1717401600, "deadline": 1717488000, "done": false}]}` , nil
}
```
-### Way 4: Using Officially Packaged Tools
+### Approach 4: Use Official Tools
-In addition to implementing tools yourself, we also provide many ready-to-use tools. These tools have been fully tested and optimized and can be directly integrated into your Agent. For example, take the duckduckgo Search tool:
+Beyond custom tools, Eino provides many well-tested, ready-to-use tools. For example, DuckDuckGo Search:
```go
import (
"github.com/cloudwego/eino-ext/components/tool/duckduckgo"
)
-// Create the duckduckgo Search tool
+
+// Create DuckDuckGo Search tool
searchTool, err := duckduckgo.NewTool(ctx, &duckduckgo.Config{})
```
-Using the tools provided by eino-ext not only avoids the workload of redundant development but also ensures the stability and reliability of the tools. These tools have been thoroughly tested and are continually maintained, allowing them to be directly integrated into the project for use.
+Using tools from `eino-ext` avoids reinvention and ensures reliability — they’re maintained and continuously improved.
-## **Using Chain to Construct an Agent**
+## Build an Agent with Chain
-When constructing an Agent, the ToolsNode is a core component responsible for managing and executing tool invocations. ToolsNode can integrate multiple tools and provides a unified invocation interface. It supports both synchronous invocation (Invoke) and streaming invocation (Stream), allowing flexible handling of different types of tool execution requirements.
+`ToolsNode` is a core component for agents, managing tool invocation. It can host multiple tools and supports both synchronous (`Invoke`) and streaming (`Stream`) execution.
-To create a ToolsNode, you need to provide a configuration for a list of tools:
+To create a `ToolsNode`, provide a tool list configuration:
```go
import (
@@ -179,12 +180,12 @@ import (
)
conf := &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{tool1, tool2}, // Tools can be InvokableTool or StreamableTool
+ Tools: []tool.BaseTool{tool1, tool2}, // tools can be InvokableTool or StreamableTool
}
toolsNode, err := compose.NewToolNode(context.Background(), conf)
```
-Below is a complete example of an Agent that uses OpenAI's ChatModel in conjunction with the aforementioned Todo tools:
+Below is a complete agent example using OpenAI’s `ChatModel` and the Todo tools above:
```go
import (
@@ -202,10 +203,10 @@ import (
func main() {
// Initialize tools
todoTools := []tool.BaseTool{
- getAddTodoTool(), // NewTool construction
- updateTool, // InferTool construction
- &ListTodoTool{}, // Implements Tool interface
- searchTool, // Officially packaged tool
+ getAddTodoTool(), // NewTool
+ updateTool, // InferTool
+ &ListTodoTool{}, // Implement Tool interface
+ searchTool, // Official tool
}
// Create and configure ChatModel
@@ -216,7 +217,7 @@ func main() {
if err != nil {
log.Fatal(err)
}
- // Get tool information and bind to ChatModel
+ // Bind tool infos to ChatModel
toolInfos := make([]*schema.ToolInfo, 0, len(todoTools))
for _, tool := range todoTools {
info, err := tool.Info(ctx)
@@ -230,6 +231,7 @@ func main() {
log.Fatal(err)
}
+
// Create tools node
todoToolsNode, err := compose.NewToolNode(context.Background(), &compose.ToolsNodeConfig{
Tools: todoTools,
@@ -238,13 +240,13 @@ func main() {
log.Fatal(err)
}
- // Build the complete processing chain
+ // Build chain
chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
chain.
AppendChatModel(chatModel, compose.WithNodeName("chat_model")).
AppendToolsNode(todoToolsNode, compose.WithNodeName("tools"))
- // Compile and run the chain
+ // Compile and run
agent, err := chain.Compile(ctx)
if err != nil {
log.Fatal(err)
@@ -254,45 +256,45 @@ func main() {
resp, err := agent.Invoke(ctx, []*schema.Message{
{
Role: schema.User,
- Content: "Add a TODO for learning Eino and search for the repository address of cloudwego/eino",
+ Content: "Add a TODO to learn Eino and search for the cloudwego/eino repo URL",
},
})
if err != nil {
log.Fatal(err)
}
- // Output the result
+ // Print output
for _, msg := range resp {
fmt.Println(msg.Content)
}
}
```
-This example assumes that the ChatModel will always make a tool invocation decision.
+This example assumes the `ChatModel` will decide to make tool calls when appropriate.
-## **Creating Agents Using Other Methods**
+## Other Ways to Build Agents
-In addition to the aforementioned Chain/Graph-based agents, Eino also provides encapsulation of common Agent models.
+Beyond Chain/Graph-based agents, Eino provides ready-made agent patterns.
-### **ReAct Agent**
+### ReAct Agent
-ReAct (Reasoning + Acting) Agent combines reasoning and action capabilities through a think-act-observe loop to solve complex problems. It can conduct deep reasoning while performing tasks and adjust strategies based on observations, making it particularly suitable for complex scenarios requiring multi-step reasoning.
+ReAct (Reasoning + Acting) combines deep reasoning with action through a think–act–observe loop. It’s well-suited for multi-step reasoning in complex tasks.
-> For more details on react agent, see: [Eino: React Agent Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual)
+> Learn more: [Eino: ReAct Agent Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual)
-### **Multi Agent**
+### Multi Agent
-A Multi Agent system consists of multiple agents working in cooperation, each with its own specific responsibilities and expertise. Through interaction and collaboration among agents, it can handle more complex tasks and achieve division of labor and cooperation. This approach is particularly suitable for scenarios that require the integration of knowledge from multiple specialized fields.
+Multi-agent systems coordinate multiple agents, each with distinct responsibilities and expertise. Through interaction and collaboration, they can tackle complex tasks requiring multiple areas of knowledge.
-> For more details on multi agent, see: [Eino Tutorial: Host Multi-Agent ](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
+> Learn more: [Eino Tutorial: Host Multi-Agent](/docs/eino/core_modules/flow_integration_components/multi_agent_hosting)
-## **Conclusion**
+## Summary
-This document introduces the basic methods of constructing agents using the Eino framework. By using different methods such as Chain, Tool Calling, and ReAct, we can flexibly build AI Agents according to actual needs.
+This article introduced core approaches to building agents with Eino. Using chains, tool calling, or ReAct patterns, you can flexibly construct AI agents to meet practical needs.
-Agents are an important direction in the development of AI technology. They can not only understand user intentions but also take proactive actions to complete complex tasks by calling various tools. As the capabilities of LLMs continue to improve, agents will play an increasingly important role in the future, becoming a crucial bridge connecting AI and the real world. We hope that Eino can provide users with stronger and more user-friendly Agent construction solutions, promoting more innovation in applications based on Agents.
+Agents are a vital direction in AI — they understand user intent and take action by calling tools to accomplish complex tasks. As LLMs advance, agents will increasingly bridge AI and the real world. We hope Eino helps you build powerful, user-friendly agents and inspires new agent-driven applications.
-## **Related Reading**
+## Related Reading
- Quick Start
- - [Implement an easy LLM application](/docs/eino/quick_start/simple_llm_application)
+ - [Build a Minimal LLM Application — ChatModel](/docs/eino/quick_start/simple_llm_application)
diff --git a/content/en/docs/eino/quick_start/simple_llm_application.md b/content/en/docs/eino/quick_start/simple_llm_application.md
index 821627050a9..a1dad7817f5 100644
--- a/content/en/docs/eino/quick_start/simple_llm_application.md
+++ b/content/en/docs/eino/quick_start/simple_llm_application.md
@@ -1,56 +1,57 @@
---
Description: ""
-date: "2025-02-21"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: Implement an easy LLM application
+title: Build a Minimal LLM Application
weight: 1
---
-This guide will help you get started quickly with building a simple LLM application using the ChatModel in the Eino framework. We will demonstrate how to use ChatModel through an example of creating a "programmer encourager".
+This guide helps you quickly get started building a simple LLM application with Eino’s `ChatModel`. We’ll implement a “Programmer Encouragement Assistant” to demonstrate how to use `ChatModel` effectively.
> 💡
-> Code snippets from the examples in this article can be found here: [flow/eino-examples/quickstart/chat](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat)
+> Code snippets in this guide: [eino-examples/quickstart/chat](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat)
-## **Introduction to ChatModel**
+## ChatModel Overview
-ChatModel is an abstraction of the conversational large model within the Eino framework. It provides a unified interface to interact with different large model services (such as OpenAI, Ollama, etc.).
+`ChatModel` is Eino’s abstraction over chat-oriented LLMs. It provides a unified interface to interact with various model providers such as OpenAI and Ollama.
-> For a more detailed introduction to the component, refer to: [Eino: ChatModel guide](/docs/eino/core_modules/components/chat_model_guide)
+> For a detailed component guide, see: [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide)
-## **Structure and Usage of Messages**
+## Message Structure and Usage
-In Eino, a conversation is represented through `schema.Message`, which is an abstract definition of a conversation message by Eino. Each message includes the following important fields:
+In Eino, conversations are represented by `schema.Message`, an abstraction for a single chat message. Each message includes:
-- `Role`: The role of the message, which can be:
- - `system`: System instructions used to set the model's behavior and role
- - `user`: User's input
- - `assistant`: The model's reply
- - `tool`: The result of tool invocation
-- `Content`: The specific content of the message
+- `Role`: one of
+ - `system`: a system directive defining behavior and role
+ - `user`: user input
+ - `assistant`: model response
+ - `tool`: tool call result
+- `Content`: the message content
-## **Implementing a Programmer Encourager**
+## Implementing the Encouragement Assistant
-Let's learn how to use ChatModel by implementing a Programmer Encourager. This assistant can not only provide technical advice but also offer psychological support when programmers feel down.
+We’ll build a Programmer Encouragement Assistant that offers technical suggestions and supportive messaging when the developer is feeling down.
-### **Creating Dialogue Templates and Generating Messages**
+### Create a Template and Generate Messages
-Eino provides powerful templating functions to construct messages to be input into the LLM:
+Eino provides versatile templating for constructing messages sent to the model:
-1. Template Rendering, supporting three template formats:
- - FString: Python-style simple string formatting (e.g., "Hello, {name}!")
- - Jinja2: Jinja2-style templates supporting rich expressions (e.g., "Hello, {{name}}!")
- - GoTemplate: Go language built-in text/template format (e.g., "Hello, {{.name}}!")
-2. Message Placeholder: Supports inserting a set of messages (such as conversation history)
+1. Template rendering with three formats:
+ - `FString`: Python-style string interpolation (e.g., "Hello, {name}!")
+ - `Jinja2`: Jinja2-style templating with rich expressions (e.g., "Hello, {{name}}!")
+ - `GoTemplate`: Go’s `text/template` (e.g., "Hello, {{.name}}!")
+2. Message placeholders: insert a list of messages (e.g., conversation history)
```go
-// optional=false indicates a required message list. An error will occur if the corresponding variable is not found in the template input
+// optional=false means the message list is required;
+// an absent variable in template input causes an error
schema.MessagesPlaceholder("chat_history", false)
```
-> For a more detailed component introduction, refer to: [Eino: ChatTemplate guide](/docs/eino/core_modules/components/chat_template_guide)
+> For details, see: [Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide)
-Below is the complete code for creating and using a dialogue template with FString format + message placeholder:
+Complete example using `FString` format and a messages placeholder:
```go
// eino-examples/quickstart/chat/template.go
@@ -62,47 +63,47 @@ import (
"github.com/cloudwego/eino/schema"
)
-// Creating a template using FString format
+// Create a template using FString
template := prompt.FromMessages(schema.FString,
// System message template
- schema.SystemMessage("You are a {role}. You need to answer questions in a {style} manner. Your goal is to help programmers maintain a positive and optimistic attitude, providing technical advice while also caring about their mental health."),
+ schema.SystemMessage("You are a {role}. Please respond in a {style} tone. Your goal is to keep developers positive and optimistic while offering technical advice and caring about their mental well-being."),
- // Insert required conversation history (leave this empty for new conversations)
+ // Insert conversation history (omit for a new conversation)
schema.MessagesPlaceholder("chat_history", true),
// User message template
schema.UserMessage("Question: {question}"),
)
-// Generating messages using the template
+// Render messages from the template
messages, err := template.Format(context.Background(), map[string]any{
- "role": "Programmer Encourager",
+ "role": "Programmer Encouragement Assistant",
"style": "positive, warm, and professional",
- "question": "My code keeps throwing errors, I feel so frustrated. What should I do?",
- // Conversation history (this example simulates two rounds of conversation history)
+ "question": "My code keeps throwing errors and I feel frustrated. What should I do?",
+ // Dialogue history (simulate two rounds)
"chat_history": []*schema.Message{
- schema.UserMessage("Hello"),
- schema.AssistantMessage("Hey! I'm your Programmer Encourager! Remember, every great programmer grows through debugging. How can I assist you today?", nil),
- schema.UserMessage("I feel like the code I write is terrible"),
- schema.AssistantMessage("Every programmer has gone through this stage! The important thing is that you are constantly learning and improving. Let's take a look at the code together; I'm sure that through refactoring and optimization, it will get better. Remember, Rome wasn't built in a day, and code quality is improved through continuous refinement.", nil),
+ schema.UserMessage("Hi"),
+ schema.AssistantMessage("Hey! I’m your encouragement assistant! Remember, every great engineer grows through debugging. How can I help?", nil),
+ schema.UserMessage("I think my code is terrible"),
+ schema.AssistantMessage("Every developer feels that way at times! What matters is continuous learning and improvement. Let’s review the code together — I’m confident with refactoring and optimization it’ll get better. Remember, Rome wasn’t built in a day; code quality improves with continuous effort.", nil),
},
})
```
-### Creating ChatModel
+### Create a ChatModel
-ChatModel is one of the core components in the Eino framework, providing a unified interface to interact with various LLMs. Eino currently supports implementations of the following LLMs:
+`ChatModel` is one of Eino’s core components, providing a unified interface across LLM providers. Eino currently supports:
-- OpenAI: Support for models such as GPT-3.5/GPT-4 (also supports OpenAI services provided by Azure)
-- Ollama: Support for locally deployed open-source models
-- Ark: Model services on the Volcano Engine (e.g., ByteDance's Doubao LLM)
-- More models are being supported
+- OpenAI: GPT-3.5 / GPT-4 family (including Azure OpenAI)
+- Ollama: locally hosted open-source models
+- Ark: models on Volcano Engine (e.g., ByteDance’s Doubao)
+- More providers are coming
-> For supported models, refer to: [Eino: Ecosystem](/docs/eino/ecosystem_integration/chat_model/)
+> Supported models: [Eino: Ecosystem Integration](/docs/eino/ecosystem_integration)
-Below, we demonstrate how to create and use ChatModel with OpenAI and Ollama as examples:
+Examples using OpenAI and Ollama:
-#### **OpenAI (select either this or Ollama below)**
+#### OpenAI (use either this or Ollama below)
```go
// eino-examples/quickstart/chat/openai.go
@@ -114,16 +115,16 @@ import (
)
chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- Model: "gpt-4o", // Model version used
+ Model: "gpt-4o", // model version
APIKey: os.Getenv("OPENAI_API_KEY"), // OpenAI API key
})
```
-> For detailed information on OpenAI ChatModel, refer to: [ChatModel - OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
+> Details: [ChatModel — OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
-#### **Ollama (select either this or OpenAI above)**
+#### Ollama (use either this or OpenAI above)
-Ollama supports running open-source models locally, making it suitable for scenarios requiring data privacy or offline usage.
+Ollama supports running open-source models locally — ideal for privacy-sensitive or offline scenarios.
```go
// eino-examples/quickstart/chat/ollama.go
@@ -132,19 +133,20 @@ import (
"github.com/cloudwego/eino-ext/components/model/ollama"
)
+
chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434", // Ollama service address
- Model: "llama2", // Model name
+ BaseURL: "http://localhost:11434", // Ollama server
+ Model: "llama2", // model name
})
```
-> For information on OpenAI, refer to: [ChatModel - Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
+> Details: [ChatModel — Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
-Regardless of the implementation used, ChatModel provides a consistent interface, allowing you to easily switch between different models without modifying a large amount of code.
+Thanks to Eino’s unified `ChatModel` abstraction and ready-to-use implementations, your business logic remains focused and insulated from provider specifics. You can swap models without broad code changes.
-### **Running ChatModel**
+### Run the ChatModel
-After obtaining the input messages and initializing the ChatModel instance in the previous steps, you can start running the ChatModel. Eino ChatModel provides two running modes: generating full messages (generate) and streaming messages (stream):
+With messages and a configured model, you can now run the `ChatModel`. Eino exposes two modes: generate a full message at once (`Generate`) or stream the message incrementally (`Stream`).
```go
// eino-examples/quickstart/chat/generate.go
@@ -155,15 +157,15 @@ After obtaining the input messages and initializing the ChatModel instance in th
/*** create chat model
* chatModel, err := xxx
-*/
+*/
result, err := chatModel.Generate(ctx, messages)
streamResult, err := chatModel.Stream(ctx, messages)
```
-In practical applications, many scenarios require using streaming responses, especially to "enhance user experience": the stream running mode lets the ChatModel provide typewriter-like output effects, allowing users to receive model responses earlier.
+Streaming is essential for user experience: `Stream` allows the model to emit tokens progressively like a typewriter, giving users timely feedback.
-The handling of streaming output in Eino is as follows:
+Eino’s streaming consumption looks like this:
```go
// eino-examples/quickstart/chat/stream.go
@@ -181,7 +183,7 @@ func reportStream(sr *schema.StreamReader[*schema.Message]) {
i := 0
for {
message, err := sr.Recv()
- if err == io.EOF { // End of stream output
+ if err == io.EOF { // end of stream
return
}
if err != nil {
@@ -193,13 +195,13 @@ func reportStream(sr *schema.StreamReader[*schema.Message]) {
}
```
-For a complete implementation, refer to: [eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go)
+Complete implementation: [eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go)
-## **Summary**
+## Summary
-This example demonstrates how to use the Eino framework to build an LLM application through the case of a programmer encourager. From the creation of the ChatModel to the use of message templates, and then to the actual conversation implementation, we believe you have gained a basic understanding of the Eino framework. Whether you choose OpenAI, Ollama, or other model implementations, Eino provides a unified and simple way to use them. We hope this example helps you quickly start building your own LLM application.
+This example shows how to build an LLM-powered application in Eino via `ChatModel`, from templating to conversation handling. Whether you choose OpenAI, Ollama, or another provider, Eino offers a unified, straightforward workflow. Hopefully this helps you quickly start building your own LLM applications.
-## **Related Reading**
+## Related Reading
- Quick Start
- - [Agent-Enable LLM to have hands](/docs/eino/quick_start/agent_llm_with_tools)
+ - [Agent — Give your LLM hands](/docs/eino/quick_start/agent_llm_with_tools)
diff --git a/content/en/docs/hertz/tutorials/third-party/middleware/swagger.md b/content/en/docs/hertz/tutorials/third-party/middleware/swagger.md
index a48844dd945..749fdd83e6d 100644
--- a/content/en/docs/hertz/tutorials/third-party/middleware/swagger.md
+++ b/content/en/docs/hertz/tutorials/third-party/middleware/swagger.md
@@ -13,7 +13,7 @@ description: "Hertz middleware to automatically generate RESTful API documentati
>
> See the migration guide below.
-## Migration Guide
+## Migration Guide
1. Remove deprecated dependencies
@@ -125,6 +125,7 @@ On a web browser, go to http://localhost:8888/swagger/index.html or whichever ad
For users who want to create their own custom UI, they will need to download the Swagger UI dist files, and serve the UI files as static assets.
Copy the following files from [swagger-ui/dist](https://github.com/swagger-api/swagger-ui/tree/master/dist) and place them in `swagger-ui/`.
+
- https://github.com/swagger-api/swagger-ui/blob/master/dist/favicon-16x16.png
- https://github.com/swagger-api/swagger-ui/blob/master/dist/favicon-32x32.png
- https://github.com/swagger-api/swagger-ui/blob/master/dist/swagger-ui.css
diff --git a/content/zh/docs/eino/FAQ.md b/content/zh/docs/eino/FAQ.md
new file mode 100644
index 00000000000..2e1c7dada2d
--- /dev/null
+++ b/content/zh/docs/eino/FAQ.md
@@ -0,0 +1,151 @@
+---
+Description: ""
+date: "2025-12-11"
+lastmod: ""
+tags: []
+title: FAQ
+weight: 6
+---
+
+# Q: cannot use openapi3.TypeObject (untyped string constant "object") as *openapi3.Types value in struct literal,cannot use types (variable of type string) as *openapi3.Types value in struct literal
+
+检查 github.com/getkin/kin-openapi 依赖版本不能超过 v0.118.0。eino V0.6.0 之后的版本不再依赖 kin-openapi 库。
+
+# Q: Agent 流式调用时不会进入 ToolsNode 节点。或流式效果丢失,表现为非流式。
+
+- 先更新 eino 版本到最新
+
+不同的模型在流式模式下输出工具调用的方式可能不同: 某些模型(如 OpenAI) 会直接输出工具调用;某些模型 (如 Claude) 会先输出文本,然后再输出工具调用。因此需要使用不同的方法来判断,这个字段用来指定判断模型流式输出中是否包含工具调用的函数。
+
+ReAct Agent 的 Config 中有一个 StreamToolCallChecker 字段,如未填写,Agent 会使用“非空包”是否包含工具调用判断:
+
+```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,在模型常规输出 answer 时,需要判断**所有包**是否包含 ToolCall,从而导致“流式判断”的效果丢失。如果希望尽可能保留“流式判断”效果,解决这一问题的建议是:
+
+> 💡
+> 尝试添加 prompt 来约束模型在工具调用时不额外输出文本,例如:“如果需要调用 tool,直接输出 tool,不要输出文本”。
+>
+> 不同模型受 prompt 影响可能不同,实际使用时需要自行调整 prompt 并验证效果。
+
+# Q: [github.com/bytedance/sonic/loader](http://github.com/bytedance/sonic/loader): invalid reference to runtime.lastmoduledatap
+
+老版本 sonic 不兼容 go1.24,更新版本大于 v1.13.2 即可
+
+# Q: Tool Input 反序列化失败:failed to invoke tool call {tool_call_id}: unmarshal input fail
+
+目前模型一般不会产生非法 json 输出,可以先确认下反序列化失败原因是什么,大概率是模型输出超长截断导致。
+
+# Q: Eino 如何实现批处理节点? 类似 Coze 中的批处理节点
+
+Eino 目前不支持批处理,可选方法有两种
+
+1. 每次请求按需动态构建 graph,额外成本不高。 这种方法需要注意 Chain Parallel 要求其中并行节点数量大于一,
+2. 自定义批处理节点,节点内自行批处理任务
+
+# Q: eino 支持把模型结构化输出吗
+
+分两步,第一步要求模型输出结构化数据,有三个方法:
+
+1. 部分模型支持直接配置(比如 openai 的 response format),可以看下模型配置里有没有。
+2. 通过 tool call 功能获得
+3. 写 prompt 要求模型
+
+得到模型结构化输出后,可以用 schema.NewMessageJSONParser 把 message 转换成你需要的 struct
+
+# Q:图片识别场景中报错:One or more parameters specified in the request are not valid
+
+检查模型是否支持图片输入
+
+# Q: 如何获取模型(chat model)输出的 Reasoning Content/推理/深度思考 内容:
+
+如果模型封装支持输出 Reasoning Content/推理/深度思考 内容,这些内容会储存到模型输出的 Message 的 ReasoningContent 字段。
+
+# Q:报错中包含"context deadline exceeded" "timeout" "context canceled"
+
+分情况讨论:
+
+1. context.canceled: 在执行 graph 或者 agent 时,用户侧传入了一个可以 cancel 的 context,并发起了取消。排查应用层代码的 context cancel 操作。此报错与 eino 框架无关。
+2. Context deadline exceeded: 可能是两种情况:
+ 1. 在执行 graph 或者 agent 时,用户侧传入了一个带 timeout 的 context,触发了超时。
+ 2. 给 ChatModel 或者其他外部资源配置了 timeout 或带 timeout 的 httpclient,触发了超时。
+
+查看抛出的 error 中的 `node path: [node name x]`,如果 node name 不是 ChatModel 等带外部调用的节点,大概率是 2-a 这种情况,反之大概率是 2-b 这种情况。
+
+如果怀疑是 2-a 这种情况,自行排查下上游链路那个环节给 context 设置了 timeout,常见的可能性如 faas 平台等。
+
+如果怀疑是 2-b 这种情况,看下节点是否自行配置了超时,比如 Ark ChatModel 配置了 Timeout,或者 OpenAI ChatModel 配置了 HttpClient(内部配置了 Timeout)。如果都没有配置,但依然超时了,可能是模型侧 SDK 的默认超时。已知 Ark SDK 默认超时 10 分钟,Deepseek SDK 默认超时 5 分钟。
+
+# Q:想要在子图中获取父图的 State 怎么做
+
+如果子图和父图的 State 类型不同,则可以通过 `ProcessState[父图 state type]()` 来处理父图的 State。如果子图和父图的 State 类型相同,则想办法让 State 类型变成不同的,比如用类型别名:`type NewParentStateType StateType`。
+
+# Q: eino-ext 支持的 Model 模型的如何适配多模特的输入输出 ?
+
+eino-ext 支持的多模态输入输出场景,可以查阅 [https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model](https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model) 对应模型的 Examples 示例;
+
+# Q: 使用最新的多模态支持字段 UserInputMultiContent 输入多模态数据,但模型侧好像没有我传入的多模态数据或者多模态输入时,读不到 multicontent 的内容
+
+最新版本的 Eino 引入 UserInputMultiContent 与 AssistantGenMultiContent 分别表达用户侧输入的多模态数据与模型侧返回的多模态数据,其中 eino-ext 中的 chatmodel 实现都已经做了适配,如果发现模型侧没有收到多模态信息,可以尝试升级下我们使用的模型的包。go get 到最新版本再次尝试运行看是否问题得到解决。
+
+# Q: 升级到 0.6.x 版本后,有不兼容问题
+
+根据先前社区公告规划 [Migration from OpenAPI 3.0 Schema Object to JSONSchema in Eino · cloudwego/eino · Discussion #397](https://github.com/cloudwego/eino/discussions/397),已发布 eino V0.6.1 版本。重要更新内容为移除了 getkin/kin-openapi 依赖以及所有 OpenAPI 3.0 相关代码。
+
+eino-ext 部分 module 报错 undefined: schema.NewParamsOneOfByOpenAPIV3 等问题,升级报错的 eino-ext module 到最新版本即可。
+
+如果 schema 改造比较复杂,可以使用 [JSONSchema 转换方法](https://bytedance.larkoffice.com/wiki/ZMaawoQC4iIjNykzahwc6YOknXf)文档中的工具方法辅助转换。
+
+Q: Eino-ext 提供的 ChatModel 有哪些模型是支持 Response API 形式调用嘛?
+
+- Eino-ext 默认生成的 Chatmodel 不支持 Response API 形式调用,只支持 Chat Completion 接口,特别的 ARK Chat Model 下 隐式支持了 Response API 的调用,用户需要配置 Cache.APIType = _ResponsesAPI;_
diff --git a/content/zh/docs/eino/_index.md b/content/zh/docs/eino/_index.md
index 12c01d745db..c74da58fe3c 100644
--- a/content/zh/docs/eino/_index.md
+++ b/content/zh/docs/eino/_index.md
@@ -1,6 +1,6 @@
---
Description: Eino 是基于 Go 的 AI 应用开发框架
-date: "2025-03-18"
+date: "2025-12-09"
lastmod: ""
linktitle: Eino
menu:
diff --git a/content/zh/docs/eino/core_modules/_index.md b/content/zh/docs/eino/core_modules/_index.md
index 25d5853f353..d4d7d362ac5 100644
--- a/content/zh/docs/eino/core_modules/_index.md
+++ b/content/zh/docs/eino/core_modules/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-10"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: 核心模块'
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/_index.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
index 9b447b54574..5c94ffdd3ef 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-10"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Chain & Graph & Workflow 编排功能'
@@ -45,7 +45,7 @@ Eino 对「编排」有着这样的洞察:
- 详细信息参考:[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)
-- 提供了 “类型对齐” 的开发方式的强化,降低开发者心智负担,把 Go 的**类型安全**特性发挥出来
+- 提供了 “类型对齐” 的开发方式的强化,降低开发者心智负担,把 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)
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
index 7d50b4a4db6..e9bf87d0f7e 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-01-22"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino: CallOption 能力与规范'
-weight: 5
+weight: 6
---
**CallOption**: 对 Graph 编译产物进行调用时,直接传递数据给特定的一组节点(Component、Implementation、Node)的渠道
@@ -296,6 +296,7 @@ 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"))
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
index 8398c7749a7..e61ed4693e0 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino: Callback 用户手册'
-weight: 4
+weight: 5
---
## 解决的问题
@@ -21,7 +21,7 @@ Callbacks 支持“**横切面功能注入**”和“**中间状态透出**”
### 触发实体
-Component(包括官方定义的组件类型和 Lambda),Graph Node(以及 Chain Node),Graph 自身(以及 Chain)。这三类实体,都有横切面功能注入、中间状态透出的需求,因此都会触发 callback。具体见下面的“[触发方式](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)”一节。
+Component(包括官方定义的组件类型和 Lambda),Graph Node(以及 Chain/Workflow Node),Graph 自身(以及 Chain/Workflow)。这三类实体,都有横切面功能注入、中间状态透出的需求,因此都会触发 callback。具体见下面的“[触发方式](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)”一节。
### 触发时机
@@ -94,7 +94,7 @@ type RunInfo struct {
- 有接口的 Component:是啥接口,就是啥
- Lambda:固定值 Lambda
- Graph Node: 用内部的 Component/Lambda/Graph 的值。
- - Graph 自身:固定值 Graph / Chain. (之前曾有 StateGraph / StateChain ,现已整合到 Graph / Chain 中)
+ - Graph 自身:固定值 Graph / Chain / Workflow. (之前曾有 StateGraph / StateChain ,现已整合到 Graph / Chain 中)
### Callback Input & Output
@@ -345,12 +345,16 @@ func ConvCallbackOutput(src callbacks.CallbackOutput) *CallbackOutput {
如果用户的 Handler 只关注特定类型的组件,比如 ReactAgent 的场景,只关注 ChatModel 和 Tool,建议使用 HandlerHelper 来快速创建具体类型的 Callback Handler:
```go
-handler := NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler()
+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
@@ -371,7 +375,9 @@ HandlerHelper 支持全部的官方组件,目前的列表是:ChatModel, Chat
针对 Lambda,Graph,Chain 这些输入输出类型不确定的“组件”,也可以使用 HandlerHelper,但是只能做到上面的第 1 点,即按照组件类型做自动的过滤,2、3 点依然需要用户自己实现:
```go
-handler := NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)...Handler()
+import ucb "github.com/cloudwego/eino/utils/callbacks"
+
+handler := ucb.NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)...Handler()
```
这时,NewHandlerHelper().Lambda() 需要传入 callbacks.Handler 可以用下面的 HandlerBuilder 来实现。
@@ -381,7 +387,9 @@ handler := NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)
如果用户的 Handler 需要关注多个组件类型,但却只需要关注部分的触发时机,可以使用 HandlerBuilder:
```go
-handler := NewHandlerBuilder().OnStartFn(fn)...Build()
+import "github.com/cloudwego/eino/callbacks"
+
+handler := callbacks.NewHandlerBuilder().OnStartFn(fn)...Build()
```
## 最佳实践
@@ -389,28 +397,312 @@ handler := 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 外使用
-使用 InitCallbacks 注入 RunInfo 和 Handlers。RunInfo 的各字段需自行设置。Global Handlers 会自动注入。
+这个场景是:不使用 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")
+}
```
-ctx = callbacks.InitCallbacks(ctx, runInfo, handlers...)
-componentA.Invoke(ctx, input)
+
+对上面的样例代码做下说明:
+
+- 初始化:在 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
+ })
+}
```
-如果一个 componentA 内部调用了其他的 componentB (比如 ToolsNode 内部调用 Tool),需要在 componentB 执行前替换 RunInfo:
+上面的代码假设了“内部的 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
+ })
+}
```
-func ComponentARun(ctx, inputA) {
- // 复用 ctx 中已有的 Handlers(包括 Global Handlers),只替换 RunInfo
- ctx = callbacks.ReuseHandlers(ctx, newRunInfo)
- componentB.Invoke(ctx, inputB)
-
- // RunInfo 和 Handlers 都替换
- ctx = callbacks.InitCallbacks(ctx, newRunInfo, newHandlers...)
- componentB.Invoke(ctx, inputB)
+
+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),
+ )
}
```
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
index c96891bcf12..d90e9216e4a 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-28"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: Chain/Graph 编排介绍'
@@ -47,7 +47,7 @@ func main() {
_ = g.AddEdge(nodeOfPrompt, nodeOfModel)
_ = g.AddEdge(nodeOfModel, compose.END)
- r, err := g.Compile(ctx, compose.WithMaxRunSteps(10))
+ r, err := g.Compile(ctx)
if err != nil {
panic(err)
}
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
new file mode 100644
index 00000000000..8f500a0a04b
--- /dev/null
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md
@@ -0,0 +1,412 @@
+---
+Description: ""
+date: "2025-12-09"
+lastmod: ""
+tags: []
+title: 'Eino: 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, sm StateModifier) 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)
+
+## 流式传输中的 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/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md
deleted file mode 100644
index e2d8d88d002..00000000000
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/interrupt_checkpoint.md
+++ /dev/null
@@ -1,246 +0,0 @@
----
-Description: ""
-date: "2025-08-07"
-lastmod: ""
-tags: []
-title: 'Eino: Interrupt & CheckPoint使用手册'
-weight: 6
----
-
-# 介绍
-
-使用 Interrupt & CheckPoint 功能,可以实现在指定位置暂停 Graph 执行并在之后断点续传,如果是 StateGraph,还可以在断点续传前修改 State。
-
-> 💡
-> 断点续传仅能复原输入和运行时各节点产生的数据,需要确保 Graph 编排和 CallOptions 完全相同。
-
-# 使用 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
- SubGraph map[string]InterruptInfo
-}
-
-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
-// coompose/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 提供了注册方法 RegisterSerializableType:
-
-```go
-import "github.com/cloudwego/eino/compose"
-
-type MyStruct struct {
- // struct body
-}
-
-// func RegisterSerializableType[T any](name string) error
-err = compose.RegisterSerializableType[MyStruct]("MyStruct")
-```
-
-注册后的类型在序列化时将被额外记录类型信息,因此在反序列化时,即使不指明类型(比如反序列化到 interface{}),Eino 也可以反序列化出正确的类型。注册方法中的 key 唯一标识了这个类型,一旦确定了 key 需要保证其不能改变,否则已持久化的 checkpoint 将不能被正确恢复。
-
-> 💡
-> 结构体的未导出字段无法访问,因此不会被存储/恢复
-
-如果注册的类型实现了 json Marshaler 和 Unmarshaler,此类型的序列化和反序列化会使用自定义方法。
-
-```
-// encoding/json
-
-type Marshaler interface {
- MarshalJSON() ([]byte, error)
-}
-
-type Unmarshaler interface {
- UnmarshalJSON([]byte) error
-}
-```
-
-## 开启 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, sm StateModifier) Option
-type StateModifier func(ctx context.Context, path NodePath, state any) error
-func WithStateModifier(sm StateModifier) GraphCompileOption
-```
-
-Checkpoint id 会被作为 CheckPointStore 的 key 使用,graph 运行时会检查 CheckPointStore 是否存在此 id,如果存在则从 checkpoint 中恢复运行;interrupt 是会把 graph 状态保存到此 id 中。
-
-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 传空即可。
-
-## 动态 Interrupt
-
-节点返回特殊错误可以动态地触发 Interrupt:
-
-```
-// eion/compose/checkpoint.go
-var InterruptAndRerun = errors.New("interrupt and rerun")
-```
-
-Eino Graph 接收到节点返回此错误后会发生 interrupt,恢复运行时,会再次运行此节点,再次运行前会调用 StateModifier 修改 state(如果已配置)。
-
-这种情况下,再次运行节点时输入会替换为空值,而不是原本的输入,如果再次运行时需要仍需要原本输入,需要提前保存到 State 中。
-
-# 流式传输中的 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 的前提下,AddAnyGraph 时使用 WithGraphCompileOptions 传入 InterruptNodes 可以开启子图的 Interrupt&CheckPoint,父图未设置 CheckPointer 时会在 Compile 时报错。
-
-```go
-/* graph compose code
-xxx
-*/
-g.AddAnyGraph("node1", subGraph, WithGraphCompileOptions(
- WithInterruptAfterNodes([]string{"node2"}),
-))
-
-g.Compile(ctx, WithCheckPointer(cp))
-```
-
-如果在子图中 interrupt,resume 时修改的 state 应为子图 state。TODO,说明下 StateModifier 中 Path 使用
-
-# 例子
-
-[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)
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
index 7efe0fa1c42..e2e6f571747 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles.md
@@ -1,15 +1,15 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: 编排的设计理念'
weight: 2
---
-大模型应用编排方案中,最火的就是 langchain 以及 langgraph,其官方提供了 python 和 ts 的 sdk,这两门语言以其灵活性著称,灵活性给 sdk 的开发带来了极大的便利,但同时,也给 sdk 的使用者带来了极大的困扰和心智负担。
+大模型应用编排框架的主流语言是 python,这门语言以其灵活性著称,灵活性给 sdk 的开发带来便利,但同时也给 sdk 的使用者带来了心智负担。
-Go 作为一门 极其简单 的编程语言,确定的 `静态类型` 是让其变得简单的重要原因之一,而 eino,则保持了这一重要特性: `确定的类型` + `Compile 时类型检查`。
+基于 golang 的 eino 则是 `静态类型` ,在 Compile 时做类型检查,避免了 python 等动态语言的运行时类型问题。
## 以上下游 `类型对齐` 为基本准则
@@ -17,7 +17,7 @@ eino 的最基础编排方式为 graph,以及简化的封装 chain。不论是
这之间蕴含了一个基本假设:**前一个运行节点的输出值,可以作为下一个节点的输入值。**
-在 Go 中,要实现这个假设,有两个基本方案:
+在 golang 中,要实现这个假设,有两个基本方案:
1. 把不同节点的输入输出都变成一种更泛化的类型,例如 `any` 、`map[string]any` 等。
1. 采用泛化成 any 的方案,但对应的代价是: 开发者在写代码时,需要显式转换成具体类型才能使用。这会极大增加开发者的心智负担,因此最终放弃此方案。
@@ -88,7 +88,7 @@ eino 的最基础编排方式为 graph,以及简化的封装 chain。不论是
```go
func TestChain() {
- chain := compose.NewChain[map[string]any,string]()
+ chain := compose.NewChain[map[string]interface,string]()
nodeTemplate := &fakeChatTemplate{} // input: map[string]any, output: []*schema.Message
@@ -222,7 +222,7 @@ AddChatModelNode("xxx", model, WithStreamStatePostHandler(postHandler))
Runnable 的一个重要作用就是提供了 「Invoke」、「Stream」、「Collect」、「Transform」 四种调用方式。
-> 上述几种调用方式的介绍以及详细的 Runnable 介绍可以查看: [Eino: 基础概念介绍](/zh/docs/eino/overview)
+> 上述几种调用方式的介绍以及详细的 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]`。
@@ -285,11 +285,10 @@ func TestTypeMatch(t *testing.T) {
在 stream 模式下,拼接帧 是一个非常常见的操作,拼接时,会先把 `*StreamReader[T] ` 中的所有元素取出来转成 `[]T`,再尝试把 `[]T` 拼接成一个完整的 `T`。框架内已经内置支持了如下类型的拼接:
-- `*schema.Message`: 详情见 `schema.ConcatMessages()`
+- `*schema.Message`: 详情见 `schema.``ConcatMessages``()`
- `string`: 实现逻辑等同于 `+=`
- `[]*schema.Message`: 详情见 `compose.concatMessageArray()`
- `Map`: 把相同 key 的 val 进行合并,合并逻辑同上,若存在无法合并的类型,则失败 (ps: 不是覆盖)
-- `Struct` 或 `Struct 指针`:先转成 `[]map[string]any` 的,再以 map 的逻辑合并。要求 struct 中不能有 unexported field。
- 其他 slice:只有当 slice 中只有一个元素是非零值时,才能合并。
对其他场景,或者当用户想用定制逻辑覆盖掉上面的默认行为时,开发者可自行实现 concat 方法,并使用 `compose.RegisterStreamChunkConcatFunc()` 注册到全局的拼接函数中。
@@ -340,19 +339,12 @@ Eino 的 Graph 中的数据在 Node、Branch、Handler 间流转时,一律是
### 扇入与合并
-**扇入**:多个上游的数据汇入到下游,一起作为下游的输入。需要明确定义多个上游的输出,如何**合并(Merge)**起来。Eino 的选择是,首先要求多个上游输出的**实际类型**必须相同且为可合并的类型。
+**扇入**:多个上游的数据汇入到下游,一起作为下游的输入。需要明确定义多个上游的输出,如何**合并(Merge)**起来。
-可合并的类型可以是:
+默认情况下,首先要求多个上游输出的**实际类型**必须相同且为 Map,且相互间 key 不可重复。其次:
-- Map 类型,且相互间 key 不重复。
-- 任意类型,且通过 `compose.RegisterValuesMergeFunc` 注册了合并函数。
-
-其次:
-
-- 在非流式场景下,
- - 如果输入的类型有注册过合并函数,则使用注册的合并函数合并为一个值。
- - 如果输入的类型是 Map,则合并成为一个 Map,包含所有上游的所有键值对。
-- 在流式场景下,将类型相同的多个上游 StreamReader 合并为一个 StreamReader。实际 Recv 时从效果为从多个上游 StreamReader 中公平读取。
+- 在非流式场景下,合并后成为一个 Map,包含所有上游的所有键值对。
+- 在流式场景下,将类型相同的多个上游 StreamReader 合并为一个 StreamReader。实际 Recv 时效果为从多个上游 StreamReader 中公平读取。
在 AddNode 时,可以通过添加 WithOutputKey 这个 Option 来把节点的输出转成 Map:
@@ -372,7 +364,7 @@ graph.AddLambdaNode("your_node_key", compose.InvokableLambda(func(ctx context.Co
func RegisterValuesMergeFunc[T any](fn func([]T) (T, error))
```
-Workflow 可以做到多个上游的输出字段映射到下游节点的不同字段。Eino 内部会将上游输出的 Struct 转换为 Map,因此 Merge 依然符合上面的规则。
+Workflow 可以做到多个上游的多个输出字段映射到下游节点的不同字段。这并不属于合并场景,而是点对点的字段映射。事实上,eino workflow 目前不支持“多个上游字段同时映射到相同的下游字段”。
### 流式处理
@@ -474,11 +466,12 @@ Eino 支持各种维度的 Call Option 分配方式:
- 每个节点有确定的前序节点,当所有前序节点都完成后,本节点才具备运行条件。
- 不支持图中有环,因为会打破“每个节点有确定的前序节点”这一假定。
- 支持 Branch。在运行时,将 Branch 未选中的节点记为已跳过,不影响 AllPredecessor 的语义。
-- 不需要手动对齐 SuperStep。
> 💡
-> 设置 NodeTriggerMode == AllPredecessor 后,节点会在所有前驱就绪后执行,但并不是立即执行,而是依然遵循 SuperStep——在一批节点全部执行完成后再运行新的可运行节点。
+> 设置 NodeTriggerMode = AllPredecessor 后,节点会在所有前驱就绪后执行,但并不是立即执行,而是依然遵循 SuperStep——在一批节点全部执行完成后再运行新的可运行节点。
+>
+> 如果在 Compile 时传入 compose.WithEagerExecution(),则就绪的节点会立刻运行。
>
-> 如果在 Compile 是传入 compose.WithEagerExecution(),则就绪的节点会立刻运行。
+> 在 Eino v0.4.0 版本及之后的版本中,设置 NodeTriggerMode = AllPredecessor 后会默认开启 EagerExecution。
总结起来,pregel 模式灵活强大但有额外的心智负担,dag 模式清晰简单但场景受限。在 Eino 框架中,Chain 是 pregel 模式,Workflow 是 dag 模式,Graph 则都支持,可由用户从 pregel 和 dag 中选择。
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
index 6600e9f29ef..076a0058d29 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-10"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Eino 流式编程要点
-weight: 3
+weight: 4
---
> 💡
@@ -174,29 +174,29 @@ type Runnable[I, O any] interface {
如果用户通过 **Invoke** 来调用 Graph,则 Graph 内部所有组件都以 Invoke 范式来调用。如果某个组件,没有实现 Invoke 范式,则 Eino 框架自动根据组件实现了的流式范式,封装出 Invoke 调用范式,优先顺位如下:
-- 若组件实现了 Stream,则通过 Stream 封装 Invoke,即自动 concat 输出流。
+- 若组件实现了 Stream,则将 Stream 封装成 Invoke,即自动 concat 输出流。
-- 否则,若组件实现了 Collect,则通过 Collect 封装 Invoke,即非流式入参转单帧流。
+- 否则,若组件实现了 Collect,则将 Collect 封装成 Invoke,即非流式入参转单帧流。
-- 如果都没实现,则必须实现 Transform,通过 Transform 封装 Invoke,即入参转单帧流,出参 concat。
+- 如果都没实现,则必须实现 Transform,将 Transform 封装成 Invoke,即入参转单帧流,出参 concat。
如果用户通过 **Stream/Collect/Transform** 来调用 Graph,则 Graph 内部所有组件都以 Transform 范式来调用。如果某个组件,没有实现 Transform 范式,则 Eino 框架自动根据组件实现了的流式范式,封装出 Transform 调用范式,优先顺位如下:
-- 若组件实现了 Stream,则通过 Stream 封装 Transform,即自动 concat 输入流。
+- 若组件实现了 Stream,则将 Stream 封装成 Transform,即自动 concat 输入流。
-- 否则,若组件实现了 Collect,则通过 Collect 封装 Transform,即非流式出参转单帧流。
+- 否则,若组件实现了 Collect,则将 Collect 封装成 Transform,即非流式出参转单帧流。
-- 如果都没实现,则必须实现 Invoke,通过 Invoke 封装 Transform,即入参流 concat,出参转单帧流
+- 如果都没实现,则必须实现 Invoke,将 Invoke 封装成 Transform,即入参流 concat,出参转单帧流
@@ -212,7 +212,7 @@ type Runnable[I, O any] interface {
- 目前 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 的入参的 cancat 函数,这本可以避免。
+ - 目前 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,需要考虑的因素会快速增加。在这种情况下,即使框架能定义出一套明确的、更优的普适规则,也很难解释清楚,理解和使用成本会很高,很可能已经超过了这个新规则实际带来的好处。
diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
index c7366a0ae06..8f457824fc6 100644
--- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
+++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/workflow_orchestration_framework.md
@@ -1,17 +1,26 @@
---
Description: ""
-date: "2025-08-07"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: Workflow 编排框架'
-weight: 7
+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
+```
本质特点是:
@@ -70,7 +79,7 @@ Graph 编排时,因为“类型对齐”的要求,如果 f1 -> f2,则 f1
### 最简单的 workflow
-START -> node -> END.
+START -> node -> END
@@ -376,25 +385,6 @@ func main() {
// but no data passing between them.
AddInputWithOptions(compose.START, nil, compose.WithNoDirectDependency())
- wf := compose.NewWorkflow[float64, map[string]float64]()
-
- wf.AddLambdaNode("b1", compose.InvokableLambda(bidder1)).
- AddInput(compose.START)
-
- // 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, 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, nil, compose.WithNoDirectDependency())
-
wf.End().AddInput("b1", compose.ToField("bidder1")).
AddInput("b2", compose.ToField("bidder2"))
@@ -420,7 +410,7 @@ func main() {
```go
func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode {
- return n.addDependencyRelation(fromNodeKey, nil, &workflowAddInputOpts{dependencyWithoutInput: true})
+ return n.addDependencyRelation(fromNodeKey, nil, &workflowAddInputOpts{dependencyWithoutInput: _true_})
}
```
@@ -431,7 +421,7 @@ func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode {
在上面的例子中,我们用与 Graph API 几乎完全相同的方式添加了一个 branch:
```go
- // add a branch just like adding branch in Graph.
+// 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
@@ -473,7 +463,7 @@ func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *Wo
让我们修改下上面的“竞拍”例子,给竞拍者 1 和竞拍者 2 分别给一个“预算”的静态配置:
-
+
budget1 和 budget2 会分别以“静态值”的形式注入到 bidder1 和 bidder2 的 input 中。使用 `SetStaticValue` 方法给 workflow 节点配置静态值:
@@ -712,7 +702,7 @@ Merge 是指一个节点的输入映射自多个 `FieldMapping` 的情况。
```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) {
+ wf.End().AddInput(_START_, ToField("a", WithCustomExtractor(func(input any) (any, error) {
return input.([]int)[0], nil
})))
r, err := wf.Compile(context.Background())
diff --git a/content/zh/docs/eino/core_modules/components/_index.md b/content/zh/docs/eino/core_modules/components/_index.md
index 550712370dd..87c8699f13e 100644
--- a/content/zh/docs/eino/core_modules/components/_index.md
+++ b/content/zh/docs/eino/core_modules/components/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-01-22"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Components 组件'
diff --git a/content/zh/docs/eino/core_modules/components/chat_model_guide.md b/content/zh/docs/eino/core_modules/components/chat_model_guide.md
index 17ccfb6b5f7..00dbe21e1a8 100644
--- a/content/zh/docs/eino/core_modules/components/chat_model_guide.md
+++ b/content/zh/docs/eino/core_modules/components/chat_model_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-10-17"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: ChatModel 使用说明'
-weight: 0
+weight: 1
---
## 基本介绍
@@ -78,7 +78,7 @@ type Message struct {
Content string
// MultiContent 是多模态内容,支持文本、图片、音频等
// Deprecated: 已废弃,使用UserInputMultiContent替代
- MultiContent []ChatMessagePart
+ ~~ MultiContent []ChatMessagePart~~
// UserInputMultiContent 用来存储用户输入的多模态数据,支持文本、图片、音频、视频、文件
// 使用此字段时限制模型角色为User
UserInputMultiContent []MessageInputPart
@@ -263,9 +263,31 @@ handler := &callbacksHelper.ModelCallbackHandler{
return ctx
},
OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*model.CallbackOutput]) context.Context {
- fmt.Println("开始接收流式输出")
- defer output.Close()
- return ctx
+ fmt.Println("开始接收流式输出")
+ defer output.Close()
+
+ for {
+ chunk, err := output.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ fmt.Printf("流读取错误: %v\n", err)
+ return
+ }
+ if chunk == nil || chunk.Message == nil {
+ continue
+ }
+
+ // 仅在模型输出包含 ToolCall 时打印
+ if len(chunk.Message.ToolCalls) > 0 {
+ for _, tc := range chunk.Message.ToolCalls {
+ fmt.Printf("检测到 ToolCall,arguments: %s\n", tc.Function.Arguments)
+ }
+ }
+ }
+
+ return ctx
},
}
diff --git a/content/zh/docs/eino/core_modules/components/chat_template_guide.md b/content/zh/docs/eino/core_modules/components/chat_template_guide.md
index e482019b814..585b5198816 100644
--- a/content/zh/docs/eino/core_modules/components/chat_template_guide.md
+++ b/content/zh/docs/eino/core_modules/components/chat_template_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-04-21"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino: ChatTemplate 使用说明'
-weight: 0
+weight: 2
---
## **基本介绍**
diff --git a/content/zh/docs/eino/core_modules/components/document_loader_guide/_index.md b/content/zh/docs/eino/core_modules/components/document_loader_guide/_index.md
index 037a43115a6..94b1f4b497d 100644
--- a/content/zh/docs/eino/core_modules/components/document_loader_guide/_index.md
+++ b/content/zh/docs/eino/core_modules/components/document_loader_guide/_index.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-19"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino: Document Loader 使用说明'
-weight: 6
+weight: 8
---
## **基本介绍**
@@ -108,7 +108,7 @@ log.Printf("doc content: %v", docs[0].Content)
```go
// 在 Chain 中使用
-chain := compose.NewChain[document.Source, []*schema.Document]()
+chain := compose.NewChain[string, []*schema.Document]()
chain.AppendLoader(loader)
// 编译并运行
diff --git a/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md b/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
index 20e86d15413..3c9ce80c2ad 100644
--- a/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
+++ b/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-04-21"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Document Parser 接口使用说明'
@@ -182,8 +182,8 @@ if err != nil {
}
log.Printf("doc content: %v", docs[0].Content)
-log.Printf("Extension: %s\n", docs[0].MetaData[file.MetaKeyExtension]) // 输出: Extension: .txt
-log.Printf("Source: %s\n", docs[0].MetaData[file.MetaKeySource]) // 输出: Source: ./document.txt
+log.Printf("Extension: %s\n", docs[0].MetaData[file._MetaKeyExtension_]) // 输出: Extension: .txt
+log.Printf("Source: %s\n", docs[0].MetaData[file._MetaKeySource_]) // 输出: Source: ./document.txt
```
## **自定义解析器实现**
diff --git a/content/zh/docs/eino/core_modules/components/document_transformer_guide.md b/content/zh/docs/eino/core_modules/components/document_transformer_guide.md
index fef75d1d71a..de731cb1802 100644
--- a/content/zh/docs/eino/core_modules/components/document_transformer_guide.md
+++ b/content/zh/docs/eino/core_modules/components/document_transformer_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-05-07"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Document Transformer 使用说明'
-weight: 8
+weight: 9
---
## **基本介绍**
diff --git a/content/zh/docs/eino/core_modules/components/embedding_guide.md b/content/zh/docs/eino/core_modules/components/embedding_guide.md
index 2ecf15304bc..cffb81f24f5 100644
--- a/content/zh/docs/eino/core_modules/components/embedding_guide.md
+++ b/content/zh/docs/eino/core_modules/components/embedding_guide.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-10"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Embedding 使用说明'
@@ -227,7 +227,7 @@ func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...e
BatchSize: e.batchSize,
}
options.Options = embedding.GetCommonOptions(options.Options, opts...)
- options = embedding.GetImplSpecificOptions(options, opts...)
+ options = embedding.GetImplSpecificOptions(options.Options, opts...)
// 2. 获取 callback manager
cm := callbacks.ManagerFromContext(ctx)
diff --git a/content/zh/docs/eino/core_modules/components/indexer_guide.md b/content/zh/docs/eino/core_modules/components/indexer_guide.md
index e5f6a09f7ce..f210a803e07 100644
--- a/content/zh/docs/eino/core_modules/components/indexer_guide.md
+++ b/content/zh/docs/eino/core_modules/components/indexer_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-01-22"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Indexer 使用说明'
-weight: 10
+weight: 6
---
## **基本介绍**
diff --git a/content/zh/docs/eino/core_modules/components/lambda_guide.md b/content/zh/docs/eino/core_modules/components/lambda_guide.md
index 8f04d207b7f..769a43ddd68 100644
--- a/content/zh/docs/eino/core_modules/components/lambda_guide.md
+++ b/content/zh/docs/eino/core_modules/components/lambda_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino: Lambda 使用说明'
-weight: 9
+weight: 5
---
## **基本介绍**
@@ -64,7 +64,7 @@ lambda := compose.InvokableLambda(func(ctx context.Context, input string) (outpu
- StreamableLambda
```go
-// input 可以是任意类型;output 必须是 *schema.StreamReader[O],其中 O 可以是任意类型
+// input 可以是任意类型,output 必须是 *schema.StreamReader[O],其中 O 可以是任意类型
lambda := compose.StreamableLambda(func(ctx context.Context, input string) (output *schema.StreamReader[string], err error) {
// some logic
})
@@ -73,7 +73,7 @@ lambda := compose.StreamableLambda(func(ctx context.Context, input string) (outp
- CollectableLambda
```go
-// input 必须是 *schema.StreamReader[I],其中 I 可以是任意类型;output 可以是任意类型
+// input 必须是 *schema.StreamReader[I],其中 I 可以是任意类型,output 可以是任意类型
lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output string, err error) {
// some logic
})
@@ -82,12 +82,16 @@ lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.Stre
- TransformableLambda
```go
-// input 必须是 *schema.StreamReader[I],其中 I 可以是任意类型;output 必须是 *schema.StreamReader[O],其中 O 可以是任意类型
+// input 和 output 必须是 *schema.StreamReader[I],其中 I 可以是任意类型
lambda := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output *schema.StreamReader[string], err error) {
// some logic
})
```
+- 四种 Lambda 方法的构造方法中,具有如下几个相同的 Option 选项
+- compose.WithLambdaType(): 修改 Lambda 组件的 Component 类型,默认是:Lambda
+- compose.WithLambdaCallbackEnable(): 关闭 Lambda 组件默认 在 Graph 中开启的 Node Callback
+
#### 使用自定义 Option
每一种交互方式都对应了一个构建方法,以下以 Invoke 为例:
diff --git a/content/zh/docs/eino/core_modules/components/retriever_guide.md b/content/zh/docs/eino/core_modules/components/retriever_guide.md
index 0602a43d825..481f77f7439 100644
--- a/content/zh/docs/eino/core_modules/components/retriever_guide.md
+++ b/content/zh/docs/eino/core_modules/components/retriever_guide.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-01-22"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino: Retriever 使用说明'
-weight: 0
+weight: 4
---
## **基本介绍**
diff --git a/content/zh/docs/eino/core_modules/components/tools_node_guide/_index.md b/content/zh/docs/eino/core_modules/components/tools_node_guide/_index.md
index 2fcea9d6dbf..7f1443f51b7 100644
--- a/content/zh/docs/eino/core_modules/components/tools_node_guide/_index.md
+++ b/content/zh/docs/eino/core_modules/components/tools_node_guide/_index.md
@@ -1,22 +1,99 @@
---
Description: ""
-date: "2025-04-09"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino: ToolsNode&Tool 使用说明'
-weight: 0
+weight: 3
---
## **基本介绍**
-ToolsNode 组件是一个用于扩展模型能力的组件,它允许模型调用外部工具来完成特定的任务。这个组件可用于以下场景中:
+`Tool` 在 eino 框架中的定义是“ChatModel 能够选择调用的外部能力”,包括本地函数,MCP server tool 等。
-- 让模型能够获取实时信息(如搜索引擎、天气查询等)
-- 使模型能够执行特定的操作(如数据库操作、API 调用等)
-- 扩展模型的能力范围(如数学计算、代码执行等)
-- 与外部系统集成(如知识库查询、插件系统等)
+`ToolsNode` 是 eino 框架指定的”Tool 执行器“,无论是 Graph 内还是 Agent 中,Tool 的执行都要通过 ToolsNode:
-## **组件定义**
+```go
+// compose/tool_node.go
+
+// run tools using `Invoke`
+func (tn *ToolsNode) Invoke(ctx context.Context, input *schema.Message,
+ opts ...ToolsNodeOption) ([]*schema.Message, error)
+
+// run tools using `Stream`
+func (tn *ToolsNode) Stream(ctx context.Context, input *schema.Message,
+ opts ...ToolsNodeOption) (*schema.StreamReader[[]*schema.Message], error)
+```
+
+给 ToolsNode 配置一个 Tool 列表以及一些配套策略:
+
+```go
+// compose/tool_node.go
+
+type ToolsNodeConfig struct {
+ Tools []tool.BaseTool
+
+ UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)
+
+ ExecuteSequentially bool
+
+ ToolArgumentsHandler func(ctx context.Context, name, arguments string) (string, error)
+
+ ToolCallMiddlewares []ToolMiddleware
+}
+```
+
+这样 ToolsNode 就“能够执行配置的 Tool”,并获得一些扩展能力,如执行时序、异常处理、入参处理、middleware 扩展等。
+
+ToolsNode 如何“决策”应该执行哪个 Tool?它不决策,而是依据输入的 `*schema.Message` 来执行:
+
+```go
+// schema/message.go
+
+type Message struct {
+ // role should be 'assistant' for tool call message
+ Role RoleType `json:"role"`
+
+ // here each `ToolCall` is generated by ChatModel and to be executed by ToolsNode
+ ToolCalls []ToolCall `json:"tool_calls,omitempty"`
+
+ // other fields...
+}
+
+// ToolCall is the tool call in a message.
+// It's used in Assistant Message when there are tool calls should be made.
+type ToolCall struct {
+ // Index is used when there are multiple tool calls in a message.
+ // In stream mode, it's used to identify the chunk of the tool call for merging.
+ Index *int `json:"index,omitempty"`
+ // ID is the id of the tool call, it can be used to identify the specific tool call.
+ ID string `json:"id"`
+ // Type is the type of the tool call, default is "function".
+ Type string `json:"type"`
+ // Function is the function call to be made.
+ Function FunctionCall `json:"function"`
+
+ // Extra is used to store extra information for the tool call.
+ Extra map[string]any `json:"extra,omitempty"`
+}
+
+// FunctionCall is the function call in a message.
+// It's used in Assistant Message.
+type FunctionCall struct {
+ // Name is the name of the function to call, it can be used to identify the specific function.
+ Name string `json:"name,omitempty"`
+ // Arguments is the arguments to call the function with, in JSON format.
+ Arguments string `json:"arguments,omitempty"`
+}
+```
+
+ChatModel(LLM) 生成要调用的 []ToolCall(包含 ToolName,Argument 等),放到 *schema.Message 中传给 ToolsNode。ToolsNode 针对每个 ToolCall 实际执行一次调用。
+
+如果配置了 ExecuteSequentially,则 ToolsNode 会按照 []ToolCall 中的先后顺序来执行工具。
+
+每个 ToolCall 调用完成后的结果,又会封装为 *schema.Message,作为 ToolsNode 输出的一部分。
+
+## **Tool ****定义**
### **接口定义**
@@ -30,7 +107,7 @@ type BaseTool interface {
Info(ctx context.Context) (*schema.ToolInfo, error)
}
-// 支持同步调用的工具接口
+// 可调用的工具接口,支持同步调用
type InvokableTool interface {
BaseTool
InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
@@ -49,7 +126,7 @@ type StreamableTool interface {
- 参数:
- ctx:上下文对象
- 返回值:
- - `*schema.ToolInfo`:工具的描述信息,用于提供给大模型
+ - `*schema.ToolInfo`:工具的描述信息
- error:获取信息过程中的错误
#### **InvokableRun 方法**
@@ -97,39 +174,6 @@ type ToolInfo struct {
Tool 组件使用 ToolOption 来定义可选参数, ToolsNode 没有抽象公共的 option。每个具体的实现可以定义自己的特定 Option,通过 WrapToolImplSpecificOptFn 函数包装成统一的 ToolOption 类型。
-```go
-package tool
-
-// Option defines call option for InvokableTool or StreamableTool component, which is part of component interface signature.
-// Each tool implementation could define its own options struct and option funcs within its own package,
-// then wrap the impl specific option funcs into this type, before passing to InvokableRun or StreamableRun.
-type Option struct {
- implSpecificOptFn any
-}
-
-// WrapImplSpecificOptFn wraps the impl specific option functions into Option type.
-// T: the type of the impl specific options struct.
-// Tool implementations are required to use this function to convert its own option functions into the unified Option type.
-// For example, if the tool defines its own options struct:
-//
-// type customOptions struct {
-// conf string
-// }
-//
-// Then the tool needs to provide an option function as such:
-//
-// func WithConf(conf string) Option {
-// return WrapImplSpecificOptFn(func(o *customOptions) {
-// o.conf = conf
-// }
-// }
-func WrapImplSpecificOptFn[T any](optFn func(*T)) Option {
- return Option{
- implSpecificOptFn: optFn,
- }
-}
-```
-
## **使用方式**
```go
@@ -140,16 +184,11 @@ import (
)
// 创建工具节点
-toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- searchTool, // 搜索工具
- weatherTool, // 天气查询工具
- calculatorTool, // 计算器工具
- },
+toolsNode := compose.NewToolsNode([]tool.Tool{
+ searchTool, // 搜索工具
+ weatherTool, // 天气查询工具
+ calculatorTool, // 计算器工具
})
-if err != nil {
- return err
-}
// Mock LLM 输出作为输入
input := &schema.Message{
@@ -169,8 +208,6 @@ toolMessages, err := toolsNode.Invoke(ctx, input)
ToolsNode 通常不会被单独使用,一般用于编排之中接在 ChatModel 之后。
-如果要和 ChatModel 共同使用,即 ChatModel 产生tool call调用指令,Eino解析tool call指令来调用 ToolsNode, 需要调用 ChatModel 的 WithTools() 函数将工具描述信息传递给大模型。
-
### **在编排中使用**
```go
@@ -180,17 +217,12 @@ import (
"github.com/cloudwego/eino/schema"
)
-// 创建工具节点,一个工具节点可包含多种工具
-toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- searchTool, // 搜索工具
- weatherTool, // 天气查询工具
- calculatorTool, // 计算器工具
- },
+// 创建工具节点
+toolsNode := compose.NewToolsNode([]tool.Tool{
+ searchTool, // 搜索工具
+ weatherTool, // 天气查询工具
+ calculatorTool, // 计算器工具
})
-if err != nil {
- return err
-}
// 在 Chain 中使用
chain := compose.NewChain[*schema.Message, []*schema.Message]()
@@ -201,9 +233,7 @@ graph := compose.NewGraph[*schema.Message, []*schema.Message]()
graph.AddToolsNode(toolsNode)
```
-## **Option 和 Callback 使用**
-
-### **Option 使用示例**
+## **Option 机制**
自定义 Tool 可根据自己需要实现特定的 Option:
@@ -223,38 +253,9 @@ func WithTimeout(timeout time.Duration) tool.Option {
o.Timeout = timeout
})
}
-
-type MyTool struct {
- options MyToolOptions
-}
-
-func (t *MyTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
- // 省略具体实现
- return nil, err
-}
-func (t *MyTool) StreamableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error) {
- // 省略具体实现
- return nil, err
-}
-
-func (t *MyTool) InvokableRun(ctx context.Context, argument string, opts ...tool.Option) (string, error) {
- // 将执行编排时传入的自定义配置设置到MyToolOptions中
- tmpOptions := tool.GetImplSpecificOptions(&t.options, opts...)
-
- // 根据tmpOptions中Timeout的值处理Timeout逻辑
- return "", nil
-}
```
-执行编排时,可以使用 compose.WithToolsNodeOption() 传入 ToolsNode 相关的Option设置,ToolsNode下的所有 Tool 都能接收到
-```go
-streamReader, err := graph.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "hello",
- },
-}, compose.WithToolsNodeOption(compose.WithToolOption(WithTimeout(10 * time.Second))))
-```
+## **Option 和 Callback 使用**
### **Callback 使用示例**
@@ -318,8 +319,25 @@ if err != nil {
result, err := runnable.Invoke(ctx, input, compose.WithCallbacks(helper))
```
+## 如何获取 ToolCallID
+
+在 tool 函数体、tool callback handler 中,都可以通过 `compose.GetToolCallID(ctx)` 函数获取当前 Tool 的 ToolCallID。
+
## **已有实现**
1. Google Search Tool: 基于 Google 搜索的工具实现 [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch)
2. duckduckgo search tool: 基于 duckduckgo 搜索的工具实现 [Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
3. MCP: 把 mcp server 作为 tool[Tool - MCP](/zh/docs/eino/ecosystem_integration/tool/tool_mcp)
+
+### v0.5.x->0.6.x
+
+鉴于以下两点考虑:
+
+1. 各大模型厂商 API、MCP Tool 协议约定使用 JSONSchema 来描述工具 input/output schema。
+2. Eino 引用的 getkin/kin-openapi@v0.118.0 有安全问题,且 kin-openapi 安全版本有不兼容更新。
+
+Eino 移除了 OpenAPI schema 3.0 相关的所有定义与方法,转为使用 JSONSchema 2020-12。具体移除及增加的定义与方法详见 [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397) 。
+
+升级后,部分 eino-ext module 可能报错“_undefined: schema.NewParamsOneOfByOpenAPIV3_”等问题,升级报错的 eino-ext module 到最新版本即可。
+
+如果 schema 改造比较复杂,可以使用 [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397) 中提供的工具方法辅助转换。
diff --git a/content/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md b/content/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
index 36fd930215a..9aa92fd0abc 100644
--- a/content/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
+++ b/content/zh/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-04-09"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 如何创建一个 tool ?
-weight: 0
+weight: 1
---
## Tool 的基本结构
@@ -81,11 +81,11 @@ map[string]*schema.ParameterInfo{
这样的表示方式非常简单直观,当参数由开发者通过编码的方式手动维护时常用。
-### 方式 2 - openapi3.Schema
+### 方式 2 - JSON Schema
-另一种常用于表示参数约束的方式是 `JSON schema`,由 OAI 定义的 [OpenAPI](https://github.com/OAI/OpenAPI-Specification) 则是最常用的标准,Eino 中也支持使用 openapi3.Schema 来表示参数的约束。
+另一种常用于表示参数约束的方式是 `JSON schema`: [https://json-schema.org/draft/2020-12](https://json-schema.org/draft/2020-12).
-OpenAPI3 的标准中对参数的约束方式非常丰富,详细描述可以参考 [OpenAPI 3.03](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object),在实际的使用中,一般不由开发者自行构建此结构体,而是使用一些方法来生成。
+JSON Schema 的标准中对参数的约束方式非常丰富。在实际的使用中,一般不由开发者自行构建此结构体,而是使用一些方法来生成。
#### 使用 GoStruct2ParamsOneOf 生成
@@ -97,11 +97,12 @@ func GoStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error)
其中从 T 中提取参数的字段名称和描述,提取时所用的 Tag 如下:
-- jsonschema: "description=xxx"
+- jsonschema_description:"xxx " [推荐]或者 jsonschema: "description=xxx"
+ - description 中一般会有逗号,且 tag 中逗号是不同字段的分隔符,且不可被转义,强烈推荐使用 jsonschema_description 这个单独的 Tag 标签
- jsonschema: "enum=xxx,enum=yyy,enum=zzz"
- jsonschema: "required"
- json: "xxx,omitempty" => 可用 json tag 的 omitempty 代表非 required
-- 使用 utils.WithSchemaCustomizer 实现自定义的解析方法
+- 使用 utils.WithSchemaModifier 实现自定义的解析方法
可参考如下例子:
@@ -114,8 +115,8 @@ import (
)
type User struct {
- Name string `json:"name" jsonschema:"required,description=the name of the user"`
- Age int `json:"age" jsonschema:"description=the age of the user"`
+ Name string `json:"name" jsonschema_description=the name of the user jsonschema:"required"`
+ Age int `json:"age" jsonschema_description:"the age of the user"`
Gender string `json:"gender" jsonschema:"enum=male,enum=female"`
}
@@ -126,13 +127,9 @@ func main() {
这个方法一般不由开发者调用,往往直接使用 `utils.GoStruct2ToolInfo()` 来构建 ToolInfo,或者直接用 `utils.InferTool()` 直接构建 tool,可详见下方把 “本地函数转为 tool” 部分。
-#### 通过 openapi.json 文件生成
-
-由于 openapi 是一个很通用的标准,很多工具或平台都可以导出 openapi.json 文件,尤其是一些 http 的接口管理工具中。如果 tool 是对一些 openapi 的封装,则可以用到这种方式。
-
-使用示例可见 [eino-examples](https://github.com/cloudwego/eino-examples/blob/main/components/tool/openapi3/main.go#L33)。
+## 实现 Tool 的方式
-## 方式 1 - 直接实现接口
+### 方式 1 - 直接实现接口
由于 tool 的定义都是接口,因此最直接实现一个 tool 的方式即实现接口,以 InvokableTool 为例:
@@ -161,7 +158,7 @@ func (t *AddUser) InvokableRun(_ context.Context, argumentsInJSON string, _ ...t
由于大模型给出的 function call 参数始终是一个 string,对应到 Eino 框架中,tool 的调用参数入参也就是一个序列化成 string 的 json。因此,这种方式需要开发者自行处理参数的反序列化,并且调用的结果也用 string 的方式返回。
-## 方式 2 - 把本地函数转为 tool
+### 方式 2 - 把本地函数转为 tool
在开发过程中,我们经常需要把一个本地函数封装成 Eino 的 tool,比如我们代码中本身已经有了一个 `AddUser` 的方法,但为了让大模型可以自主决策如何调用这个方法,我们要把这个方法变成一个 tool 并 bind 到大模型上。
@@ -169,7 +166,7 @@ Eino 中提供了 `NewTool` 的方法来把一个函数转成 tool,同时,
> 下方方法的示例可以参考 [cloudwego/eino/components/tool/utils/invokable_func_test.go](https://github.com/cloudwego/eino/blob/main/components/tool/utils/invokable_func_test.go) 和 [cloudwego/eino/components/tool/utils/streamable_func_test.go](https://github.com/cloudwego/eino/blob/main/components/tool/utils/streamable_func_test.go) 中的单元测试。此处仅以 InvokableTool 为例,StreamableTool 也有对应的构建方法。
-### 使用 NewTool 方法
+#### 使用 NewTool 方法
当一个函数满足下面这种函数签名时,就可以用 NewTool 把其变成一个 InvokableTool :
@@ -234,7 +231,7 @@ func createTool() tool.InvokableTool {
}
```
-### 使用 InferTool 方法
+#### 使用 InferTool 方法
从 NewTool 中可以看出,构建一个 tool 的过程需要分别传入 ToolInfo 和 InvokeFunc ,其中,ToolInfo 中包含 ParamsOneOf 的部分,这代表着函数的入参约束,同时,InvokeFunc 的函数签名中也有 input 的参数,这就意味着: ParamsOneOf 的部分和 InvokeFunc 的 input 参数需要保持一致。
@@ -274,7 +271,7 @@ func createTool() (tool.InvokableTool, error) {
}
```
-### 使用 InferOptionableTool 方法
+#### 使用 InferOptionableTool 方法
Option 机制是 Eino 提供的一种在运行时传递动态参数的机制,详情可以参考 [Eino: CallOption 能力与规范](/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities),这套机制在自定义 tool 中同样适用。
@@ -326,11 +323,11 @@ func useInInvoke() {
}
```
-## 方式 3 - 使用 eino-ext 中提供的 tool
+### 方式 3 - 使用 eino-ext 中提供的 tool
除了自定义的各种 tool 需要自行实现外,eino-ext 项目中还有很多通用的 tool 实现,可以实现开箱即用,比如 [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch)、[Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search) 、wikipedia、httprequest 等等,可以参考 [https://github.com/cloudwego/eino-ext/tree/main/components/tool](https://github.com/cloudwego/eino-ext/tree/main/components/tool) 中的各种实现。
-## 方式 4 - 使用 MCP 协议
+### 方式 4 - 使用 MCP 协议
MCP(Model Context Protocol)是一个开放的模型上下文协议,现在越来越多的工具和平台都在基于这套协议把自身的能力暴露给大模型调用,eino 可以把基于 MCP 提供的可调用工具作为 tool,这将极大扩充 tool 的种类。
diff --git a/content/zh/docs/eino/core_modules/devops/_index.md b/content/zh/docs/eino/core_modules/devops/_index.md
index 0dd11130a93..55c1d1eab84 100644
--- a/content/zh/docs/eino/core_modules/devops/_index.md
+++ b/content/zh/docs/eino/core_modules/devops/_index.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino Dev: 应用开发工具链'
-weight: 4
+weight: 5
---
🚀 Eino 是 Go AI 集成组件的研发框架,提供了 AI 应用相关的常用组件以及集成组件编排能力,为了更好的辅助开发者使用 Eino,我们提供了 「Eino Dev 插件」 ,现在就安装插件 ( [EinoDev 插件安装指南](/zh/docs/eino/core_modules/devops/ide_plugin_guide)),助你高效开发 🚀
diff --git a/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md
index 6193511a95b..fca7ce46f42 100644
--- a/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md
+++ b/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md
@@ -51,7 +51,7 @@ weight: 1
- 在 VS Code 中点击「Extension 图标」,进入插件市场,搜索 Eino Dev,安装即可
-
+
## 功能简介
@@ -61,10 +61,10 @@ weight: 1
Goland
右侧边栏找到「Eino Dev」图标并点击:
-
+
| VS Code
在底部找到「Eino Dev」并点击:
-
+
|
diff --git a/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
index 614827d89fc..f195e80e1d6 100644
--- a/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
+++ b/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-11-20"
lastmod: ""
tags: []
title: Eino Dev 可视化调试插件功能指南
@@ -334,3 +334,67 @@ IP 和 Port 配置完成后,点击确认,调试插件会自动连接到目
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/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
index 4b99cccc770..611a9a86326 100644
--- a/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
+++ b/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-03"
lastmod: ""
tags: []
title: Eino Dev 可视化编排插件功能指南
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_collaboration.md b/content/zh/docs/eino/core_modules/eino_adk/agent_collaboration.md
index 02984a88000..d4fa337d985 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_collaboration.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_collaboration.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino ADK: Agent 协作'
weight: 4
---
-## 概述回顾
+# Agent 协作
概述文档已经对 Agent 协作提供了基础的说明,下面将结合代码,对协作与组合原语的设计与实现进行介绍:
@@ -15,15 +15,15 @@ weight: 4
- Agent 间协作方式
-
- | 协助方式 | 描述 |
+
+ | 协作方式 | 描述 |
| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
- AgentInput 的上下文策略
-
+
| 上下文策略 | 描述 |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 |
@@ -31,20 +31,20 @@ weight: 4
- 决策自主性
-
+
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
- 组合原语
-
+
| 类型 | 描述 | 运行模式 | 协作方式 | 上下文策略 | 决策自主性 |
- | SubAgents | 将用户提供的 agent 作为 父Agent,用户提供的 subAgents 列表作为 子Agents,组合而成可自主决策的 Agent,其中的 Name 和 Description 作为该 Agent 的名称标识和描述。当前限定一个 Agent 只能有一个 父 Agent可采用 SetSubAgents 函数,构建 「多叉树」 形式的 Multi-Agent在这个「多叉树」中,AgentName 需要保持唯一 |  | Transfer | 上游 Agent 全对话 | 自主决策 |
- | Sequential | 将用户提供的 SubAgents 列表,组合成按照顺序依次执行的 Sequential Agent,其中的 Name 和 Description 作为 Sequential Agent 的名称标识和描述。Sequential Agent 执行时,将 SubAgents 列表,按照顺序依次执行,直至将所有 Agent 执行一遍后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
- | Parallel | 将用户提供的 SubAgents 列表,组合成基于相同上下文,并发执行的 Parallel Agent,其中的 Name 和 Description 作为 Parallel Agent 的名称标识和描述。Parallel Agent 执行时,将 SubAgents 列表,并发执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
- | Loop | 将用户提供的 SubAgents 列表,按照数组顺序依次执行,循环往复,组合成 Loop Agent,其中的 Name 和 Description 作为 Loop Agent 的名称标识和描述。Loop Agent 执行时,将 SubAgents 列表,顺序执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
- | AgentAsTool | 将一个 Agent 转换成 Tool,被其他的 Agent 当成普通的 Tool 使用。一个 Agent 能否将其他 Agent 当成 Tool 进行调用,取决于自身的实现。adk 中提供的 ChatModelAgent 支持 AgentAsTool 的功能 |  | ToolCall | 全新任务描述 | 自主决策 |
+ | SubAgents | 将用户提供的 agent 作为 父Agent,用户提供的 subAgents 列表作为 子Agents,组合而成可自主决策的 Agent,其中的 Name 和 Description 作为该 Agent 的名称标识和描述。当前限定一个 Agent 只能有一个 父 Agent可采用 SetSubAgents 函数,构建 「多叉树」 形式的 Multi-Agent在这个「多叉树」中,AgentName 需要保持唯一 |  | Transfer | 上游 Agent 全对话 | 自主决策 |
+ | Sequential | 将用户提供的 SubAgents 列表,组合成按照顺序依次执行的 Sequential Agent,其中的 Name 和 Description 作为 Sequential Agent 的名称标识和描述。Sequential Agent 执行时,将 SubAgents 列表,按照顺序依次执行,直至将所有 Agent 执行一遍后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+ | Parallel | 将用户提供的 SubAgents 列表,组合成基于相同上下文,并发执行的 Parallel Agent,其中的 Name 和 Description 作为 Parallel Agent 的名称标识和描述。Parallel Agent 执行时,将 SubAgents 列表,并发执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+ | Loop | 将用户提供的 SubAgents 列表,按照数组顺序依次执行,循环往复,组合成 Loop Agent,其中的 Name 和 Description 作为 Loop Agent 的名称标识和描述。Loop Agent 执行时,将 SubAgents 列表,顺序执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+ | AgentAsTool | 将一个 Agent 转换成 Tool,被其他的 Agent 当成普通的 Tool 使用。一个 Agent 能否将其他 Agent 当成 Tool 进行调用,取决于自身的实现。adk 中提供的 ChatModelAgent 支持 AgentAsTool 的功能 |  | ToolCall | 全新任务描述 | 自主决策 |
## 上下文传递
@@ -61,21 +61,24 @@ History 对应【上游 Agent 全对话上下文策略】,多 Agent 系统中
通过这种方式,其他 Agent 的行为被当作了提供给当前 Agent 的“外部信息”或“事实陈述”,而不是它自己的行为,从而避免了 LLM 的上下文混乱。
-
+
-在 Eino ADK 中,当为一个 Agent 构建 AgentInput 时,会对 History 中的 Event 进行过滤,确保 Agent 只会接收到当前 Agent 的直接或间接父 Agent 产生的 Event。换句话说,只有当 Event 的 RunPath “属于”当前 Agent 的 RunPath 时,该 Event 才会参与构建 Agent 的 Input。
+在 Eino ADK 中,当为一个 Agent 构建 AgentInput 时,它能看到的 History 是“所有在我之前产生的 AgentEvent”。
-> 💡
-> RunPathA “属于” RunPathB 定义为 RunPathA 与 RunPathB 相同或者 RunPathA 是 RunPathB 的前缀
+值得一提的是 ParallelWorkflowAgent:并行的两个子 Agent(A,B),在并行执行过程中,相互不可见对方产生的 AgentEvent,因为并行的 A、B 没有谁是在另一个之前。
+
+#### RunPath
+
+History 中每个 AgentEvent 都是由“特定 Agent 在特定的执行序列中产生的”,也就是 AgentEvent 有自身的 RunPath。RunPath 的作用是传递出这个信息,在 eino 框架中不乘载其他功能。
下面表格中给出各种编排模式下,Agent 执行时的具体 RunPath:
-
+
| Example | RunPath |
- | Agent: [Agent]SubAgent: [Agent, SubAgent] |
- | Agent: [Agent]Agent(after function call): [Agent] |
- | Agent1: [SequentialAgent, LoopAgent, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2]Agent1: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1, Agent2]Agent3: [SequentialAgent, LoopAgent, Agent3]Agent4: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent4]Agent5: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent5]Agent6: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent6] |
- | Agent: [Agent]SubAgent: [Agent, SubAgent]Agent: [Agent, SubAgent, Agent] |
+ | Agent: [Agent]SubAgent: [Agent, SubAgent] |
+ | Agent: [Agent]Agent(after function call): [Agent] |
+ | Agent1: [SequentialAgent, LoopAgent, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2]Agent1: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1]Agent2: [SequentialAgent, LoopAgent, Agent1, Agent2, Agent1, Agent2]Agent3: [SequentialAgent, LoopAgent, Agent3]Agent4: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent4]Agent5: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent5]Agent6: [SequentialAgent, LoopAgent, Agent3, ParallelAgent, Agent6] |
+ | Agent: [Agent]SubAgent: [Agent, SubAgent]Agent: [Agent, SubAgent, Agent] |
#### 自定义
@@ -94,19 +97,40 @@ func WithHistoryRewriter(h HistoryRewriter) AgentOption
#### 概念
-SessionValues 是在一次运行中持续存在的全局临时 KV 存储,用于支持跨 Agent 的状态管理和数据共享,一次运行中的任何 Agent 可以在任何时间读写 SessionValues。Eino ADK 提供了三种方法访问 SessionValues:
+SessionValues 是在一次运行中持续存在的全局临时 KV 存储,用于支持跨 Agent 的状态管理和数据共享,一次运行中的任何 Agent 可以在任何时间读写 SessionValues。
+
+Eino ADK 提供了多种方法供 Agent 运行时内部并发安全的读写 Session Values:
```go
// github.com/cloudwego/eino/adk/runctx.go
-// 批量设置 SessionValues (key冲突时覆盖)
-func AddSessionValues(ctx context.Context, kvs map[string]any)
// 获取全部 SessionValues
func GetSessionValues(ctx context.Context) map[string]any
-// 设置 SessionValue (key冲突时覆盖)
-func AddSessionValue(ctx context.Context, key string, value any)
+// 批量设置 SessionValues
+func AddSessionValues(ctx context.Context, kvs 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 机制基于 Context 来实现,而 Runner 运行会对 Context 重新初始化,因此在 Run 方法外通过 `AddSessionValues` 或 `AddSessionValue` 注入 SessionValues 是不生效的。
+
+如果您需要在 Agent 运行前就注入数据到 SessionValues 中,需要使用专用的 Option 来协助实现,用法如下:
+
+```go
+// github.com/cloudwego/eino/adk/call_option.go
+// WithSessionValues 在 Agent 运行前注入 SessionValues
+func WithSessionValues(v map[string]any) AgentRunOption
+
+// 用法:
+runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent})
+iterator := runner.Run(ctx, []adk.Message{schema.UserMessage("xxx")},
+ adk.WithSessionValues(map[string]any{
+ PlanSessionKey: 123,
+ UserInputSessionKey: []adk.Message{schema.UserMessage("yyy")},
+ }),
+)
```
## Transfer SubAgents
@@ -153,7 +177,7 @@ type OnSubAgents interface {
接下来以一个多功能对话 Agent 演示 Transfer 能力,目标是搭建一个可以查询天气或者与用户对话的 Agent,Agent 结构如下:
-
+
三个 Agent 均使用 ChatModelAgent 实现:
@@ -381,7 +405,7 @@ func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTr
在 Supervisor 模式中,子 Agent 执行完毕后固定回到 Supervisor,由 Supervisor 生成下一步任务目标。此时可以使用 AgentWithDeterministicTransferTo:
-
+
```go
// github.com/cloudwego/eino/adk/prebuilt/supervisor.go
@@ -419,7 +443,7 @@ WorkflowAgent 支持以代码中预设好的流程运行 Agents。Eino ADK 提
SequentialAgent 会按照你提供的顺序,依次执行一系列 Agent:
-
+
```go
type SequentialAgentConfig struct {
@@ -435,7 +459,7 @@ func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Age
LoopAgent 基于 SequentialAgent 实现,在 SequentialAgent 运行完成后,再次从头运行:
-
+
```go
type LoopAgentConfig struct {
@@ -453,7 +477,7 @@ func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error)
ParallelAgent 会并发运行若干 Agent:
-
+
```go
type ParallelAgentConfig struct {
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_extension.md b/content/zh/docs/eino/core_modules/eino_adk/agent_extension.md
index 35ba8da44c4..87413947772 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_extension.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_extension.md
@@ -1,21 +1,19 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino ADK: Agent Runner 与扩展'
-weight: 6
+weight: 3
---
-## Agent Runner
+# Agent Runner
-### 定义
+## 定义
-Runner 是 Eino ADK 中负责执行 Agent 的核心引擎。它的主要作用是管理和控制 Agent 的整个生命周期,如处理多 Agent 协作,保存传递上下文等,interrupt、callback 等切面能力也均依赖 Runner 实现。
+Runner 是 Eino ADK 中负责执行 Agent 的核心引擎。它的主要作用是管理和控制 Agent 的整个生命周期,如处理多 Agent 协作,保存传递上下文等,interrupt、callback 等切面能力也均依赖 Runner 实现。任何 Agent 都应通过 Runner 来运行。
-任何 Agent 都应通过 Runner 来运行。
-
-### Interrupt & Resume
+## Interrupt & Resume
Agent Runner 提供运行时中断与恢复的功能,该功能允许一个正在运行的 Agent 主动中断其执行并保存当前状态,支持从中断点恢复执行。该功能常用于 Agent 处理流程中需要外部输入、长时间等待或可暂停等场景。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_hitl.md b/content/zh/docs/eino/core_modules/eino_adk/agent_hitl.md
index 5b414b17c28..33d2ada48c6 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_hitl.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_hitl.md
@@ -1,23 +1,93 @@
---
Description: ""
-date: "2025-11-07"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: 'Eino human-in-the-loop框架:技术架构指南'
-weight: 8
+title: Eino human-in-the-loop框架:技术架构指南
+weight: 6
---
## 概述
-本文档提供 Eino 的human-in-the-loop (Human-in-the-Loop, HITL) 框架架构的技术细节,重点介绍中断/恢复机制和底层的寻址系统。
+本文档提供 Eino 的 human-in-the-loop (Human-in-the-Loop, HITL) 框架架构的技术细节,重点介绍中断/恢复机制和底层的寻址系统。
-## human-in-the-loop的需求
+## human-in-the-loop 的需求
下图说明了在中断/恢复过程中,每个组件必须回答的关键问题。理解这些需求是掌握架构设计背后原因的关键。
-
+```mermaid
+graph TD
+ subgraph P1 [中断阶段]
+ direction LR
+ subgraph Dev1 [开发者]
+ direction TB
+ D1[我现在应该中断吗?
我之前被中断过吗?]
+ D2[用户应该看到关于此中断的
什么信息?]
+ D3[我应该保留什么状态
以便后续恢复?]
+ D1 --> D2 --> D3
+ end
+
+ subgraph Fw1 [框架]
+ direction TB
+ F1[中断发生在执行层级的
哪个位置?]
+ F2[如何将状态与
中断位置关联?]
+ F3[如何持久化中断
上下文和状态?]
+ F4[用户需要什么信息
来理解中断?]
+ F1 --> F2 --> F3 --> F4
+ end
+
+ Dev1 --> Fw1
+ end
+
+ subgraph P2 [用户决策阶段]
+ direction TB
+ subgraph "最终用户"
+ direction TB
+ U1[中断发生在流程的
哪个环节?]
+ U2[开发者提供了
什么类型的信息?]
+ U3[我应该恢复这个
中断吗?]
+ U4[我应该为恢复
提供数据吗?]
+ U5[我应该提供什么类型的
恢复数据?]
+ U1 --> U2 --> U3 --> U4 --> U5
+ end
+ end
+
+
+ subgraph P3 [恢复阶段]
+ direction LR
+ subgraph Fw2 [框架]
+ direction TB
+ FR1[哪个实体正在中断
以及如何重新运行它?]
+ FR2[如何为被中断的实体
恢复上下文?]
+ FR3[如何将用户数据
路由到中断实体?]
+ FR1 --> FR2 --> FR3
+ end
+
+ subgraph Dev2 [开发者]
+ direction TB
+ DR1[我是显式的
恢复目标吗?]
+ DR2[如果不是目标,我应该
重新中断以继续吗?]
+ DR3[中断时我保留了
什么状态?]
+ DR4[如果提供了用户恢复数据,
该如何处理?]
+ DR1 --> DR2 --> DR3 --> DR4
+ end
+
+ Fw2 --> Dev2
+ end
+
+ P1 --> P2 --> P3
+
+ classDef dev fill:#e1f5fe
+ classDef fw fill:#f3e5f5
+ classDef user fill:#e8f5e8
+
+ class D1,D2,D3,DR1,DR2,DR3,DR4 dev
+ class F1,F2,F3,F4,FR1,FR2,FR3 fw
+ class U1,U2,U3,U4,U5 user
+```
因此,我们的目标是:
+
1. 帮助开发者尽可能轻松地回答上述问题。
2. 帮助最终用户尽可能轻松地回答上述问题。
3. 使框架能够自动并开箱即用地回答上述问题。
@@ -86,7 +156,7 @@ Based on the user's request, use the "BookTicket" tool to book tickets.`,
}
```
-2. 创建一个 Runner,配置 CheckPointStore,并运行,传入一个 CheckPointID。Eino 用 CheckPointStore 来保存 Agent 中断时的运行状态,这里用的 InMemoryStore,保存在内存中。实际使用中,推荐用分布式存储比如 redis。另外,Eino 用 CheckPointID 来唯一标识和串联“中断前”和“中断后”的两次(或多次)运行。
+1. 创建一个 Runner,配置 CheckPointStore,并运行,传入一个 CheckPointID。Eino 用 CheckPointStore 来保存 Agent 中断时的运行状态,这里用的 InMemoryStore,保存在内存中。实际使用中,推荐用分布式存储比如 redis。另外,Eino 用 CheckPointID 来唯一标识和串联“中断前”和“中断后”的两次(或多次)运行。
```go
a := NewTicketBookingAgent()
@@ -102,7 +172,7 @@ runner := adk.NewRunner(ctx, adk.RunnerConfig{
iter := runner.Query(ctx, "book a ticket for Martin, to Beijing, on 2025-12-01, the phone number is 1234567. directly call tool.", adk.WithCheckPointID("1"))
```
-3. 从 AgentEvent 中拿到 interrupt 信息 `event.Action.Interrupted.InterruptContexts[0].Info`,在这里是“准备给谁订哪趟车,是否同意”。同时会拿到一个 InterruptID(`event.Action.Interrupted.InterruptContexts[0].ID`),Eino 框架用这个 InterruptID 来标识“哪里发生了中断”。这里直接打印在了终端上,实际使用中,可能需要作为 HTTP 响应返回给前端。
+1. 从 AgentEvent 中拿到 interrupt 信息 `event.Action.Interrupted.InterruptContexts[0].Info`,在这里是“准备给谁订哪趟车,是否同意”。同时会拿到一个 InterruptID(`event.Action.Interrupted.InterruptContexts[0].ID`),Eino 框架用这个 InterruptID 来标识“哪里发生了中断”。这里直接打印在了终端上,实际使用中,可能需要作为 HTTP 响应返回给前端。
```go
var lastEvent *adk.AgentEvent
@@ -125,7 +195,7 @@ for {
interruptID := lastEvent.Action.Interrupted.InterruptContexts[0].ID
```
-4. 给用户展示 interrupt 信息,并接收到用户的响应,比如“同意”。在这个例子里面,都是在本地终端上展示给用户和接收用户输入的。在实际应用中,可能是用 ChatBot 做输入输出。
+1. 给用户展示 interrupt 信息,并接收到用户的响应,比如“同意”。在这个例子里面,都是在本地终端上展示给用户和接收用户输入的。在实际应用中,可能是用 ChatBot 做输入输出。
```go
var apResult *tool.ApprovalResult
@@ -167,10 +237,10 @@ tool 'BookTicket' interrupted with arguments '{"location":"Beijing","passenger_n
your input here: Y
```
-5. 调用 Runner.ResumeWithParams,传入同一个 InterruptID,以及用来恢复的数据,这里是“同意”。在这个例子里,首次 `Runner.Query` 和之后的 `Runner.ResumeWithParams` 是在一个实例中,在真实场景,可能是 ChatBot 前端的两次请求,打到服务端的两个实例中。只要 CheckPointID 两次相同,且给 Runner 配置的 CheckPointStore 是分布式存储,Eino 就能做到跨实例的中断恢复。
+1. 调用 Runner.ResumeWithParams,传入同一个 InterruptID,以及用来恢复的数据,这里是“同意”。在这个例子里,首次 `Runner.Query` 和之后的 `Runner.ResumeWithParams` 是在一个实例中,在真实场景,可能是 ChatBot 前端的两次请求,打到服务端的两个实例中。只要 CheckPointID 两次相同,且给 Runner 配置的 CheckPointStore 是分布式存储,Eino 就能做到跨实例的中断恢复。
```go
-_// here we directly resumes right in the same instance where the original `Runner.Query` happened.
+// here we directly resumes right in the same instance where the original `Runner.Query` happened.
// In the real world, the original `Runner.Run/Query` and the subsequent `Runner.ResumeWithParams`
// can happen in different processes or machines, as long as you use the same `CheckPointID`,
// and you provided a distributed `CheckPointStore` when creating the `Runner` instance.
@@ -193,7 +263,7 @@ for {
}
prints.Event(event)
-__}_
+}
```
完整样例输出:
@@ -224,19 +294,70 @@ answer: The ticket for Martin to Beijing on 2025-12-01 has been successfully boo
以下流程图说明了高层次的中断/恢复流程:
-
+```mermaid
+flowchart TD
+ U[最终用户]
+
+ subgraph R [Runner]
+ Run
+ Resume
+ end
+
+ U -->|初始输入| Run
+ U -->|恢复数据| Resume
+
+ subgraph E [(任意嵌套的)实体]
+ Agent
+ Tool
+ ...
+ end
+
+ subgraph C [运行上下文]
+ Address
+ InterruptState
+ ResumeData
+ end
+
+ Run -->|任意数量的 transfer / call| E
+ R <-->|存储/恢复| C
+ Resume -->|重放 transfer / call| E
+ C -->|自动分配给| E
+```
以下序列图显示了三个主要参与者之间按时间顺序的交互流程:
-
+```mermaid
+sequenceDiagram
+ participant D as 开发者
+ participant F as 框架
+ participant U as 最终用户
+
+
+ Note over D,F: 1. 中断阶段
+ D->>F: 调用 StatefulInterrupt()
指定信息和状态
+ F->>F: 持久化 InterruptID->{address, state}
+
+
+ Note over F,U: 2. 用户决策阶段
+ F->>U: 抛出 InterruptID->{address, info}
+ U->>U: 决定 InterruptID->{resume data}
+ U->>F: 调用 TargetedResume()
提供 InterruptID->{resume data}
+
+
+ Note over D,F: 3. 恢复阶段
+ F->>F: 路由到中断实体
+ F->>D: 提供状态和恢复数据
+ D->>D: 处理恢复
+```
## ADK 包 API
-ADK 包提供了用于构建具有human-in-the-loop能力的可中断 agent 的高级抽象。
+ADK 包提供了用于构建具有 human-in-the-loop 能力的可中断 agent 的高级抽象。
### 1. 用于中断的 API
#### `Interrupt`
+
创建一个基础的中断动作。当 agent 需要暂停执行以请求外部输入或干预,但不需要保存任何内部状态以供恢复时使用。
```go
@@ -244,12 +365,14 @@ func Interrupt(ctx context.Context, info any) *AgentEvent
```
**参数:**
+
- `ctx`: 正在运行组件的上下文。
- `info`: 描述中断原因的面向用户的数据。
**返回:** 带有中断动作的 `*AgentEvent`。
**示例:**
+
```go
// 在 agent 的 Run 方法内部:
@@ -260,6 +383,7 @@ return adk.Interrupt(ctx, "用户查询不明确,请澄清。")
---
#### `StatefulInterrupt`
+
创建一个中断动作,同时保存 agent 的内部状态。当 agent 具有必须恢复才能正确继续的内部状态时使用。
```go
@@ -267,6 +391,7 @@ func StatefulInterrupt(ctx context.Context, info any, state any) *AgentEvent
```
**参数:**
+
- `ctx`: 正在运行组件的上下文。
- `info`: 描述中断的面向用户的数据。
- `state`: agent 的内部状态对象,它将被序列化并存储。
@@ -274,6 +399,7 @@ func StatefulInterrupt(ctx context.Context, info any, state any) *AgentEvent
**返回:** 带有中断动作的 `*AgentEvent`。
**示例:**
+
```go
// 在 agent 的 Run 方法内部:
@@ -295,6 +421,7 @@ return adk.StatefulInterrupt(ctx, "在继续前需要用户反馈", currentState
---
#### `CompositeInterrupt`
+
为协调多个子组件的组件创建一个中断动作。它将一个或多个子 agent 的中断组合成一个单一、内聚的中断。任何包含子 agent 的 agent(例如,自定义的 `Sequential` 或 `Parallel` agent)都使用此功能来传播其子级的中断。
```go
@@ -303,6 +430,7 @@ func CompositeInterrupt(ctx context.Context, info any, state any,
```
**参数:**
+
- `ctx`: 正在运行组件的上下文。
- `info`: 描述协调器自身中断原因的面向用户的数据。
- `state`: 协调器 agent 自身的状态(例如,被中断的子 agent 的索引)。
@@ -311,6 +439,7 @@ func CompositeInterrupt(ctx context.Context, info any, state any,
**返回:** 带有中断动作的 `*AgentEvent`。
**示例:**
+
```go
// 在一个运行两个子 agent 的自定义顺序 agent 中...
subAgent1 := &myInterruptingAgent{}
@@ -336,6 +465,7 @@ if subInterruptEvent.Action.Interrupted != nil {
### 2. 用于获取中断信息的 API
#### `InterruptInfo` 和 `InterruptCtx`
+
当 agent 执行被中断时,`AgentEvent` 包含结构化的中断信息。`InterruptInfo` 结构体包含一个 `InterruptCtx` 对象列表,每个对象代表层级中的一个中断点。
`InterruptCtx` 为单个可恢复的中断点提供了一个完整的、面向用户的上下文。
@@ -379,10 +509,12 @@ if event.Action != nil && event.Action.Interrupted != nil {
### 3. 用于最终用户恢复的 API
-#### `(*Runner).ResumeWithParams`
+#### `(*Runner).``ResumeWithParams`
+
使用“显式定向恢复”策略从检查点继续中断的执行。这是最常见和最强大的恢复方式,允许您定位特定的中断点并为其提供数据。
使用此方法时:
+
- 地址在 `ResumeParams.Targets` 映射中的组件将是显式目标。
- 地址不在 `ResumeParams.Targets` 映射中的被中断组件必须重新中断自己以保留其状态。
@@ -392,6 +524,7 @@ func (r *Runner) ResumeWithParams(ctx context.Context, checkPointID string,
```
**参数:**
+
- `ctx`: 用于恢复的上下文。
- `checkPointID`: 要从中恢复的检查点的标识符。
- `params`: 中断参数,包含中断 ID 到恢复数据的映射。这些 ID 可以指向整个执行图中的任何可中断组件。
@@ -400,6 +533,7 @@ func (r *Runner) ResumeWithParams(ctx context.Context, checkPointID string,
**返回:** agent 事件的异步迭代器。
**示例:**
+
```go
// 收到中断事件后...
interruptID := interruptEvent.Action.Interrupted.InterruptContexts[0].ID
@@ -429,11 +563,12 @@ for event := range resumeIterator.Events() {
### 4. 用于开发者恢复的 API
#### `ResumeInfo` 结构体
+
`ResumeInfo` 持有恢复中断的 agent 执行所需的所有信息。它由框架创建并传递给 agent 的 `Resume` 方法。
```go
type ResumeInfo struct {
- // WasInterrupted 指示此 agent 是否是中断的直接来源。
+ // WasInterrupted 指示此 agent 在前一次 Runner 运行中是否发生了中断。
WasInterrupted bool
// InterruptState 持有通过 StatefulInterrupt 或 CompositeInterrupt 保存的状态。
@@ -450,6 +585,7 @@ type ResumeInfo struct {
```
**示例:**
+
```go
import (
"context"
@@ -462,7 +598,7 @@ import (
// 在 agent 的 Resume 方法内部:
func (a *myAgent) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
if !info.WasInterrupted {
- // 在恢复流程中不应发生。
+ // 已经进入了 Resume 方法,必定 WasInterrupted = true
return adk.NewAsyncIterator([]*adk.AgentEvent{{Err: errors.New("not an interrupt")}}, nil)
}
@@ -494,6 +630,7 @@ func (a *myAgent) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk.
### 1. 用于中断的 API
#### `Interrupt`
+
创建一个特殊错误,该错误向执行引擎发出信号,以在组件的特定地址处中断当前运行并保存检查点。这是单个、非复合组件发出可恢复中断信号的标准方式。
```go
@@ -501,12 +638,14 @@ func Interrupt(ctx context.Context, info any) error
```
**参数:**
+
- `ctx`: 正在运行组件的上下文,用于检索当前执行地址。
- `info`: 关于中断的面向用户的信息。此信息不会被持久化,但会通过 `InterruptCtx` 暴露给调用应用程序。
---
#### `StatefulInterrupt`
+
与 `Interrupt` 类似,但也保存组件的内部状态。状态保存在检查点中,并在恢复时通过 `GetInterruptState` 提供回组件。
```go
@@ -514,6 +653,7 @@ func StatefulInterrupt(ctx context.Context, info any, state any) error
```
**参数:**
+
- `ctx`: 正在运行组件的上下文。
- `info`: 关于中断的面向用户的信息。
- `state`: 中断组件需要持久化的内部状态。
@@ -521,6 +661,7 @@ func StatefulInterrupt(ctx context.Context, info any, state any) error
---
#### `CompositeInterrupt`
+
创建一个表示复合中断的特殊错误。它专为“复合”节点(如 `ToolsNode`)或任何协调多个独立的、可中断子流程的组件而设计。它将多个子中断错误捆绑成一个单一的错误,引擎可以将其解构为可恢复点的扁平列表。
```go
@@ -528,12 +669,14 @@ func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error)
```
**参数:**
+
- `ctx`: 正在运行的复合节点的上下文。
- `info`: 复合节点本身的面向用户的信息(可以为 `nil`)。
- `state`: 复合节点本身的状态(可以为 `nil`)。
- `errs`: 来自子流程的错误列表。这些可以是 `Interrupt`、`StatefulInterrupt` 或嵌套的 `CompositeInterrupt` 错误。
**示例:**
+
```go
// 一个并行运行多个进程的节点。
var errs []error
@@ -555,6 +698,7 @@ if len(errs) > 0 {
### 2. 用于获取中断信息的 API
#### `ExtractInterruptInfo`
+
从 `Runnable` 的 `Invoke` 或 `Stream` 方法返回的错误中提取结构化的 `InterruptInfo` 对象。这是应用程序在执行暂停后获取所有中断点列表的主要方式。
```go
@@ -566,6 +710,7 @@ if ok {
```
**示例:**
+
```go
// 在调用一个中断的图之后...
_, err := graph.Invoke(ctx, "initial input")
@@ -582,6 +727,7 @@ if err != nil {
### 3. 用于最终用户恢复的 API
#### `Resume`
+
通过不提供数据来定位一个或多个组件,为“显式定向恢复”操作准备上下文。当恢复行为本身就是信号时,这很有用。
```go
@@ -589,6 +735,7 @@ func Resume(ctx context.Context, interruptIDs ...string) context.Context
```
**示例:**
+
```go
// 中断后,我们得到两个中断 ID:id1 和 id2。
// 我们想在不提供特定数据的情况下恢复两者。
@@ -601,6 +748,7 @@ resumeCtx := compose.Resume(context.Background(), id1, id2)
---
#### `ResumeWithData`
+
准备一个上下文以使用数据恢复单个特定组件。它是 `BatchResumeWithData` 的便捷包装器。
```go
@@ -608,6 +756,7 @@ func ResumeWithData(ctx context.Context, interruptID string, data any) context.C
```
**示例:**
+
```go
// 使用特定数据恢复单个中断点。
resumeCtx := compose.ResumeWithData(context.Background(), interruptID, "这是您请求的特定数据。")
@@ -618,6 +767,7 @@ resumeCtx := compose.ResumeWithData(context.Background(), interruptID, "这是
---
#### `BatchResumeWithData`
+
这是准备恢复上下文的核心函数。它将恢复目标(中断 ID)及其相应数据的映射注入到上下文中。中断 ID 作为键存在的组件在调用 `GetResumeContext` 时将收到 `isResumeFlow = true`。
```go
@@ -625,6 +775,7 @@ func BatchResumeWithData(ctx context.Context, resumeData map[string]any) context
```
**示例:**
+
```go
// 一次性恢复多个中断点,每个中断点使用不同的数据。
resumeData := map[string]any{
@@ -641,6 +792,7 @@ resumeCtx := compose.BatchResumeWithData(context.Background(), resumeData)
### 4. 用于开发者恢复的 API
#### `GetInterruptState`
+
提供一种类型安全的方式来检查和检索先前中断的持久化状态。这是组件用来了解其过去状态的主要函数。
```go
@@ -648,11 +800,13 @@ func GetInterruptState[T any](ctx context.Context) (wasInterrupted bool, hasStat
```
**返回值:**
+
- `wasInterrupted`: 如果节点是先前中断的一部分,则为 `true`。
- `hasState`: 如果提供了状态并成功转换为类型 `T`,则为 `true`。
- `state`: 类型化的状态对象。
**示例:**
+
```go
// 在 lambda 或 tool 的执行逻辑内部:
wasInterrupted, hasState, state := compose.GetInterruptState[*MyState](ctx)
@@ -670,6 +824,7 @@ if wasInterrupted {
---
#### `GetResumeContext`
+
检查当前组件是否是恢复操作的目标,并检索用户提供的任何数据。这通常在 `GetInterruptState` 确认组件被中断后调用。
```go
@@ -677,11 +832,13 @@ func GetResumeContext[T any](ctx context.Context) (isResumeFlow bool, hasData bo
```
**返回值:**
+
- `isResumeFlow`: 如果组件被恢复调用明确指定为目标,则为 `true`。如果为 `false`,组件必须重新中断以保留其状态。
- `hasData`: 如果为此组件提供了数据,则为 `true`。
- `data`: 用户提供的类型化数据。
**示例:**
+
```go
// 在 lambda 或 tool 的执行逻辑内部,检查 GetInterruptState 之后:
wasInterrupted, _, oldState := compose.GetInterruptState[*MyState](ctx)
@@ -707,11 +864,11 @@ if wasInterrupted {
### 对地址的需求
-寻址系统旨在解决有效的human-in-the-loop交互中的三个基本需求:
+寻址系统旨在解决有效的 human-in-the-loop 交互中的三个基本需求:
-1. **状态附加**:要将本地状态附加到中断点,我们需要为每个中断点提供一个稳定、唯一的定位器。
-2. **定向恢复**:要为特定的中断点提供定向的恢复数据,我们需要一种精确识别每个点的方法。
-3. **中断定位**:要告诉最终用户中断在执行层级中的确切位置。
+1. **状态附加**:要将本地状态附加到中断点,我们需要为每个中断点提供一个稳定、唯一的定位器。
+2. **定向恢复**:要为特定的中断点提供定向的恢复数据,我们需要一种精确识别每个点的方法。
+3. **中断定位**:要告诉最终用户中断在执行层级中的确切位置。
### 地址如何满足这些需求
@@ -724,6 +881,7 @@ if wasInterrupted {
### 地址结构和段类型
#### `Address` 结构
+
```go
type Address struct {
Segments []AddressSegment
@@ -742,14 +900,77 @@ type AddressSegment struct {
**ADK 层视角** (简化的、以 Agent 为中心的视图):
-
+```mermaid
+graph TD
+ A[Address] --> B[AddressSegment 1]
+ A --> C[AddressSegment 2]
+ A --> D[AddressSegment 3]
+
+ B --> B1[Type: Agent]
+ B --> B2[ID: A]
+
+ C --> C1[Type: Agent]
+ C --> C2[ID: B]
+
+ D --> D1[Type: Tool]
+ D --> D2[ID: search_tool]
+ D --> D3[SubID: 1]
+
+ style A fill:#e1f5fe
+ style B fill:#f3e5f5
+ style C fill:#f3e5f5
+ style D fill:#f3e5f5
+ style B1 fill:#e8f5e8
+ style B2 fill:#e8f5e8
+ style C1 fill:#e8f5e8
+ style C2 fill:#e8f5e8
+ style D1 fill:#e8f5e8
+ style D2 fill:#e8f5e8
+ style D3 fill:#e8f5e8
+```
**Compose 层视角** (详细的、完整的层级视图):
-
+
+```mermaid
+graph TD
+ A[Address] --> B[AddressSegment 1]
+ A --> C[AddressSegment 2]
+ A --> D[AddressSegment 3]
+ A --> E[AddressSegment 4]
+
+ B --> B1[Type: Runnable]
+ B --> B2[ID: my_graph]
+
+ C --> C1[Type: Node]
+ C --> C2[ID: sub_graph]
+
+ D --> D1[Type: Node]
+ D --> D2[ID: tools_node]
+
+ E --> E1[Type: Tool]
+ E --> E2[ID: mcp_tool]
+ E --> E3[SubID: 1]
+
+ style A fill:#e1f5fe
+ style B fill:#f3e5f5
+ style C fill:#f3e5f5
+ style D fill:#f3e5f5
+ style E fill:#f3e5f5
+ style B1 fill:#e8f5e8
+ style B2 fill:#e8f5e8
+ style C1 fill:#e8f5e8
+ style C2 fill:#e8f5e8
+ style D1 fill:#e8f5e8
+ style D2 fill:#e8f5e8
+ style E1 fill:#e8f5e8
+ style E2 fill:#e8f5e8
+ style E3 fill:#e8f5e8
+```
### 特定层的地址段类型
#### ADK 层段类型
+
ADK 层提供了执行层级的简化、以 agent 为中心的抽象:
```go
@@ -762,12 +983,14 @@ const (
```
**关键特性:**
+
- **Agent 段**: 表示 agent 级别的执行段(通常省略 `SubID`)。
- **Tool 段**: 表示 tool 级别的执行段(`SubID` 用于确保唯一性)。
- **简化视图**: 为 agent 开发者抽象掉底层复杂性。
- **示例路径**: `Agent:A → Agent:B → Tool:search_tool:1`
#### Compose 层段类型
+
`compose` 层对整个执行层级提供了细粒度的控制和可见性:
```go
@@ -781,6 +1004,7 @@ const (
```
**关键特性:**
+
- **Runnable 段**: 表示顶层可执行文件(Graph、Workflow、Chain)。
- **Node 段**: 表示执行图中的单个节点。
- **Tool 段**: 表示 `ToolsNode` 内的特定 tool 调用。
@@ -795,17 +1019,17 @@ const (
## 向后兼容性
-human-in-the-loop框架保持与现有代码的完全向后兼容性。所有先前的中断和恢复模式将继续像以前一样工作,同时通过新的寻址系统提供增强的功能。
+human-in-the-loop 框架保持与现有代码的完全向后兼容性。所有先前的中断和恢复模式将继续像以前一样工作,同时通过新的寻址系统提供增强的功能。
### 1. 图中断兼容性
在节点/工具中使用已弃用的 `NewInterruptAndRerunErr` 或 `InterruptAndRerun` 的先前图中断流程将继续被支持,但需要一个关键的额外步骤:**错误包装**。
-由于这些遗留函数不是地址感知的,调用它们的组件有责任捕获错误,并使用 `WrapInterruptAndRerunIfNeeded` 辅助函数将地址信息包装进去。这通常在协调遗留组件的复合节点内部完成。
+由于这些函数感知不到新增的寻址系统,调用它们的组件有责任捕获错误,并使用 `WrapInterruptAndRerunIfNeeded` 辅助函数将地址信息包装进去。这通常在调用旧组件的复合节点(比如官方的 ToolsNode)内部完成。
-> **注意**:如果您选择**不**使用 `WrapInterruptAndRerunIfNeeded`,遗留行为将被保留。最终用户仍然可以像以前一样使用 `ExtractInterruptInfo` 从错误中获取信息。但是,由于产生的中断上下文将缺少正确的地址,因此将无法对该特定中断点使用新的定向恢复 API。要完全启用新的地址感知功能,必须进行包装。
+> **注意**:如果您选择**不**使用 `WrapInterruptAndRerunIfNeeded`,这些函数的原始行为将被保留。最终用户仍然可以像以前一样使用 `ExtractInterruptInfo` 从错误中获取信息。但是,由于产生的中断上下文将缺少正确的地址,因此将无法对该特定中断点使用新的定向恢复 API。要完全启用新的地址感知功能,必须进行包装。
-```go
+```java
// 1. 一个使用已弃用中断的遗留工具
func myLegacyTool(ctx context.Context, input string) (string, error) {
// ... tool 逻辑
@@ -836,18 +1060,17 @@ if err != nil {
}
```
-**增强功能**:通过包装错误,`InterruptInfo` 将包含一个正确的 `[]*InterruptCtx`,其中包含完全限定的地址,从而允许遗留组件无缝集成到新的人机协同框架中。
-
-### 2. 对编译时静态中断图的兼容性
+### 2. 兼容 **Compile 时添加的静态中断**
-通过 `WithInterruptBeforeNodes` 或 `WithInterruptAfterNodes` 添加的先前静态中断图继续有效,但状态处理的方式得到了显著改进。
+通过 `WithInterruptBeforeNodes` 或 `WithInterruptAfterNodes` 添加的静态中断继续有效,但状态处理的方式得到了改进。
-当静态中断被触发时,会生成一个 `InterruptCtx`,其地址指向图本身。关键在于,`InterruptCtx.Info` 字段现在直接暴露了该图的状态。
+当静态中断被触发时,会生成一个 `InterruptCtx`,其地址指向定义了该中断的图(或子图)。关键在于,`InterruptCtx.Info` 字段现在直接暴露了该图的状态。
这启用了一个更直接、更直观的工作流:
-1. 最终用户收到 `InterruptCtx`,并可以通过 `.Info` 字段检查图的实时状态。
-2. 他们可以直接修改这个状态对象。
-3. 然后,他们可以通过 `ResumeWithData` 和 `InterruptCtx.ID` 将修改后的状态对象传回以恢复执行。
+
+1. 最终用户收到 `InterruptCtx`,并可以通过 `.Info` 字段检查图的实时状态。
+2. 他们可以直接修改这个状态对象。
+3. 然后,他们可以通过 `ResumeWithData` 和 `InterruptCtx.ID` 将修改后的图 state 对象传回以恢复执行。
这种新模式通常不再需要使用旧的 `WithStateModifier` 选项,尽管为了完全的向后兼容性,该选项仍然可用。
@@ -894,13 +1117,16 @@ if isInterrupt {
与旧版 agent 的兼容性是在数据结构层面维护的,确保了旧的 agent 实现能在新框架内继续运作。其关键在于 `adk.InterruptInfo` 和 `adk.ResumeInfo` 结构体是如何被填充的。
**对最终用户(应用层)而言:**
+
当从 agent 收到一个中断时,`adk.InterruptInfo` 结构体中会同时填充以下两者:
+
- 新的、结构化的 `InterruptContexts` 字段。
- 遗留的 `Data` 字段,它将包含原始的中断信息(例如 `ChatModelAgentInterruptInfo` 或 `WorkflowInterruptInfo`)。
这使得最终用户可以逐步迁移他们的应用逻辑来使用更丰富的 `InterruptContexts`,同时在需要时仍然可以访问旧的 `Data` 字段。
**对 Agent 开发者而言:**
+
当一个旧版 agent 的 `Resume` 方法被调用时,它收到的 `adk.ResumeInfo` 结构体仍然包含现已弃用的嵌入式 `InterruptInfo` 字段。该字段被填充了相同的遗留数据结构,允许 agent 开发者维持其现有的恢复逻辑,而无需立即更新到新的地址感知 API。
```go
@@ -922,7 +1148,6 @@ if event.Action != nil && event.Action.Interrupted != nil {
}
}
-
// --- Agent 开发者视角 ---
// 在一个旧版 agent 的 Resume 方法内部:
@@ -937,7 +1162,6 @@ func (a *myLegacyAgent) Resume(ctx context.Context, info *adk.ResumeInfo) *adk.A
}
// ... 继续执行
- return a.Run(ctx, &adk.AgentInput{Input: "resumed execution"})
}
```
@@ -948,22 +1172,26 @@ func (a *myLegacyAgent) Resume(ctx context.Context, info *adk.ResumeInfo) *adk.A
- **增强的功能**: 新的寻址系统为所有中断提供了更丰富的结构化上下文 (`InterruptCtx`),同时旧的数据字段仍然会被填充以实现完全兼容。
- **灵活的状态管理**: 对于静态图中断,你可以选择通过 `.Info` 字段进行现代、直接的状态修改,或者继续使用旧的 `WithStateModifier` 选项。
-这种向后兼容性模型确保了现有用户的平滑过渡,同时为采用强大的新的 human-in-the-loop 交互功能提供了清晰的路径。
+这种向后兼容性模型确保了现有用户的平滑过渡,同时为采用新的 human-in-the-loop 功能提供了清晰的路径。
## 实现示例
-有关human-in-the-loop模式的完整、可工作的示例,请参阅 [eino-examples repository](https://github.com/cloudwego/eino-examples/pull/125)。该仓库包含四个作为独立示例实现的典型模式:
+有关 human-in-the-loop 模式的完整、可工作的示例,请参阅 [eino-examples repository](https://github.com/cloudwego/eino-examples/pull/125)。该仓库包含四个作为独立示例实现的典型模式:
### 1. 审批模式
+
在关键工具调用之前的简单、显式批准。非常适合不可逆操作,如数据库修改或金融交易。
-### 2. 审查与编辑模式
+### 2. 审查与编辑模式
+
高级模式,允许在执行前进行人工审查和原地编辑工具调用参数。非常适合纠正误解。
### 3. 反馈循环模式
+
迭代优化模式,其中 agent 生成内容,人类提供定性反馈以进行改进。
### 4. 追问模式
+
主动模式,其中 agent 识别出不充分的工具输出并请求澄清或下一步行动。
-这些示例演示了中断/恢复机制的实际用法,并附有可重用的工具包装器和详细文档。
\ No newline at end of file
+这些示例演示了中断/恢复机制的实际用法,并附有可重用的工具包装器和详细文档。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/_index.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/_index.md
index 261f75ee708..7c43c7783ae 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/_index.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino ADK: Agent 实现'
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md
index 5bab1dee30b..443e675cffa 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md
@@ -1,25 +1,23 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino ADK: ChatModelAgent'
weight: 1
---
-## ChatModelAgent 概述
+# ChatModelAgent 概述
-### Import Path
+## Import Path
-```
-import github.com/cloudwego/eino/adk
-```
+`import ``github.com/cloudwego/eino/adk`
-### 什么是 ChatModelAgent
+## 什么是 ChatModelAgent
`ChatModelAgent` 是 Eino ADK 中的一个核心预构建 的 Agent,它封装了与大语言模型(LLM)进行交互、并支持使用工具来完成任务的复杂逻辑。
-### ChatModelAgent ReAct 模式
+## ChatModelAgent ReAct 模式
`ChatModelAgent` 内使用了 [ReAct](https://react-lm.github.io/) 模式,该模式旨在通过让 ChatModel 进行显式的、一步一步的“思考”来解决复杂问题。为 `ChatModelAgent` 配置了工具后,它在内部的执行流程就遵循了 ReAct 模式:
@@ -30,7 +28,7 @@ import github.com/cloudwego/eino/adk
当没有配置工具时,`ChatModelAgent` 退化为一次 ChatModel 调用。
-
+
可以通过 ToolsConfig 为 ChatModelAgent 配置 Tool:
@@ -48,7 +46,7 @@ type ToolsConfig struct {
ToolsConfig 复用了 Eino Graph ToolsNodeConfig,详细参考:[Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。额外提供了 ReturnDirectly 配置,ChatModelAgent 调用配置在 ReturnDirectly 中的 Tool 后会直接退出。
-### ChatModelAgent 配置字段
+## ChatModelAgent 配置字段
```go
type ChatModelAgentConfig struct {
@@ -139,7 +137,7 @@ func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ .
}
```
-### ChatModelAgent Transfer
+## ChatModelAgent Transfer
`ChatModelAgent` 支持将其他 Agent 的元信息转为自身的 Tool ,经由 ChatModel 判断实现动态 Transfer:
@@ -147,7 +145,7 @@ func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ .
```go
const (
- _TransferToAgentInstruction _= `Available other agents: %s
+ TransferToAgentInstruction = `Available other agents: %s
Decision rule:
- If you're best suited for the question according to your description: ANSWER
@@ -163,14 +161,14 @@ func genTransferToAgentInstruction(ctx context.Context, agents []Agent) string {
agent.Name(ctx), agent.Description(ctx)))
}
- return fmt.Sprintf(_TransferToAgentInstruction_, sb.String(), _TransferToAgentToolName_)
+ return fmt.Sprintf(TransferToAgentInstruction, sb.String(), TransferToAgentToolName)
}
```
- `Transfer Tool` 运行会设置 Transfer Event,指定跳转到目标 Agent 上,完成后 ChatModelAgent 退出。
- Agent Runner 接收到 Transfer Event 后,跳转到目标 Agent 上执行,完成 Transfer 操作
-### ChatModelAgent AgentAsTool
+## ChatModelAgent AgentAsTool
当需要被调用的 Agent 不需要完整的运行上下文,仅需要明确清晰的入参即可正确运行时,该 Agent 可以转换为 Tool 交由 `ChatModelAgent` 判断调用:
@@ -198,12 +196,13 @@ a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
})
```
-## ChatModelAgent 使用示例
+# ChatModelAgent 使用示例
-### 场景说明
+## 场景说明
创建一个图书推荐 Agent,Agent 将能够根据用户的输入推荐相关图书。
+## 代码实现
### 步骤 1: 定义工具
@@ -311,6 +310,7 @@ func NewBookRecommendAgent() adk.Agent {
}
```
+###
### 步骤 4: 通过 Runner 运行
@@ -350,7 +350,7 @@ func main() {
}
```
-### 运行结果
+## 运行结果
```yaml
message:
@@ -377,9 +377,9 @@ usage: &{185 31 216}
======
```
-## ChatModelAgent 中断与恢复
+# ChatModelAgent 中断与恢复
-### 介绍
+## 介绍
`ChatModelAgent` 使用了 Eino Graph 实现,因此在 agent 中可以复用 Eino Graph 的 Interrupt&Resume 能力。
@@ -409,7 +409,7 @@ func WithNewInput(input string) tool.Option {
}
```
-### 场景说明
+## 示例
下面我们将基于上面【ChatModelAgent 使用示例】小节中的代码,为 `BookRecommendAgent` 增加一个工具 `ask_for_clarification`,当用户提供的信息不足以支持推荐时,Agent 将调用这个工具向用户询问更多信息,`ask_for_clarification` 使用了 Interrupt&Resume 能力来实现向用户“询问”。
@@ -594,7 +594,7 @@ usage: &{317 20 337}
======
```
-## 总结
+# 总结
`ChatModelAgent` 是 ADK 核心 Agent 实现,充当应用程序 "思考" 的部分,利用 LLM 强大的功能进行推理、理解自然语言、作出决策、生成相应、进行工具交互。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md
index aaa019b6f27..4e9ae338d63 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-11-14"
+date: "2025-12-01"
lastmod: ""
tags: []
title: 'Eino ADK MultiAgent: DeepAgents'
@@ -27,7 +27,7 @@ agent, err := deep.New(ctx, &deep.Config{})
DeepAgents 是一种多智能体(Multi-Agent)架构,其核心思想在于通过一个主代理(MainAgent)来协调、规划和委派任务。主代理本身不直接执行所有操作,而是利用其内置的大模型和一系列工具来与外部世界交互或将复杂任务分解给专门的子代理(SubAgents)。
-
+
上图展示了 DeepAgents 的核心组件与它们之间的调用关系:
@@ -45,7 +45,7 @@ DeepAgents 是一种多智能体(Multi-Agent)架构,其核心思想在于
WriteTodos 的 Description 描述了任务拆解、规划的原则,主 Agent 通过调用 WriteTodos 工具,在上下文中添加子任务列表来启发后续推理、执行过程:
-
+
1. 模型接收用户输入。
2. 模型调用 WriteTodos 工具,参数为依照 WriteTodos Description 产生的任务列表。这次工具调用被添加到上下文中,供后续参考。
@@ -101,14 +101,14 @@ Excel Agent 是一个“看得懂 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](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/deep)
### 运行结果
@@ -153,5 +153,6 @@ answer: 已成功将 `questions.csv` 表格中的第一列数据提取至新文
4b131973/first_column.csv`
操作过程中已处理路径拼接和异常捕获(如文件不存在、格式错误等问题),确保数据
-提取完整性和文件生成稳定性。若需要调整文件路径或对数据格式有进一步要求,请随时告知。
+提取完整性和文件生成稳定性。若需要调整文件路径或对数据格式有进一步要求,请随时告知
+。
```
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
index 3b40fe70248..561476180cc 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/plan_execute.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-03"
lastmod: ""
tags: []
title: 'Eino ADK MultiAgent: Plan-Execute Agent'
@@ -11,15 +11,13 @@ weight: 3
### Import Path
-```
-import github.com/cloudwego/eino/adk/prebuilt/planexecute
-```
+`import ``github.com/cloudwego/eino/adk/prebuilt/planexecute`
### 什么是 Plan-Execute Agent?
Plan-Execute Agent 是 Eino ADK 中一种基于「规划-执行-反思」范式的多智能体协作框架,旨在解决复杂任务的分步拆解、执行与动态调整问题。它通过 **Planner(规划器)**、**Executor(执行器)**和 **Replanner(重规划器)** 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态 replanning,最终达成用户目标。
-
+
Plan-Execute Agent 适用于需要多步骤推理、工具集成或动态调整策略的场景(如研究分析、复杂问题解决、自动化工作流等),其核心优势在于:
@@ -30,13 +28,15 @@ Plan-Execute Agent 适用于需要多步骤推理、工具集成或动态调整
### Plan-Execute Agent 结构
-Plan-Execute Agent 由三个核心智能体与一个协调器构成,各组件职责如下:
+Plan-Execute Agent 由三个核心智能体与一个协调器构成,基于 ADK 中提供的 ChatModelAgent 和 WorkflowAgents 能力共同完成构建:
+
+
#### 1. Planner
- **核心功能**:根据用户目标生成初始任务计划(结构化步骤序列)
- **实现方式**:
- - 基于工具调用模型(如 GPT-4),通过 `PlanTool` 生成符合 JSON Schema 的步骤列表
+ - 使用支持工具调用的模型(如 GPT-4),通过 `PlanTool` 生成符合 JSON Schema 的步骤列表
- 或直接使用支持结构化输出的模型,直接生成 `Plan` 格式结果
- **输出**:`Plan` 对象(包含有序步骤列表),存储于 Session 中供后续流程使用
@@ -143,7 +143,6 @@ type ReplannerConfig struct {
- 内层 `LoopAgent`:循环执行 `Executor` 和 `Replanner`,直至任务完成或达到最大迭代次数
```go
-// github.com/cloudwego/eino/adk/prebuilt/planexecute
// New creates a new plan execute agent with the given configuration.
func New(ctx context.Context, cfg *PlanExecuteConfig) (adk.Agent, error)
@@ -176,6 +175,7 @@ Plan-Execute Agent 的完整工作流程如下:
### 场景说明
实现一个「调研」Agent:
+
1. **Planner**:为调研目标规划详细步骤
2. **Executor**:执行计划中的首个步骤,必要时使用搜索工具(duckduckgo)
3. **Replanner**:评估执行结果,若信息不足则调整计划,否则生成最终总结
@@ -275,7 +275,7 @@ func newPlanExecuteAgent(ctx context.Context) adk.Agent {
replanner := newReplanner(ctx, model)
// 组合为 PlanExecuteAgent(固定 execute - replan 最大迭代 10 次)
- planExecuteAgent, err := planexecute.New(ctx, &planexecute.Config{
+ planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{
Planner: planner,
Executor: executor,
Replanner: replanner,
@@ -332,7 +332,7 @@ func main() {
}
// 打印智能体输出(计划、执行结果、最终响应等)
if msg, err := event.Output.MessageOutput.GetMessage(); err == nil && msg.Content != "" {
- // code...
+ log.Printf("\n=== Agent Output ===\n%s\n", msg.Content)
}
}
}
@@ -486,3 +486,25 @@ 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/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
index b0009eba555..dcd4f993b19 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/supervisor.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-03"
lastmod: ""
tags: []
title: 'Eino ADK MultiAgent: Supervisor Agent'
@@ -11,15 +11,13 @@ weight: 4
### Import Path
-```
-import github.com/cloudwego/eino/adk/prebuilt/supervisor
-```
+`import ``github.com/cloudwego/eino/adk/prebuilt/supervisor`
### 什么是 Supervisor Agent?
Supervisor Agent 是一种中心化多 Agent 协作模式,由一个监督者(Supervisor Agent) 和多个子 Agent(SubAgents)组成。Supervisor 负责任务的分配、子 Agent 执行过程的监控,以及子 Agent 完成后的结果汇总与下一步决策;子 Agent 则专注于执行具体任务,并在完成后通过 WithDeterministicTransferTo 自动将任务控制权交回 Supervisor。
-
+
该模式适用于需要动态协调多个专业 Agent 完成复杂任务的场景,例如:
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
index 20b07524d1f..258102bcd13 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/workflow.md
@@ -1,21 +1,19 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-03"
lastmod: ""
tags: []
title: 'Eino ADK: Workflow Agents'
weight: 2
---
-## Workflow Agents 概述
+# Workflow Agents 概述
-### 导入路径
+## 导入路径
-```
-import github.com/cloudwego/eino/adk
-```
+`import ``github.com/cloudwego/eino/adk`
-### 什么是 Workflow Agents
+## 什么是 Workflow Agents
Workflow Agents 是 eino ADK 中的一种特殊 Agent 类型,它允许开发者以预设的流程来组织和执行多个子 Agent。
@@ -29,13 +27,13 @@ Eino ADK 提供了三种基础的 Workflow Agent 类型:
这些 Workflow Agent 可以相互嵌套,构建更复杂的执行流程,满足各种业务场景需求。
-## SequentialAgent
+# SequentialAgent
-### 功能
+## 功能
SequentialAgent 是最基础的 Workflow Agent,它按照配置中提供的顺序,依次执行一系列子 Agent。每个子 Agent 执行完成后,其输出会通过 History 机制传递给下一个子 Agent,形成一个线性的执行链。
-
+
```go
type SequentialAgentConfig struct {
@@ -59,7 +57,7 @@ SequentialAgent 适用于以下场景:
- **管道式处理**:每个步骤的输出作为下个步骤的输入
- **有依赖关系的任务序列**:后续任务依赖前面任务的结果
-### 示例
+## 示例
示例展示了如何使用 SequentialAgent 创建一个三步骤的文档处理流水线:
@@ -245,13 +243,13 @@ func main() {
文档处理流水线执行完成!
```
-## LoopAgent
+# LoopAgent
-### 功能
+## 功能
LoopAgent 基于 SequentialAgent 实现,它会重复执行配置的子 Agent 序列,直到达到最大迭代次数或某个子 Agent 产生 ExitAction。LoopAgent 特别适用于需要迭代优化、反复处理或持续监控的场景。
-
+
```go
type LoopAgentConfig struct {
@@ -277,7 +275,7 @@ LoopAgent 适用于以下场景:
- **反复处理**:需要多轮处理才能达到满意结果的任务
- **自我改进**:Agent 根据前面的执行结果不断改进自己的输出
-### 示例
+## 示例
示例展示了如何使用 LoopAgent 创建一个代码优化循环:
@@ -445,12 +443,13 @@ func processData(data []int) []int {
运行结果为:
-```go
+```java
开始代码优化循环...
=== 第 1 轮 - CodeAnalyzer ===
分析提供的代码:
+```go
func processData(data []int) []int {
result := []int{}
for i := 0; i < len(data); i++ {
@@ -463,7 +462,7 @@ func processData(data []int) []int {
}
return result
}
-
+```
### 1. 性能瓶颈
@@ -500,6 +499,7 @@ func processData(data []int) []int {
优化代码:
+```go
func processData(data []int) []int {
if len(data) == 0 {
return nil
@@ -521,6 +521,7 @@ func processData(data []int) []int {
return result
}
+```
### 优化点说明
@@ -535,6 +536,7 @@ func processData(data []int) []int {
=== 第 1 轮 - CodeOptimizer ===
+```go
// processData 返回输入切片中所有大于最小元素的值。
// 如果输入为空,则返回 nil。
func processData(data []int) []int {
@@ -560,7 +562,7 @@ func processData(data []int) []int {
return result
}
-
+```
=== 第 1 轮 - ExitController ===
TERMINATE
@@ -568,14 +570,113 @@ 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 {
@@ -599,7 +700,7 @@ func processData(data []int) []int {
return result
}
-
+```
=== 第 5 轮 - ExitController ===
TERMINATE
@@ -610,17 +711,56 @@ TERMINATE
-## ParallelAgent
+## BreakLoop
-### 功能
+在 Loop Agent 中,当某个 Agent 需要中断循环运行时,您可以使用 ADK 提供的对应 Break Action。
-ParallelAgent 允许多个子 Agent 基于相同的输入上下文并发执行,所有子 Agent 同时开始执行,并等待全部完成后结束。这种模式特别适用于可以独立并行处理的任务,能够显著提高执行效率。
+```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 {
@@ -651,7 +791,7 @@ ParallelAgent 适用于以下场景:
- **性能优化**:通过并行执行减少总体执行时间
- **多专家咨询**:同时咨询多个专业领域的 Agent
-### 示例
+## 示例
示例展示了如何使用 ParallelAgent 同时从四个不同角度分析产品方案:
@@ -1120,6 +1260,6 @@ func main() {
共收到 4 个分析结果
```
-## 总结
+# 总结
-Workflow Agents 为 Eino ADK 提供了强大的多 Agent 协作能力,通过合理选择和组合这些 Workflow Agent,开发者可以构建出高效、可靠的多 Agent 协作系统,满足各种复杂的业务需求。
\ No newline at end of file
+Workflow Agents 为 Eino ADK 提供了强大的多 Agent 协作能力,通过合理选择和组合这些 Workflow Agent,开发者可以构建出高效、可靠的多 Agent 协作系统,满足各种复杂的业务需求。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md b/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
index 1569cb777f9..ee3908519be 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_interface.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino ADK: Agent 抽象'
weight: 3
---
-## Agent 定义
+# Agent 定义
Eino 定义了 Agent 的基础接口,实现此接口的 Struct 可被视为一个 Agent:
@@ -21,21 +21,21 @@ type Agent interface {
}
```
-
+
| Method | 说明 |
| Name | Agent 的名称,作为 Agent 的标识 |
| Description | Agent 的职能描述信息,主要用于让其他的 Agent 了解和判断该 Agent 的职责或功能 |
| Run | Agent 的核心执行方法,返回一个迭代器,调用者可以通过这个迭代器持续接收 Agent 产生的事件 |
-### AgentInput
+## AgentInput
Run 方法接收 AgentInput 作为 Agent 的输入:
```go
type AgentInput struct {
- Messages []_Message_
-_ _EnableStreaming bool
+ Messages []Message
+ EnableStreaming bool
}
type Message = *schema.Message
@@ -68,9 +68,9 @@ input := &adk.AgentInput{
- 当 `EnableStream=false` 时,二者均输出非流
- 当 `EnableStream=true` 时,ChatModel 输出流,Tool 因为不具备输出流的能力,仍然输出非流。
-
+
-### AgentRunOption
+## AgentRunOption
`AgentRunOption` 由 Agent 实现定义,可以在请求维度修改 Agent 配置或者控制 Agent 行为。
@@ -118,7 +118,7 @@ func genOpt() {
}
```
-### AsyncIterator
+## AsyncIterator
`Agent.Run` 返回了一个迭代器 `AsyncIterator[*AgentEvent]`:
@@ -183,7 +183,7 @@ func (m *MyAgent) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.Ag
}
```
-### AgentWithOptions
+## AgentWithOptions
使用 `AgentWithOptions` 方法可以在 Eino ADK Agent 中进行一些通用配置。
@@ -199,7 +199,7 @@ Eino ADK 当前内置支持的配置有:
- `WithDisallowTransferToParent`:配置该 SubAgent 不允许 Transfer 到 ParentAgent,会触发该 SubAgent 的 `OnDisallowTransferToParent` 回调方法
- `WithHistoryRewriter`:配置后该 Agent 在执行前会通过该方法重写接收到的上下文信息
-## AgentEvent
+# AgentEvent
AgentEvent 是 Agent 在其运行过程中产生的核心事件数据结构。其中包含了 Agent 的元信息、输出、行为和报错:
@@ -222,7 +222,7 @@ type AgentEvent struct {
func EventFromMessage(msg Message, msgStream MessageStream, role schema.RoleType, toolName string) *AgentEvent
```
-### AgentName & RunPath
+## AgentName & RunPath
`AgentName` 和 `RunPath` 字段是由框架自动进行填充,它们提供了关于事件来源的重要上下文信息,在复杂的、由多个 Agent 构成的系统中至关重要。
@@ -235,7 +235,7 @@ type RunStep struct {
- `AgentName` 标明了是哪一个 Agent 实例产生了当前的 AgentEvent 。
- `RunPath` 记录了到达当前 Agent 的完整调用链路。`RunPath` 是一个 `RunStep` 切片,它按顺序记录了从最初的入口 Agent 到当前产生事件的 Agent 的所有 `AgentName`。
-### AgentOutput
+## AgentOutput
`AgentOutput` 封装了 Agent 产生的输出。
@@ -275,7 +275,7 @@ type MessageVariant struct {
这样做的好处是,代码在需要根据消息类型进行路由或决策时, 无需深入解析 Message 对象的具体内容 ,可以直接从 MessageVariant 的顶层字段获取所需信息,从而简化了逻辑,提高了代码的可读性和效率。
-### AgentAction
+## AgentAction
Agent 产生包含 AgentAction 的 Event 可以控制多 Agent 协作,比如立刻退出、中断、跳转等:
@@ -288,6 +288,8 @@ type AgentAction struct {
Interrupted *InterruptInfo
TransferToAgent *TransferToAgentAction
+
+ BreakLoop *BreakLoopAction
CustomizedAction any
}
@@ -301,7 +303,7 @@ type TransferToAgentAction struct {
}
```
-Eino ADK 当前预设 Action 有三种:
+Eino ADK 当前预设 Action 有四种:
1. 退出:当 Agent 产生 Exit Action 时,Multi-Agent 会立刻退出
@@ -311,7 +313,7 @@ func NewExitAction() *AgentAction {
}
```
-2. 跳转:当 Agent 产生 Transfer Action 时,会跳转到目标 Agent 运行
+1. 跳转:当 Agent 产生 Transfer Action 时,会跳转到目标 Agent 运行
```go
func NewTransferToAgentAction(destAgentName string) *AgentAction {
@@ -319,7 +321,7 @@ func NewTransferToAgentAction(destAgentName string) *AgentAction {
}
```
-3. 中断:当 Agent 产生 Interrupt Action 时,会中断 Runner 的运行。由于中断可能发生在任何位置,同时中断时需要向外传递独特的信息,Action 中提供了 `Interrupted` 字段供 Agent 设置自定义数据,Runner 接收到 Interrupted 不为空的 Action 时则认为产生了中断。Interrupt & Resume 内部机制较为复杂,在 【Eino ADK: Agent Runner】-【Eino ADK: Interrupt & Resume】章节会展开详述。
+1. 中断:当 Agent 产生 Interrupt Action 时,会中断 Runner 的运行。由于中断可能发生在任何位置,同时中断时需要向外传递独特的信息,Action 中提供了 `Interrupted` 字段供 Agent 设置自定义数据,Runner 接收到 Interrupted 不为空的 Action 时则认为产生了中断。Interrupt & Resume 内部机制较为复杂,在 【Eino ADK: Agent Runner】-【Eino ADK: Interrupt & Resume】章节会展开详述。
```go
// 例如 ChatModelAgent 中断时,会发送如下的 AgentEvent:
@@ -329,3 +331,5 @@ h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{
},
}})
```
+
+4. 中止循环:当 LoopAgent 的一个子 Agent 发出 BreakLoopAction 时,对应的 LoopAgent 会停止循环并正常退出。
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md b/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md
index 900d455c078..79d905bae37 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_preview.md
@@ -1,27 +1,27 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino ADK: 概述'
weight: 2
---
-## 什么是 Eino ADK?
+# 什么是 Eino ADK?
Eino ADK 参考 [Google-ADK](https://google.github.io/adk-docs/agents/) 的设计,提供了 Go 语言 的 Agents 开发的灵活组合框架,即 Agent、Multi-Agent 开发框架。Eino ADK 为多 Agent 交互时,沉淀了通用的 上下文传递、事件流分发和转换、任务控制权转让、中断与恢复、通用切面等能力。 适用场景广泛、模型无关、部署无关,让 Agent、Multi-Agent 开发更加简单、便利,并提供完善的生产级应用的治理能力。
Eino ADK 旨在帮助开发者开发、管理 Agent 应用。提供灵活且鲁棒的开发环境,助力开发者搭建 对话智能体、非对话智能体、复杂任务、工作流等多种多样的 Agent 应用。
-## ADK 框架
+# ADK 框架
Eino ADK 的整体模块构成,如下图所示:
-
+
-### Agent Interface
+## Agent Interface
-Eino ADK 的核心是 Agent 抽象(Agent Interface),ADK 的所有功能设计均围绕 Agent 抽象展开。详解请见 [Eino ADK: Agent 抽象](/zh/docs/eino/core_modules/eino_adk/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 {
@@ -49,23 +49,23 @@ type Agent interface {
2. 启动 Agent 的异步任务,并传入 Generator,处理 AgentInput。Agent 在这个异步任务执行核心逻辑(例如 ChatModelAgent 调用 LLM),并在产生新的事件时写入到 Generator 中,供 Agent 调用方在 Iterator 中消费
3. 启动 2 中的任务后立即返回 Iterator
-### 多 Agent 协作
+## 多 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)
+围绕 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 的任务执行状态 |
+| Transfer | 直接将任务转让给另外一个 Agent,本 Agent 则执行结束后退出,不关心转让 Agent 的任务执行状态 |
| ToolCall(AgentAsTool) | 将 Agent 当成 ToolCall 调用,等待 Agent 的响应,并可获取被调用Agent 的输出结果,进行下一轮处理 |
- AgentInput 的上下文策略
-
+
| 上下文策略 | 描述 |
| 上游 Agent 全对话 | 获取本 Agent 的上游 Agent 的完整对话记录 |
| 全新任务描述 | 忽略掉上游 Agent 的完整对话记录,给出一个全新的任务总结,作为子 Agent 的 AgentInput 输入 |
@@ -73,7 +73,7 @@ Eino ADK 定义的 Agent 协作过程中的协作原语如下:
- 决策自主性
-
+
| 决策自主性 | 描述 |
| 自主决策 | 在 Agent 内部,基于其可选的下游 Agent, 如需协助时,自主选择下游 Agent 进行协助。 一般来说,Agent 内部是基于 LLM 进行决策,不过即使是基于预设逻辑进行选择,从 Agent 外部看依然视为自主决策 |
| 预设决策 | 事先预设好一个Agent 执行任务后的下一个 Agent。 Agent 的执行顺序是事先确定、可预测的 |
@@ -81,20 +81,20 @@ Eino ADK 定义的 Agent 协作过程中的协作原语如下:
围绕协作原语,Eino ADK 提供了如下的几种 Agent 组合原语:
-
+
| 类型 | 描述 | 运行模式 | 协作方式 | 上下文策略 | 决策自主性 |
-| SubAgents | 将用户提供的 agent 作为 父Agent,用户提供的 subAgents 列表作为 子Agents,组合而成可自主决策的 Agent,其中的 Name 和 Description 作为该 Agent 的名称标识和描述。当前限定一个 Agent 只能有一个 父 Agent可采用 SetSubAgents 函数,构建 「多叉树」 形式的 Multi-Agent在这个「多叉树」中,AgentName 需要保持唯一 |  | Transfer | 上游 Agent 全对话 | 自主决策 |
-| Sequential | 将用户提供的 SubAgents 列表,组合成按照顺序依次执行的 Sequential Agent,其中的 Name 和 Description 作为 Sequential Agent 的名称标识和描述。Sequential Agent 执行时,将 SubAgents 列表,按照顺序依次执行,直至将所有 Agent 执行一遍后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
-| Parallel | 将用户提供的 SubAgents 列表,组合成基于相同上下文,并发执行的 Parallel Agent,其中的 Name 和 Description 作为 Parallel Agent 的名称标识和描述。Parallel Agent 执行时,将 SubAgents 列表,并发执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
-| Loop | 将用户提供的 SubAgents 列表,按照数组顺序依次执行,循环往复,组合成 Loop Agent,其中的 Name 和 Description 作为 Loop Agent 的名称标识和描述。Loop Agent 执行时,将 SubAgents 列表,顺序执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
-| AgentAsTool | 将一个 Agent 转换成 Tool,被其他的 Agent 当成普通的 Tool 使用。一个 Agent 能否将其他 Agent 当成 Tool 进行调用,取决于自身的实现。Eino ADK 中提供的 ChatModelAgent 支持 AgentAsTool 的功能 |  | ToolCall | 全新任务描述 | 自主决策 |
+| SubAgents | 将用户提供的 agent 作为 父Agent,用户提供的 subAgents 列表作为 子Agents,组合而成可自主决策的 Agent,其中的 Name 和 Description 作为该 Agent 的名称标识和描述。当前限定一个 Agent 只能有一个 父 Agent可采用 SetSubAgents 函数,构建 「多叉树」 形式的 Multi-Agent在这个「多叉树」中,AgentName 需要保持唯一 |  | Transfer | 上游 Agent 全对话 | 自主决策 |
+| Sequential | 将用户提供的 SubAgents 列表,组合成按照顺序依次执行的 Sequential Agent,其中的 Name 和 Description 作为 Sequential Agent 的名称标识和描述。Sequential Agent 执行时,将 SubAgents 列表,按照顺序依次执行,直至将所有 Agent 执行一遍后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+| Parallel | 将用户提供的 SubAgents 列表,组合成基于相同上下文,并发执行的 Parallel Agent,其中的 Name 和 Description 作为 Parallel Agent 的名称标识和描述。Parallel Agent 执行时,将 SubAgents 列表,并发执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+| Loop | 将用户提供的 SubAgents 列表,按照数组顺序依次执行,循环往复,组合成 Loop Agent,其中的 Name 和 Description 作为 Loop Agent 的名称标识和描述。Loop Agent 执行时,将 SubAgents 列表,顺序执行,待所有 Agent 执行完成后结束。 |  | Transfer | 上游 Agent 全对话 | 预设决策 |
+| AgentAsTool | 将一个 Agent 转换成 Tool,被其他的 Agent 当成普通的 Tool 使用。一个 Agent 能否将其他 Agent 当成 Tool 进行调用,取决于自身的实现。Eino ADK 中提供的 ChatModelAgent 支持 AgentAsTool 的功能 |  | ToolCall | 全新任务描述 | 自主决策 |
-### ChatModelAgent
+## ChatModelAgent
`ChatModelAgent` 是 Eino ADK 对 Agent 的关键实现,它封装了与大语言模型的交互逻辑,实现了 ReAct 范式的 Agent,基于 Eino 中的 Graph 编排出 ReAct Agent 控制流,通过 callbacks.Handler 导出 ReAct Agent 运行过程中产生的事件,转换成 AgentEvent 返回。
-想要进一步了解 ChatModelAgent,请见:[Eino ADK: ChatModelAgent](/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model)
+想要进一步了解 ChatModelAgent,请见:[Eino ADK: ChatModelAgent [New]](/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model)
```go
type ChatModelAgentConfig struct {
@@ -138,7 +138,7 @@ func NewChatModelAgent(_ context.Context, config *ChatModelAgentConfig) (*ChatMo
}
```
-## AgentRunner
+# AgentRunner
AgentRunner 是 Agent 的执行器,为 Agent 运行所需要的拓展功能加以支持,详解请见:[Eino ADK: Agent 扩展](/zh/docs/eino/core_modules/eino_adk/agent_extension)
diff --git a/content/zh/docs/eino/core_modules/eino_adk/agent_quickstart.md b/content/zh/docs/eino/core_modules/eino_adk/agent_quickstart.md
index 84c523cd105..b38e60bdbb6 100644
--- a/content/zh/docs/eino/core_modules/eino_adk/agent_quickstart.md
+++ b/content/zh/docs/eino/core_modules/eino_adk/agent_quickstart.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-09-30"
+date: "2025-12-11"
lastmod: ""
tags: []
title: 'Eino ADK: Quickstart'
weight: 1
---
-## Installation
+# Installation
Eino 自 0.5.0 版本正式提供 ADK 功能供用户使用,您可以在项目中输入下面命令来升级 Eino:
@@ -16,7 +16,7 @@ Eino 自 0.5.0 版本正式提供 ADK 功能供用户使用,您可以在项目
go get github.com/cloudwego/eino@latest
```
-## Agent
+# Agent
### 什么是 Eino ADK
@@ -60,34 +60,34 @@ type Agent interface {
下方表格和图提供了这些基础拓展与封装的特点,区别,与关系。后续章节中将展开介绍每种类型的原理与细节:
-
+
| 类别 | ChatModel Agent | Workflow Agents | Custom Logic | EinoBuiltInAgent(supervisor, plan-execute) |
| 功能 | 思考,生成,工具调用 | 控制 Agent 之间的执行流程 | 运行自定义逻辑 | 开箱即用的 Multi-agent 模式封装 |
| 核心 | LLM | 预确定的执行流程(顺序,并发,循环) | 自定义代码 | 基于 Eino 实践积累的经验,对前三者的高度封装 |
| 用途 | 生成,动态决策 | 结构化处理,编排 | 定制需求 | 特定场景内的开箱即用 |
-
+
-## ADK Examples
+# ADK Examples
[Eino-examples](https://github.com/cloudwego/eino-examples/tree/main/adk) 项目中提供了多种 ADK 的实施样例,您可以参考样例代码与简介,对 adk 能力构建初步的认知:
-
+
| 项目路径 | 简介 | 结构图 |
-| 顺序工作流案例 | 该示例代码展示了基于 eino adk 的 Workflow 模式构建的一个顺序执行的多智能体工作流。顺序工作流构建:通过 adk.NewSequentialAgent 创建一个名为 ResearchAgent 的顺序执行智能体,内部包含两个子智能体(SubAgents)PlanAgent 和 WriterAgent,分别负责研究计划制定和报告撰写。子智能体职责明确:PlanAgent 接收研究主题,生成详细且逻辑清晰的研究计划;WriterAgent 根据该研究计划撰写结构完整的学术报告。输入输出串联:PlanAgent 输出的研究计划作为 WriterAgent 的输入,形成清晰的上下游数据流,体现业务步骤的顺序依赖。 |  |
-| 循环工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 LoopAgent,构建了一个反思迭代型智能体框架。迭代反思框架:通过 adk.NewLoopAgent 创建 ReflectionAgent,包含两个子智能体 MainAgent 和 CritiqueAgent,支持最多 5 次迭代,形成主任务解决与批判反馈的闭环。主智能体(MainAgent):负责根据用户任务生成初步解决方案,追求准确完整的答案输出。批判智能体(CritiqueAgent):对主智能体输出进行质量审查,反馈改进意见,若结果满意则终止循环,提供最终总结。循环机制:利用 LoopAgent 的迭代能力,实现在多轮反思中不断优化解决方案,提高输出质量和准确性。 |  |
-| 并行工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 ParallelAgent,构建了一个并发信息搜集框架:并发运行框架:通过 adk.NewParallelAgent 创建 DataCollectionAgent,包含多个信息采集子智能体。子智能体职责分配:每个子智能体负责一个渠道的信息采集与分析,彼此之间无需交互,功能边界清晰。并发运行:Parallel Agent 能够同时从多个数据源启动信息收集任务,处理效率相较于串行方式显著提升。 |  |
-| 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。多层级智能体结构:实现了一个顶层 Supervisor Agent,管理两个子智能体 ——Research Agent(负责信息检索)和 Math Agent(负责数学运算)。Math Agent 内部再细分三个子智能体:Subtract Agent、Multiply Agent 和 Divide 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 分为两层:第二层是由 execute + replan agent 构成的 loop agent,即 replan 后可能需要重新 execute(重新规划后需要查询旅行信息 / 请求用户继续澄清问题)第一层是由 plan agent + 第二层构造的 loop agent 构成的 sequential agent,即 plan 仅执行一次,然后交由 loop agent 执行 |  |
-| 书籍推荐 agent(运行中断与恢复) | 该代码展示了基于 eino adk 框架构建的一个书籍推荐聊天智能体实现,体现了 Agent 运行中断与恢复功能。Agent 构建:通过 adk.NewChatModelAgent 创建一个名为 BookRecommender 的聊天智能体,用于根据用户请求推荐书籍。工具集成:集成了两个工具 —— 搜索书籍的 BookSearch 工具 和 询问澄清信息的 AskForClarification 工具,支持多轮交互和信息补充。状态管理:实现了简单的内存 CheckPoint 存储,支持会话的断点续接,保证上下文连续性。事件驱动:通过迭代 runner.Query 和 runner.Resume 获取事件流,处理执行过程中的各种事件及错误。自定义输入:支持动态接收用户输入,利用工具选项传入新的查询请求,灵活驱动任务流程。 |  |
+| 顺序工作流案例 | 该示例代码展示了基于 eino adk 的 Workflow 模式构建的一个顺序执行的多智能体工作流。顺序工作流构建:通过 adk.NewSequentialAgent 创建一个名为 ResearchAgent 的顺序执行智能体,内部包含两个子智能体(SubAgents)PlanAgent 和 WriterAgent,分别负责研究计划制定和报告撰写。子智能体职责明确:PlanAgent 接收研究主题,生成详细且逻辑清晰的研究计划;WriterAgent 根据该研究计划撰写结构完整的学术报告。输入输出串联:PlanAgent 输出的研究计划作为 WriterAgent 的输入,形成清晰的上下游数据流,体现业务步骤的顺序依赖。 |  |
+| 循环工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 LoopAgent,构建了一个反思迭代型智能体框架。迭代反思框架:通过 adk.NewLoopAgent 创建 ReflectionAgent,包含两个子智能体 MainAgent 和 CritiqueAgent,支持最多 5 次迭代,形成主任务解决与批判反馈的闭环。主智能体(MainAgent):负责根据用户任务生成初步解决方案,追求准确完整的答案输出。批判智能体(CritiqueAgent):对主智能体输出进行质量审查,反馈改进意见,若结果满意则终止循环,提供最终总结。循环机制:利用 LoopAgent 的迭代能力,实现在多轮反思中不断优化解决方案,提高输出质量和准确性。 |  |
+| 并行工作流案例 | 该示例代码基于 eino adk 的 Workflow 模式中的 ParallelAgent,构建了一个并发信息搜集框架:并发运行框架:通过 adk.NewParallelAgent 创建 DataCollectionAgent,包含多个信息采集子智能体。子智能体职责分配:每个子智能体负责一个渠道的信息采集与分析,彼此之间无需交互,功能边界清晰。并发运行:Parallel Agent 能够同时从多个数据源启动信息收集任务,处理效率相较于串行方式显著提升。 |  |
+| 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。多层级智能体结构:实现了一个顶层 Supervisor Agent,管理两个子智能体 ——Research Agent(负责信息检索)和 Math Agent(负责数学运算)。Math Agent 内部再细分三个子智能体:Subtract Agent、Multiply Agent 和 Divide 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 分为两层:第二层是由 execute + replan agent 构成的 loop agent,即 replan 后可能需要重新 execute(重新规划后需要查询旅行信息 / 请求用户继续澄清问题)第一层是由 plan agent + 第二层构造的 loop agent 构成的 sequential agent,即 plan 仅执行一次,然后交由 loop agent 执行 |  |
+| 书籍推荐 agent(运行中断与恢复) | 该代码展示了基于 eino adk 框架构建的一个书籍推荐聊天智能体实现,体现了 Agent 运行中断与恢复功能。Agent 构建:通过 adk.NewChatModelAgent 创建一个名为 BookRecommender 的聊天智能体,用于根据用户请求推荐书籍。工具集成:集成了两个工具 —— 搜索书籍的 BookSearch 工具 和 询问澄清信息的 AskForClarification 工具,支持多轮交互和信息补充。状态管理:实现了简单的内存 CheckPoint 存储,支持会话的断点续接,保证上下文连续性。事件驱动:通过迭代 runner.Query 和 runner.Resume 获取事件流,处理执行过程中的各种事件及错误。自定义输入:支持动态接收用户输入,利用工具选项传入新的查询请求,灵活驱动任务流程。 |  |
-## What's Next
+# What's Next
经过 Quickstart 概览,您应该对 Eino ADK 与 Agent 有了基础的认知。
接下来的文章将深入介绍 ADK 的核心概念,助您理解 Eino ADK 的工作原理并更好的使用它:
-
+
diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/_index.md b/content/zh/docs/eino/core_modules/flow_integration_components/_index.md
index b72e6290b37..d5726067361 100644
--- a/content/zh/docs/eino/core_modules/flow_integration_components/_index.md
+++ b/content/zh/docs/eino/core_modules/flow_integration_components/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: Flow 集成'
diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md b/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
index 61d78c0ac31..356878b8a06 100644
--- a/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
+++ b/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-01-17"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino Tutorial: Host Multi-Agent '
-weight: 0
+title: 'Eino Tutorial: Host Multi-Agent'
+weight: 2
---
-Host Multi-Agent 是一个 Host 做意图识别后,跳转到某个专家 agent 做实际的生成。
+Host Multi-Agent 是一个 Host 做意图识别后,跳转到某个专家 agent 做实际的生成。只转发,不生成子任务。
以一个简单的“日记助手”做例子:可以写日记、读日记、根据日记回答问题。
@@ -410,6 +410,7 @@ toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Mes
> 💡
> 尝试添加 prompt 来约束模型在工具调用时不额外输出文本,例如:“如果需要调用 tool,直接输出 tool,不要输出文本”。
+>
> 不同模型受 prompt 影响可能不同,实际使用时需要自行调整 prompt 并验证效果。
### Host 同时选择多个 Specialist
diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md b/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
index e2b86890e14..12f98f5c1c4 100644
--- a/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
+++ b/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-09"
lastmod: ""
tags: []
-title: 'Eino: React Agent 使用手册'
-weight: 0
+title: 'Eino: ReAct Agent 使用手册'
+weight: 1
---
# 简介
@@ -48,10 +48,8 @@ func main() {
// 初始化所需的 tools
tools := compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- mytool,
- ...
- },
+ InvokableTools: []tool.InvokableTool{mytool},
+ StreamableTools: []tool.StreamableTool{myStreamTool},
}
// 创建 agent
@@ -65,15 +63,31 @@ func main() {
### Model
-model 接收一个 ChatModel,在 agent 内部,会调用 BindTools 接口,定义为:
+由于 ReAct Agent 需要进行工具调用,Model 需要拥有 ToolCall 的能力,因此需要配置一个 ToolCallingChatModel。
+
+在 Agent 内部,会调用 WithTools 接口向模型注册 Agent 的工具列表,定义为:
```go
-type ChatModel interface {
+// 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)
-
- BindTools(tools []*schema.ToolInfo) error
+ *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)
}
```
@@ -98,7 +112,7 @@ func openaiExample() {
Model: "{{model name which support tool call}}",
})
- agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ agent, err := react.NewAgent(ctx, react.AgentConfig{
ToolCallingModel: chatModel,
ToolsConfig: ...,
})
@@ -108,10 +122,9 @@ func arkExample() {
arkModel, err := ark.NewChatModel(context.Background(), ark.ChatModelConfig{
APIKey: os.Getenv("ARK_API_KEY"),
Model: os.Getenv("ARK_MODEL"),
- BaseURL: os.Getenv("ARK_BASE_URL"),
})
- agent, err := react.NewAgent(ctx, &react.AgentConfig{
+ agent, err := react.NewAgent(ctx, react.AgentConfig{
ToolCallingModel: arkModel,
ToolsConfig: ...,
})
@@ -172,10 +185,7 @@ userInfoTool := utils.NewTool(
})
toolConfig := &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- mytool,
- ...
- },
+ InvokableTools: []tool.InvokableTool{invokeTool},
}
```
@@ -188,7 +198,7 @@ MessageModifier 会在每次把所有历史消息传递给 ChatModel 之前执
type MessageModifier func(ctx context.Context, input []*schema.Message) []*schema.Message
```
-在 Agent 中配置 MessageModifier 可以修改传入模型的 messages:
+在 Agent 中配置 MessageModifier 可以修改传入模型的 messages,常用于添加前置的 system message:
```go
import (
@@ -198,13 +208,13 @@ import (
func main() {
agent, err := react.NewAgent(ctx, &react.AgentConfig{
- ToolCallingModel: toolableChatModel,
+ 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("你是一个 Go 开发专家."))
+ res = append(res, schema.SystemMessage("你是一个 golang 开发专家."))
res = append(res, input...)
return res
},
@@ -213,12 +223,29 @@ func main() {
agent.Generate(ctx, []*schema.Message{schema.UserMessage("写一个 hello world 的代码")})
// 模型得到的实际输入为:
// []*schema.Message{
- // {Role: schema.System, Content: "You are an expert Go developer."},
- // {Role: schema.Human, Content: "Write a hello world code"}
+ // {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。
@@ -243,7 +270,7 @@ func main() {
```go
a, err = NewAgent(ctx, &AgentConfig{
- ToolCallingModel: cm,
+ Model: cm,
ToolsConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{fakeTool, fakeStreamTool},
},
@@ -285,7 +312,7 @@ func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[
}
```
-上述默认实现适用于:模型输出的 Tool Call Message 中只有 Tool Call。¡
+上述默认实现适用于:模型输出的 Tool Call Message 中只有 Tool Call。
默认实现不适用的情况:在输出 Tool Call 前,有非空的 content chunk。此时,需要自定义 tool Call checker 如下:
@@ -293,32 +320,30 @@ func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[
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
- }
+ msg, err := sr.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ // finish
+ break
+ }
- if len(msg.ToolCalls) > 0 {
- return true, nil
- }
+ return false, err
+ }
+
+ if len(msg.ToolCalls) > 0 {
+ return true, nil
+ }
}
-
return false, nil
-}
+}
```
-
-上面这个自定义 StreamToolCallChecker,在极端情况下可能需要判断所有包是否包含 ToolCall,从而导致“流式判断”的效果丢失。如果希望尽可能保留“流式判断”效果,解决这一问题的建议是:
+上面这个自定义 StreamToolCallChecker,在极端情况下可能需要判断**所有包**是否包含 ToolCall,从而导致“流式判断”的效果丢失。如果希望尽可能保留“流式判断”效果,解决这一问题的建议是:
> 💡
-> 尝试添加 prompt 来约束模型在工具调用时不额外输出文本,例如:“如果需要调用tool,直接输出tool,不要输出文本”。
->
-> 不同模型受 prompt 影响可能不同,实际使用时需要自行调整prompt并验证效果。
+> 尝试添加 prompt 来约束模型在工具调用时不额外输出文本,例如:“如果需要调用 tool,直接输出 tool,不要输出文本”。
+>
+> 不同模型受 prompt 影响可能不同,实际使用时需要自行调整 prompt 并验证效果。
## 调用
@@ -329,7 +354,7 @@ agent, _ := react.NewAgent(...)
var outMessage *schema.Message
outMessage, err = agent.Generate(ctx, []*schema.Message{
- schema.UserMessage("写一个 Go 的 hello world 程序"),
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
})
```
@@ -340,7 +365,7 @@ agent, _ := react.NewAgent(...)
var msgReader *schema.StreamReader[*schema.Message]
msgReader, err = agent.Stream(ctx, []*schema.Message{
- schema.UserMessage("写一个 Go 的 hello world 程序"),
+ schema.UserMessage("写一个 golang 的 hello world 程序"),
})
for {
@@ -365,6 +390,9 @@ for {
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.
//
@@ -376,6 +404,81 @@ func BuildAgentCallback(modelHandler *template.ModelCallbackHandler, toolHandler
}
```
+### 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 中:
diff --git a/content/zh/docs/eino/ecosystem_integration/_index.md b/content/zh/docs/eino/ecosystem_integration/_index.md
index 08bae24a81a..8b558e5ef61 100644
--- a/content/zh/docs/eino/ecosystem_integration/_index.md
+++ b/content/zh/docs/eino/ecosystem_integration/_index.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-07-21"
lastmod: ""
tags: []
title: 'Eino: 组件集成'
-weight: 0
+weight: 5
---
## 组件集成
diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
index 4475bbe3730..21e7df34784 100644
--- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
+++ b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md
@@ -7,21 +7,7 @@ title: Callback - APMPlus
weight: 0
---
-Eino 基于 [graph callback](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) 能力封装了 APMPlus 的 trace 和 metrics 能力(参见 [文档](https://www.volcengine.com/docs/6431/69092) 和 [控制台](https://console.volcengine.com/apmplus-server))
-
-## 特性
-
-- 实现了 `github.com/cloudwego/eino/callbacks.Handler` 接口
-- 实现了会话功能,能够将 Eino 应用中的同一个会话里的多个请求关联起来,并进行 [AI 会话分析](https://www.volcengine.com/docs/6431/1587839)
-- 易于与 Eino 应用集成
-
-## 安装
-
-```bash
-go get github.com/cloudwego/eino-ext/callbacks/apmplus
-```
-
-## 快速开始
+Eino 基于 [graph callback](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) 能力封装了 APMPlus 的 trace 和 metrics 能力(参见 [文档](https://www.volcengine.com/docs/6431/69092) 和 [控制台](https://console.volcengine.com/apmplus-server)),使用示例如下:
```go
package main
@@ -39,7 +25,7 @@ func main() {
// 创建apmplus handler
cbh, showdown, err := apmplus.NewApmplusHandler(&apmplus.Config{
Host: "apmplus-cn-beijing.volces.com:4317",
- AppKey: "appkey-xxx",
+ AppKey: "xxx",
ServiceName: "eino-app",
Release: "release/v0.0.1",
})
@@ -54,17 +40,6 @@ func main() {
/*
* compose and run graph
*/
- runner, _ := g.Compile(ctx)
- runner.Run(ctx)
-
- // 如想设置会话信息, 可通过 apmplus.SetSession 方法
- ctx = apmplus.SetSession(ctx, apmplus.WithSessionID("your_session_id"), apmplus.WithUserID("your_user_id"))
-
- // 执行 runner
- result, _ := runner.Invoke(ctx, "input")
- /*
- * 处理结果
- */
// 等待所有trace和metrics上报完成后退出
showdown(ctx)
@@ -74,9 +49,3 @@ func main() {
可以在 [APMPlus](https://console.volcengine.com/apmplus-server) 中查看 trace 和 metrics:
-
-在调用 Eino 应用过程中传入 Session 信息后,可以在 APMPlus 中查看 [AI 会话分析](https://www.volcengine.com/docs/6431/1587839):
-
-
-
-
diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
index a53ff56ac84..30241c5e7e0 100644
--- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
+++ b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md
@@ -1,54 +1,55 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Callback - cozeloop
+title: Callback - CozeLoop
weight: 0
---
-# CozeLoop 回调
+# **CozeLoop 回调**
这是一个为 [CozeLoop](https://github.com/cloudwego/eino) 实现的 Trace 回调。该工具实现了 `Handler` 接口,可以与 Eino 的应用无缝集成以提供增强的可观测能力。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/internel/callbacks.Handler` 接口
- 易于与 Eino 应用集成
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/callbacks/cozeloop
```
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- ccb "github.com/cloudwego/eino-ext/callbacks/cozeloop"
- "github.com/cloudwego/eino/callbacks"
- "github.com/coze-dev/cozeloop-go"
+ ccb "github.com/cloudwego/eino-ext/callbacks/cozeloop"
+ "github.com/cloudwego/eino/callbacks"
+ "github.com/coze-dev/cozeloop-go"
)
func main() {
- // 设置相关环境变量
- // COZELOOP_WORKSPACE_ID=your workspace id
- // COZELOOP_API_TOKEN=your token
- client, err := cozeloop.NewClient()
- if err != nil {
- panic(err)
- }
- defer client.Close(ctx)
- // 在服务 init 时 once 调用
- handler := ccb.NewLoopHandler(client)
- callbacks.AppendGlobalHandlers(handler)
+ // 设置相关环境变量
+ // COZELOOP_WORKSPACE_ID=your workspace id
+ // COZELOOP_API_TOKEN=your token
+ client, err := cozeloop.NewClient()
+ if err != nil {
+ panic(err)
+ }
+ defer client.Close(ctx)
+ // 在服务 init 时 once 调用
+ handler := ccb.NewLoopHandler(client)
+ callbacks.AppendGlobalHandlers(handler)
}
```
-## 更多详情
-- [CozeLoop 文档](https://github.com/coze-dev/cozeloop-go)
\ No newline at end of file
+## **更多详情**
+
+- [CozeLoop 文档](https://github.com/coze-dev/cozeloop-go)
diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
index 283cb0abc52..3809d1f3e6d 100644
--- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
+++ b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md
@@ -18,19 +18,20 @@ import (
)
func main() {
- cbh, flusher := langfuse.NewLangfuseHandler(&langfuse.Config{
+ cbh, flusher := NewLangfuseHandler(&Config{
Host: "https://cloud.langfuse.com",
PublicKey: "pk-xxx",
SecretKey: "sk-xxx",
})
- defer flusher() // 等待所有trace上报完成后退出
-
+
callbacks.AppendGlobalHandlers(cbh) // 设置langfuse为全局callback
g := NewGraph[string,string]()
/*
* compose and run graph
*/
+
+ flusher() // 等待所有trace上报完成后退出
}
```
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md b/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md
index 2e9ccaced57..e4ea8e1730d 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-10"
+date: "2025-07-21"
lastmod: ""
tags: []
title: ChatModel
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
index cd0d25f5f24..edfd8034f7a 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md
@@ -1,20 +1,23 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - ark
+title: ChatModel - ARK
weight: 0
---
+## **ARK 模型**
+
一个 [Eino](https://github.com/cloudwego/eino) 的 Volcengine Ark 模型实现,实现了 `ToolCallingChatModel` 接口。这使得与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
该软件包提供了两种不同的模型:
+
- **ChatModel**:用于基于文本和多模态的聊天补全。
- **ImageGenerationModel**:用于从文本提示或图像生成图像。
-- **ResponseAPI**:包含有助于与 openai API 交互的方法和其他服务。
+- **ResponseAPI**:包含有助于与 openai API 交互的方法和其他服务
-## 特性
+## 特性[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#%E7%89%B9%E6%80%A7)
- 实现了 `github.com/cloudwego/eino/components/model.Model`
- 轻松与 Eino 的模型系统集成
@@ -30,12 +33,6 @@ weight: 0
go get github.com/cloudwego/eino-ext/components/model/ark@latest
```
----
-
-## 聊天
-
-该模型用于标准聊天和文本生成任务。
-
### 快速开始
以下是如何使用 `ChatModel` 的快速示例:
@@ -44,73 +41,73 @@ go get github.com/cloudwego/eino-ext/components/model/ark@latest
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "how do you generate answer for user question as a machine, please answer in short?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output: \n")
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
-
- sr, err := chatModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- log.Printf("stream final output: \n")
- log.Printf(" request_id: %s \n")
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf("body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "how do you generate answer for user question as a machine, please answer in short?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output: \n")
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
+
+ sr, err := chatModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ log.Printf("stream final output: \n")
+ log.Printf(" request_id: %s \n")
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf("body: %s \n", string(respBody))
}
```
@@ -188,12 +185,8 @@ type ChatModelConfig struct {
}
```
----
-
## 图像生成
-该模型专门用于从文本提示生成图像。
-
### 快速开始
以下是如何使用 `ImageGenerationModel` 的快速示例:
@@ -202,65 +195,65 @@ type ChatModelConfig struct {
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and an image generation model ID
- imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_IMAGE_MODEL_ID"), // Use an appropriate image model ID
- })
-
- if err != nil {
- log.Fatalf("NewImageGenerationModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "a photo of a cat sitting on a table",
- },
- }
-
- msg, err := imageGenerationModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output:")
- log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s", string(respBody))
-
- sr, err := imageGenerationModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- log.Printf("stream output:")
- index := 0
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
- log.Printf("stream chunk %d: body: %s \n", index, string(respBody))
- index++
- }
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and an image generation model ID
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_IMAGE_MODEL_ID"), // Use an appropriate image model ID
+ })
+
+ if err != nil {
+ log.Fatalf("NewImageGenerationModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "a photo of a cat sitting on a table",
+ },
+ }
+
+ msg, err := imageGenerationModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s", string(respBody))
+
+ sr, err := imageGenerationModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ log.Printf("stream output:")
+ index := 0
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
+ log.Printf("stream chunk %d: body: %s \n", index, string(respBody))
+ index++
+ }
}
```
@@ -269,7 +262,6 @@ func main() {
`ImageGenerationModel` 可以使用 `ark.ImageGenerationConfig` 结构进行配置:
```go
-
type ImageGenerationConfig struct {
// For authentication, APIKey is required as the image generation API only supports API Key authentication.
// For authentication details, see: https://www.volcengine.com/docs/82379/1298459
@@ -340,914 +332,1297 @@ type ImageGenerationConfig struct {
}
```
-
## 示例
### 文本生成
```go
-
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "how do you generate answer for user question as a machine, please answer in short?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output:")
- log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s", string(respBody))
-
- sr, err := chatModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- log.Printf("stream final output:")
- log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "how do you generate answer for user question as a machine, please answer in short?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s", string(respBody))
+
+ sr, err := chatModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ log.Printf("stream final output:")
+ log.Printf(" request_id: %s", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
-
```
-### 多模态支持(图片理解)
+### 多模态支持(图片理解)[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#%E5%A4%9A%E6%A8%A1%E6%80%81%E6%94%AF%E6%8C%81%E5%9B%BE%E7%89%87%E7%90%86%E8%A7%A3)
```go
-
package main
import (
- "context"
- "encoding/base64"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "log"
+ "os"
- "github.com/cloudwego/eino/components/prompt"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/components/prompt"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- multiModalMsg := schema.UserMessage("")
- image, err := os.ReadFile("./examples/generate_with_image/eino.png")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v \n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- multiModalMsg.UserInputMultiContent = []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/png",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("Ark ChatModel output:%v \n", resp)
-
- // demonstrate how to use ChatTemplate to generate with image
- imgPlaceholder := "{img}"
- ctx = context.Background()
- chain := compose.NewChain[map[string]any, *schema.Message]()
- _ = chain.AppendChatTemplate(prompt.FromMessages(schema.FString,
- &schema.Message{
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imgPlaceholder,
- MIMEType: "image/png",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }))
- _ = chain.AppendChatModel(chatModel)
- r, err := chain.Compile(ctx)
- if err != nil {
- log.Fatalf("Compile failed, err=%v", err)
- }
-
- resp, err = r.Invoke(ctx, map[string]any{
- "img": imageStr,
- })
- if err != nil {
- log.Fatalf("Run failed, err=%v", err)
- }
-
- log.Printf("Ark ChatModel output with ChatTemplate:%v \n", resp)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ multiModalMsg := schema.UserMessage("")
+ image, err := os.ReadFile("./examples/generate_with_image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v \n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ multiModalMsg.UserInputMultiContent = []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ multiModalMsg,
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("Ark ChatModel output:%v \n", resp)
+
+ // demonstrate how to use ChatTemplate to generate with image
+ imgPlaceholder := "{img}"
+ ctx = context.Background()
+ chain := compose.NewChain[map[string]any, *schema.Message]()
+ _ = chain.AppendChatTemplate(prompt.FromMessages(schema.FString,
+ &schema.Message{
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imgPlaceholder,
+ MIMEType: "image/png",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ }))
+ _ = chain.AppendChatModel(chatModel)
+ r, err := chain.Compile(ctx)
+ if err != nil {
+ log.Fatalf("Compile failed, err=%v", err)
+ }
+
+ resp, err = r.Invoke(ctx, map[string]any{
+ "img": imageStr,
+ })
+ if err != nil {
+ log.Fatalf("Run failed, err=%v", err)
+ }
+
+ log.Printf("Ark ChatModel output with ChatTemplate:%v \n", resp)
}
-
```
-### 流式生成
+### 流式生成[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#%E6%B5%81%E5%BC%8F%E7%94%9F%E6%88%90)
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- }, ark.WithReasoningEffort(arkModel.ReasoningEffortHigh))
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close() // do not forget to close the stream
-
- msgs := make([]*schema.Message, 0)
-
- log.Printf("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- msgs = append(msgs, msg)
- if err != nil {
- log.Printf("stream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Printf("ConcatMessages failed, err=%v", err)
- return
- }
-
- log.Printf("output: %s \n", msg.Content)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ }, ark.WithReasoningEffort(arkModel.ReasoningEffortHigh))
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close() // do not forget to close the stream
+
+ msgs := make([]*schema.Message, 0)
+
+ log.Printf("typewriter output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ msgs = append(msgs, msg)
+ if err != nil {
+ log.Printf("stream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Printf("ConcatMessages failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output: %s \n", msg.Content)
}
-
```
-### 工具调用
+### 工具调用[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8)
```go
-
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Printf("BindForcedTools failed, err=%v", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output:%v \n", resp)
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("BindForcedTools failed, err=%v", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output:%v \n", resp)
}
-
```
-### 图像生成
+### 图像生成[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#%E5%9B%BE%E5%83%8F%E7%94%9F%E6%88%90-1)
```go
-
package main
import (
- "context"
- "encoding/json"
- "errors"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
- "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+ "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func ptr[T any](v T) *T {
- return &v
+ return &v
}
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
-
- // Control the size of image generated by the model.
- Size: "1K",
-
- // Control whether to generate a set of images.
- SequentialImageGeneration: ark.SequentialImageGenerationAuto,
-
- // Control the maximum number of images to generate
- SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{
- MaxImages: ptr(2),
- },
-
- // Control the format of the generated jpeg image.
- ResponseFormat: ark.ImageResponseFormatURL,
-
- // Control whether to add a watermark to the generated image
- DisableWatermark: false,
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "generate two images of a cat",
- },
- }
-
- // Use ImageGeneration API
- msg, err := imageGenerationModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output:")
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
-
- sr, err := imageGenerationModel.Stream(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- log.Printf("stream output:")
- index := 0
- chunks := make([]*schema.Message, 0, 1024)
- for {
- msgChunk, err := sr.Recv()
- if errors.Is(err, io.EOF) {
- break
- }
- if err != nil {
- log.Fatalf("Stream Recv failed, err=%v", err)
- }
-
- chunks = append(chunks, msgChunk)
-
- respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
- log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
- index++
- }
-
- msg, err = schema.ConcatMessages(chunks)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- log.Printf("stream final output:")
- log.Printf(" request_id: %s \n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+
+ // Control the size of image generated by the model.
+ Size: "1K",
+
+ // Control whether to generate a set of images.
+ SequentialImageGeneration: ark.SequentialImageGenerationAuto,
+
+ // Control the maximum number of images to generate
+ SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{
+ MaxImages: ptr(2),
+ },
+
+ // Control the format of the generated jpeg image.
+ ResponseFormat: ark.ImageResponseFormatURL,
+
+ // Control whether to add a watermark to the generated image
+ DisableWatermark: false,
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "generate two images of a cat",
+ },
+ }
+
+ // Use ImageGeneration API
+ msg, err := imageGenerationModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
+
+ sr, err := imageGenerationModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ log.Printf("stream output:")
+ index := 0
+ chunks := make([]*schema.Message, 0, 1024)
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ chunks = append(chunks, msgChunk)
+
+ respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
+ log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
+ index++
+ }
+
+ msg, err = schema.ConcatMessages(chunks)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ log.Printf("stream final output:")
+ log.Printf(" request_id: %s \n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
-
```
-### ContextAPI 前缀缓存
+### ContextAPI 前缀缓存[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#contextapi-%E5%89%8D%E7%BC%80%E7%BC%93%E5%AD%98)
```go
-
package main
import (
- "context"
- "encoding/json"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- info, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
- schema.UserMessage("my name is megumin"),
- }, 3600)
- if err != nil {
- log.Fatalf("CreatePrefix failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "what is my name?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &info.ContextID,
- }))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("\ngenerate output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
-
- outStreamReader, err := chatModel.Stream(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &info.ContextID,
- }))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- var msgs []*schema.Message
- for {
- item, e := outStreamReader.Recv()
- if e == io.EOF {
- break
- }
- if e != nil {
- log.Fatal(e)
- }
-
- msgs = append(msgs, item)
- }
- msg, err = schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- log.Printf("\nstream output: \n")
- log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ info, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
+ schema.UserMessage("my name is megumin"),
+ }, 3600)
+ if err != nil {
+ log.Fatalf("CreatePrefix failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "what is my name?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &info.ContextID,
+ }))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("\ngenerate output: \n")
+ log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
+
+ outStreamReader, err := chatModel.Stream(ctx, inMsgs, ark.WithCache(&ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &info.ContextID,
+ }))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ var msgs []*schema.Message
+ for {
+ item, e := outStreamReader.Recv()
+ if e == io.EOF {
+ break
+ }
+ if e != nil {
+ log.Fatal(e)
+ }
+
+ msgs = append(msgs, item)
+ }
+ msg, err = schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ log.Printf("\nstream output: \n")
+ log.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
-
```
-### Response API 前缀缓存
+### Response API 前缀缓存[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#response-api-%E5%89%8D%E7%BC%80%E7%BC%93%E5%AD%98)
```go
-
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- Cache: &ark.CacheConfig{
- APIType: ptrOf(ark.ResponsesAPI),
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "article_content_extractor",
- Desc: "Extract key statements and chapter summaries from the provided article content",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "content": {
- Type: schema.String,
- Desc: "The full article content to analyze and extract key information from",
- Required: true,
- },
- }),
- },
- })
-
- if err != nil {
- log.Fatalf("BindTools failed, err=%v", err)
- }
-
- // create response prefix cache, note: more than 1024 tokens are required, otherwise the prefix cache cannot be created
- cacheInfo, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
- schema.SystemMessage(`Once upon a time, in a quaint little village surrounded by vast green forests and blooming meadows, there lived a spirited young girl known as Little Red Riding Hood. She earned her name from the vibrant red cape that her beloved grandmother had sewn for her, a gift that she cherished deeply. This cape was more than just a piece of clothing; it was a symbol of the bond between her and her grandmother, who lived on the other side of the great woods, near a sparkling brook that bubbled merrily all year round.
-
- One sunny morning, Little Red Riding Hood's mother called her into the cozy kitchen, where the aroma of freshly baked bread filled the air. “My dear,” she said, “your grandmother isn’t feeling well today. I want you to take her this basket of treats. There are some delicious cakes, a jar of honey, and her favorite herbal tea. Can you do that for me?”
-
- Little Red Riding Hood’s eyes sparkled with excitement as she nodded eagerly. “Yes, Mama! I’ll take good care of them!” Her mother handed her a beautifully woven basket, filled to the brim with goodies, and reminded her, “Remember to stay on the path and don’t talk to strangers.”
-
- “I promise, Mama!” she replied confidently, pulling her red hood over her head and setting off on her adventure. The sun shone brightly, and birds chirped merrily as she walked, making her feel like she was in a fairy tale.
-
- As she journeyed through the woods, the tall trees whispered secrets to one another, and colorful flowers danced in the gentle breeze. Little Red Riding Hood was so enchanted by the beauty around her that she began to hum a tune, her voice harmonizing with the sounds of nature.
-
- However, unbeknownst to her, lurking in the shadows was a cunning wolf. The wolf was known throughout the forest for his deceptive wit and insatiable hunger. He watched Little Red Riding Hood with keen interest, contemplating his next meal.
-
- “Good day, little girl!” the wolf called out, stepping onto the path with a friendly yet sly smile.
-
- Startled, she halted and took a step back. “Hello there! I’m just on my way to visit my grandmother,” she replied, clutching the basket tightly.
-
- “Ah, your grandmother! I know her well,” the wolf said, his eyes glinting with mischief. “Why don’t you pick some lovely flowers for her? I’m sure she would love them, and I’m sure there are many beautiful ones just off the path.”
-
- Little Red Riding Hood hesitated for a moment but was easily convinced by the wolf’s charming suggestion. “That’s a wonderful idea! Thank you!” she exclaimed, letting her curiosity pull her away from the safety of the path. As she wandered deeper into the woods, her gaze fixed on the vibrant blooms, the wolf took a shortcut towards her grandmother’s house.
-
- When the wolf arrived at Grandma’s quaint cottage, he knocked on the door with a confident swagger. “It’s me, Little Red Riding Hood!” he shouted in a high-pitched voice to mimic the girl.
-
- “Come in, dear!” came the frail voice of the grandmother, who had been resting on her cozy bed, wrapped in warm blankets. The wolf burst through the door, his eyes gleaming with the thrill of his plan.
-
- With astonishing speed, the wolf gulped down the unsuspecting grandmother whole. Afterward, he dressed in her nightgown, donning her nightcap and climbing into her bed. He lay there, waiting for Little Red Riding Hood to arrive, concealing his wicked smile behind a facade of innocence.
-
- Meanwhile, Little Red Riding Hood was merrily picking flowers, completely unaware of the impending danger. After gathering a beautiful bouquet of wildflowers, she finally made her way back to the path and excitedly skipped towards her grandmother’s cottage.
-
- Upon arriving, she noticed the door was slightly ajar. “Grandmother, it’s me!” she called out, entering the dimly lit home. It was silent, with only the faint sound of an old clock ticking in the background. She stepped into the small living room, a feeling of unease creeping over her.
-
- “Grandmother, are you here?” she asked, peeking into the bedroom. There, she saw a figure lying under the covers.
-
- “Grandmother, what big ears you have!” she exclaimed, taking a few cautious steps closer.
-
- “All the better to hear you with, my dear,” the wolf replied in a voice that was deceptively sweet.
-
- “Grandmother, what big eyes you have!” Little Red Riding Hood continued, now feeling an unsettling chill in the air.
-
- “All the better to see you with, my dear,” the wolf said, his eyes narrowing as he tried to contain his glee.
-
- “Grandmother, what big teeth you have!” she exclaimed, the terror flooding her senses as she began to realize this was no ordinary visit.
-
- “All the better to eat you with!” the wolf roared, springing out of the bed with startling speed.
-
- Just as the wolf lunged towards her, a brave woodsman, who had been passing by the cottage and heard the commotion, burst through the door. His strong presence was a beacon of hope in the dire situation. “Stay back, wolf!” he shouted with authority, brandishing his axe.
-
- The wolf, taken aback by the sudden intrusion, hesitated for a moment. Before he could react, the woodsman swung his axe with determination, and with a swift motion, he drove the wolf away, rescuing Little Red Riding Hood and her grandmother from certain doom.
-
- Little Red Riding Hood was shaking with fright, but relief washed over her as the woodsman helped her grandmother out from behind the bed where the wolf had hidden her. The grandmother, though shaken, was immensely grateful to the woodsman for his bravery. “Thank you so much! You saved us!” she cried, embracing him warmly.
-
- Little Red Riding Hood, still in shock but filled with gratitude, looked up at the woodsman and said, “I promise I will never stray from the path again. Thank you for being our hero!”
-
- From that day on, the woodland creatures spoke of the brave woodsman who saved Little Red Riding Hood and her grandmother. Little Red Riding Hood learned a valuable lesson about being cautious and listening to her mother’s advice. The bond between her and her grandmother grew stronger, and they often reminisced about that day’s adventure over cups of tea, surrounded by cookies and laughter.
-
- To ensure safety, Little Red Riding Hood always took extra precautions when traveling through the woods, carrying a small whistle her grandmother had given her. It would alert anyone nearby if she ever found herself in trouble again.
-
- And so, in the heart of that small village, life continued, filled with love, laughter, and the occasional adventure, as Little Red Riding Hood and her grandmother thrived, forever grateful for the friendship of the woodsman who had acted as their guardian that fateful day.
-
- And they all lived happily ever after.
-
- The end.`),
- }, 300)
- if err != nil {
- log.Fatalf("CreatePrefixCache failed, err=%v", err)
- }
-
- // use cache information in subsequent requests
- cacheOpt := &ark.CacheOption{
- APIType: ark.ResponsesAPI,
- HeadPreviousResponseID: &cacheInfo.ResponseID,
- }
-
- outMsg, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("What is the main idea expressed above?"),
- }, ark.WithCache(cacheOpt))
-
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- respID, ok := ark.GetResponseID(outMsg)
- if !ok {
- log.Fatalf("not found response id in message")
- }
-
- log.Printf("\ngenerate output: \n")
- log.Printf(" request_id: %s\n", respID)
- respBody, _ := json.MarshalIndent(outMsg, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Cache: &ark.CacheConfig{
+ APIType: ptrOf(ark.ResponsesAPI),
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "article_content_extractor",
+ Desc: "Extract key statements and chapter summaries from the provided article content",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "content": {
+ Type: schema.String,
+ Desc: "The full article content to analyze and extract key information from",
+ Required: true,
+ },
+ }),
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("BindTools failed, err=%v", err)
+ }
+
+ // create response prefix cache, note: more than 1024 tokens are required, otherwise the prefix cache cannot be created
+ cacheInfo, err := chatModel.CreatePrefixCache(ctx, []*schema.Message{
+ schema.SystemMessage(`Once upon a time, in a quaint little village surrounded by vast green forests and blooming meadows, there lived a spirited young girl known as Little Red Riding Hood. She earned her name from the vibrant red cape that her beloved grandmother had sewn for her, a gift that she cherished deeply. This cape was more than just a piece of clothing; it was a symbol of the bond between her and her grandmother, who lived on the other side of the great woods, near a sparkling brook that bubbled merrily all year round.
+
+ One sunny morning, Little Red Riding Hood's mother called her into the cozy kitchen, where the aroma of freshly baked bread filled the air. “My dear,” she said, “your grandmother isn’t feeling well today. I want you to take her this basket of treats. There are some delicious cakes, a jar of honey, and her favorite herbal tea. Can you do that for me?”
+
+ Little Red Riding Hood’s eyes sparkled with excitement as she nodded eagerly. “Yes, Mama! I’ll take good care of them!” Her mother handed her a beautifully woven basket, filled to the brim with goodies, and reminded her, “Remember to stay on the path and don’t talk to strangers.”
+
+ “I promise, Mama!” she replied confidently, pulling her red hood over her head and setting off on her adventure. The sun shone brightly, and birds chirped merrily as she walked, making her feel like she was in a fairy tale.
+
+ As she journeyed through the woods, the tall trees whispered secrets to one another, and colorful flowers danced in the gentle breeze. Little Red Riding Hood was so enchanted by the beauty around her that she began to hum a tune, her voice harmonizing with the sounds of nature.
+
+ However, unbeknownst to her, lurking in the shadows was a cunning wolf. The wolf was known throughout the forest for his deceptive wit and insatiable hunger. He watched Little Red Riding Hood with keen interest, contemplating his next meal.
+
+ “Good day, little girl!” the wolf called out, stepping onto the path with a friendly yet sly smile.
+
+ Startled, she halted and took a step back. “Hello there! I’m just on my way to visit my grandmother,” she replied, clutching the basket tightly.
+
+ “Ah, your grandmother! I know her well,” the wolf said, his eyes glinting with mischief. “Why don’t you pick some lovely flowers for her? I’m sure she would love them, and I’m sure there are many beautiful ones just off the path.”
+
+ Little Red Riding Hood hesitated for a moment but was easily convinced by the wolf’s charming suggestion. “That’s a wonderful idea! Thank you!” she exclaimed, letting her curiosity pull her away from the safety of the path. As she wandered deeper into the woods, her gaze fixed on the vibrant blooms, the wolf took a shortcut towards her grandmother’s house.
+
+ When the wolf arrived at Grandma’s quaint cottage, he knocked on the door with a confident swagger. “It’s me, Little Red Riding Hood!” he shouted in a high-pitched voice to mimic the girl.
+
+ “Come in, dear!” came the frail voice of the grandmother, who had been resting on her cozy bed, wrapped in warm blankets. The wolf burst through the door, his eyes gleaming with the thrill of his plan.
+
+ With astonishing speed, the wolf gulped down the unsuspecting grandmother whole. Afterward, he dressed in her nightgown, donning her nightcap and climbing into her bed. He lay there, waiting for Little Red Riding Hood to arrive, concealing his wicked smile behind a facade of innocence.
+
+ Meanwhile, Little Red Riding Hood was merrily picking flowers, completely unaware of the impending danger. After gathering a beautiful bouquet of wildflowers, she finally made her way back to the path and excitedly skipped towards her grandmother’s cottage.
+
+ Upon arriving, she noticed the door was slightly ajar. “Grandmother, it’s me!” she called out, entering the dimly lit home. It was silent, with only the faint sound of an old clock ticking in the background. She stepped into the small living room, a feeling of unease creeping over her.
+
+ “Grandmother, are you here?” she asked, peeking into the bedroom. There, she saw a figure lying under the covers.
+
+ “Grandmother, what big ears you have!” she exclaimed, taking a few cautious steps closer.
+
+ “All the better to hear you with, my dear,” the wolf replied in a voice that was deceptively sweet.
+
+ “Grandmother, what big eyes you have!” Little Red Riding Hood continued, now feeling an unsettling chill in the air.
+
+ “All the better to see you with, my dear,” the wolf said, his eyes narrowing as he tried to contain his glee.
+
+ “Grandmother, what big teeth you have!” she exclaimed, the terror flooding her senses as she began to realize this was no ordinary visit.
+
+ “All the better to eat you with!” the wolf roared, springing out of the bed with startling speed.
+
+ Just as the wolf lunged towards her, a brave woodsman, who had been passing by the cottage and heard the commotion, burst through the door. His strong presence was a beacon of hope in the dire situation. “Stay back, wolf!” he shouted with authority, brandishing his axe.
+
+ The wolf, taken aback by the sudden intrusion, hesitated for a moment. Before he could react, the woodsman swung his axe with determination, and with a swift motion, he drove the wolf away, rescuing Little Red Riding Hood and her grandmother from certain doom.
+
+ Little Red Riding Hood was shaking with fright, but relief washed over her as the woodsman helped her grandmother out from behind the bed where the wolf had hidden her. The grandmother, though shaken, was immensely grateful to the woodsman for his bravery. “Thank you so much! You saved us!” she cried, embracing him warmly.
+
+ Little Red Riding Hood, still in shock but filled with gratitude, looked up at the woodsman and said, “I promise I will never stray from the path again. Thank you for being our hero!”
+
+ From that day on, the woodland creatures spoke of the brave woodsman who saved Little Red Riding Hood and her grandmother. Little Red Riding Hood learned a valuable lesson about being cautious and listening to her mother’s advice. The bond between her and her grandmother grew stronger, and they often reminisced about that day’s adventure over cups of tea, surrounded by cookies and laughter.
+
+ To ensure safety, Little Red Riding Hood always took extra precautions when traveling through the woods, carrying a small whistle her grandmother had given her. It would alert anyone nearby if she ever found herself in trouble again.
+
+ And so, in the heart of that small village, life continued, filled with love, laughter, and the occasional adventure, as Little Red Riding Hood and her grandmother thrived, forever grateful for the friendship of the woodsman who had acted as their guardian that fateful day.
+
+ And they all lived happily ever after.
+
+ The end.`),
+ }, 300)
+ if err != nil {
+ log.Fatalf("CreatePrefixCache failed, err=%v", err)
+ }
+
+ // use cache information in subsequent requests
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ResponsesAPI,
+ HeadPreviousResponseID: &cacheInfo.ResponseID,
+ }
+
+ outMsg, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("What is the main idea expressed above?"),
+ }, ark.WithCache(cacheOpt))
+
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ respID, ok := ark.GetResponseID(outMsg)
+ if !ok {
+ log.Fatalf("not found response id in message")
+ }
+
+ log.Printf("\ngenerate output: \n")
+ log.Printf(" request_id: %s\n", respID)
+ respBody, _ := json.MarshalIndent(outMsg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
func ptrOf[T any](v T) *T {
- return &v
+ return &v
}
-
```
### ContextAPI Session 缓存
```go
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ instructions := []*schema.Message{
+ schema.SystemMessage("Your name is superman"),
+ }
+
+ cacheInfo, err := chatModel.CreateSessionCache(ctx, instructions, 86400, nil)
+ if err != nil {
+ log.Fatalf("CreateSessionCache failed, err=%v", err)
+ }
+
+ thinking := &arkModel.Thinking{
+ Type: arkModel.ThinkingTypeDisabled,
+ }
+
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ContextAPI,
+ ContextID: &cacheInfo.ContextID,
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, instructions,
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ <-time.After(500 * time.Millisecond)
+
+ msg, err = chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's your name?",
+ },
+ },
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ fmt.Printf("\ngenerate output: \n")
+ fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ fmt.Printf(" body: %s\n", string(respBody))
+
+ outStreamReader, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What do I ask you last time?",
+ },
+ },
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ fmt.Println("\ntypewriter output:")
+ var msgs []*schema.Message
+ for {
+ item, e := outStreamReader.Recv()
+ if e == io.EOF {
+ break
+ }
+ if e != nil {
+ log.Fatal(e)
+ }
+
+ fmt.Print(item.Content)
+ msgs = append(msgs, item)
+ }
+
+ msg, err = schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ fmt.Print("\n\nstream output: \n")
+ fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
+ respBody, _ = json.MarshalIndent(msg, " ", " ")
+ fmt.Printf(" body: %s\n", string(respBody))
+}
+```
+### ResponseAPI Session 缓存[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark/#responseapi-session%E7%BC%93%E5%AD%98)
+
+```go
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
-
- "github.com/cloudwego/eino-ext/components/model/ark"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+
+ "github.com/cloudwego/eino/schema"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- instructions := []*schema.Message{
- schema.SystemMessage("Your name is superman"),
- }
-
- cacheInfo, err := chatModel.CreateSessionCache(ctx, instructions, 86400, nil)
- if err != nil {
- log.Fatalf("CreateSessionCache failed, err=%v", err)
- }
-
- thinking := &arkModel.Thinking{
- Type: arkModel.ThinkingTypeDisabled,
- }
-
- cacheOpt := &ark.CacheOption{
- APIType: ark.ContextAPI,
- ContextID: &cacheInfo.ContextID,
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- }
-
- msg, err := chatModel.Generate(ctx, instructions,
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- <-time.After(500 * time.Millisecond)
-
- msg, err = chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What's your name?",
- },
- },
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("\ngenerate output: \n")
- fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- fmt.Printf(" body: %s\n", string(respBody))
-
- outStreamReader, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What do I ask you last time?",
- },
- },
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- fmt.Println("\ntypewriter output:")
- var msgs []*schema.Message
- for {
- item, e := outStreamReader.Recv()
- if e == io.EOF {
- break
- }
- if e != nil {
- log.Fatal(e)
- }
-
- fmt.Print(item.Content)
- msgs = append(msgs, item)
- }
-
- msg, err = schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- fmt.Print("\n\nstream output: \n")
- fmt.Printf(" request_id: %s\n", ark.GetArkRequestID(msg))
- respBody, _ = json.MarshalIndent(msg, " ", " ")
- fmt.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Cache: &ark.CacheConfig{
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ thinking := &arkModel.Thinking{
+ Type: arkModel.ThinkingTypeDisabled,
+ }
+ cacheOpt := &ark.CacheOption{
+ APIType: ark.ResponsesAPI,
+ SessionCache: &ark.SessionCacheConfig{
+ EnableCache: true,
+ TTL: 86400,
+ },
+ }
+
+ useMsgs := []*schema.Message{
+ schema.UserMessage("Your name is superman"),
+ schema.UserMessage("What's your name?"),
+ schema.UserMessage("What do I ask you last time?"),
+ }
+
+ var input []*schema.Message
+ for _, msg := range useMsgs {
+ input = append(input, msg)
+
+ streamResp, err := chatModel.Stream(ctx, input,
+ ark.WithThinking(thinking),
+ ark.WithCache(cacheOpt))
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ var messages []*schema.Message
+ for {
+ chunk, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamResp failed, err=%v", err)
+ }
+ messages = append(messages, chunk)
+ }
+
+ resp, err := schema.ConcatMessages(messages)
+ if err != nil {
+ log.Fatalf("ConcatMessages of ark failed, err=%v", err)
+ }
+
+ fmt.Printf("stream output: \n%v\n\n", resp)
+
+ input = append(input, resp)
+ }
+}
+```
+
+## **使用方式**
+
+### ChatModel
+
+#### **组件初始化**
+
+Ark 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下:
+
+```go
+import "github.com/cloudwego/eino-ext/components/model/ark"
+
+model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ // 服务配置
+ BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // 服务地址
+ Region: "cn-beijing", // 区域
+ HTTPClient: httpClient, // 自定义 HTTP 客户端
+ Timeout: &timeout, // 超时时间
+ RetryTimes: &retries, // 重试次数
+
+ // 认证配置(二选一)
+ APIKey: "your-api-key", // API Key 认证
+ AccessKey: "your-ak", // AK/SK 认证
+ SecretKey: "your-sk",
+
+ // 模型配置
+ Model: "endpoint-id", // 模型端点 ID
+
+ // 生成参数
+ MaxTokens: &maxTokens, // 最大生成长度
+ Temperature: &temp, // 温度
+ TopP: &topP, // Top-P 采样
+ Stop: []string{}, // 停止词
+ FrequencyPenalty: &fp, // 频率惩罚
+ PresencePenalty: &pp, // 存在惩罚
+
+ // 高级参数
+ LogitBias: map[string]int{}, // Token 偏置
+ CustomHeader: map[string]string{}, // http custom header
+})
+```
+
+#### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+func main() {
+ // 普通模式
+ response, err := model.Generate(ctx, messages)
+
+ // 流式模式
+ stream, err := model.Stream(ctx, messages)
}
+```
+
+消息格式示例:
+> 注意,是否支持多模态的图片需要看具体的模型
+
+```go
+func main() {
+ imgUrl := "https://example.com/image.jpg",
+ messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: &imgUrl,
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ }
+}
```
+#### **工具调用**
+
+支持绑定工具:
-### ResponseAPI Session缓存
```go
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+// 绑定工具
+err := model.BindTools(tools)
+```
+
+> 工具相关信息,可以参考 [[🚧]Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+#### **完整使用示例**
+
+##### **直接对话**
+
+```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+)
- arkModel "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+func main() {
+ ctx := context.Background()
+
+ timeout := 30 * time.Second
+ // 初始化模型
+ model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: "your-api-key",
+ Region: "cn-beijing",
+ Model: "endpoint-id",
+ Timeout: &timeout,
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("介绍一下火山引擎"),
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ println(response.Content)
+
+ // 获取 Token 使用情况
+ if usage := response.ResponseMeta.Usage; usage != nil {
+ println("提示 Tokens:", usage.PromptTokens)
+ println("生成 Tokens:", usage.CompletionTokens)
+ println("总 Tokens:", usage.TotalTokens)
+ }
+}
+```
+
+##### **流式对话**
- "github.com/cloudwego/eino/schema"
+```go
+package main
- "github.com/cloudwego/eino-ext/components/model/ark"
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- Cache: &ark.CacheConfig{
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- thinking := &arkModel.Thinking{
- Type: arkModel.ThinkingTypeDisabled,
- }
- cacheOpt := &ark.CacheOption{
- APIType: ark.ResponsesAPI,
- SessionCache: &ark.SessionCacheConfig{
- EnableCache: true,
- TTL: 86400,
- },
- }
-
- useMsgs := []*schema.Message{
- schema.UserMessage("Your name is superman"),
- schema.UserMessage("What's your name?"),
- schema.UserMessage("What do I ask you last time?"),
- }
-
- var input []*schema.Message
- for _, msg := range useMsgs {
- input = append(input, msg)
-
- streamResp, err := chatModel.Stream(ctx, input,
- ark.WithThinking(thinking),
- ark.WithCache(cacheOpt))
- if err != nil {
- log.Fatalf("Stream failed, err=%v", err)
- }
-
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
-
- resp, err := schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of ark failed, err=%v", err)
- }
-
- fmt.Printf("stream output: \n%v\n\n", resp)
-
- input = append(input, resp)
- }
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{
+ APIKey: "your-api-key",
+ Model: "ep-xxx",
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("介绍一下 Eino"),
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ print(chunk.Content)
+ }
+}
+```
+
+### ImageGeneration Model
+
+#### 组件初始化
+
+即梦(seedream4.0)图片生成模型通过 `NewImageGenerationModel` 函数进行初始化
+
+```go
+import "github.com/cloudwego/eino-ext/components/model/ark"
+
+// Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ // 服务配置
+ BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // 服务地址
+ Region: "cn-beijing", // 区域
+ HTTPClient: httpClient, // 自定义 HTTP 客户端
+ Timeout: &timeout, // 超时时间
+ RetryTimes: &retries, // 重试次数
+
+ // 模型配置
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+
+ // 生成配置
+ Size: "1K", // 指定生成图片的大小
+ SequentialImageGeneration: ark._SequentialImageGenerationAuto_, // 决定是否生成组图
+ SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{ // 生成组图时的最大图片数量
+ MaxImages: ptr(2),
+ },
+ ResponseFormat: ark._ImageResponseFormatURL_, // 图片数据的返回方式,URL或者Base64二选一
+ DisableWatermark: false, // 是否带有 "AI 生成" 水印
+})
+```
+
+#### 生成对话
+
+图片生成模型同样支持普通模式和流式模式两种方式:
+
+```go
+func main() {
+ // 普通模式
+ response, err := model.Generate(ctx, messages)
+
+ // 流式模式
+ stream, err := model.Stream(ctx, messages)
+}
+```
+
+#### 完整使用示例
+
+##### 直接对话
+
+```go
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
+
+ "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+)
+
+// 指针辅助函数
+func ptr[T any](v T) *T {
+ return &v
}
+func main() {
+ // 初始化模型
+ ctx := context.Background()
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Size: "1920x1080",
+ SequentialImageGeneration: ark._SequentialImageGenerationDisabled_,
+ ResponseFormat: ark._ImageResponseFormatURL_,
+ DisableWatermark: false,
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ // 准备消息
+ inMsgs := []*schema.Message{
+ {
+ Role: schema._User_,
+ Content: "generate two images of a cat",
+ },
+ }
+
+ // 生成图片
+ msg, err := imageGenerationModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ // 打印图片信息
+ log.Printf("\ngenerate output: \n")
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
+}
```
+##### 流式对话
+
+```go
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "log"
+ "os"
+
+ "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
+
+ "github.com/cloudwego/eino-ext/components/model/ark"
+ "github.com/cloudwego/eino/schema"
+)
+
+// 指针辅助函数
+func ptr[T any](v T) *T {
+ return &v
+}
+
+func main() {
+ // 初始化模型
+ ctx := context.Background()
+ imageGenerationModel, err := ark.NewImageGenerationModel(ctx, &ark.ImageGenerationConfig{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ Size: "1K",
+ SequentialImageGeneration: ark._SequentialImageGenerationAuto_,
+ SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{
+ MaxImages: ptr(2),
+ },
+ ResponseFormat: ark._ImageResponseFormatURL_,
+ DisableWatermark: false,
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ // 准备消息
+ inMsgs := []*schema.Message{
+ {
+ Role: schema._User_,
+ Content: "generate two images of a cat",
+ },
+ }
+
+ // 流式生成图片
+ sr, err := imageGenerationModel.Stream(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Stream failed, err=%v", err)
+ }
+
+ // 处理流式信息
+ log.Printf("stream output: \n")
+ index := 0
+ for {
+ msgChunk, err := sr.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream Recv failed, err=%v", err)
+ }
+
+ respBody, _ = json.MarshalIndent(msgChunk, " ", " ")
+ log.Printf("stream chunk %d: body: %s\n", index, string(respBody))
+ index++
+ }
+}
+```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/ark/examples)
-## 更多信息
+## **相关文档**
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Volcengine Ark Model Documentation](https://www.volcengine.com/docs/82379/1263272)
+- [[🚧]Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [[🚧]ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
+- [[🚧]ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
+- [火山引擎官网](https://www.volcengine.com/product/doubao)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
index e6f2f9175c8..35d7143e8e9 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md
@@ -1,15 +1,15 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: ChatModel - arkbot
+title: ChatModel - ARKBot
weight: 0
---
一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Volcengine Ark Bot,它实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/model.ToolCallingChatModel`
- 轻松与 Eino 的模型系统集成
@@ -19,13 +19,13 @@ weight: 0
- 支持自定义响应解析
- 灵活的模型配置
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/model/arkbot@latest
```
-## 快速开始
+## **快速开始**
以下是如何使用 Ark Bot 的快速示例:
@@ -33,62 +33,61 @@ go get github.com/cloudwego/eino-ext/components/model/arkbot@latest
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output:")
- log.Printf(" request_id: %s", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s \n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s\n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:")
+ log.Printf(" request_id: %s", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s \n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s\n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
```
-## 配置
+## **配置**
可以使用 `arkbot.Config` 结构体配置模型:
```go
-
type Config struct {
// Timeout specifies the maximum duration to wait for API responses
// If HTTPClient is set, Timeout will not be used.
@@ -173,7 +172,7 @@ type Config struct {
}
```
-## 请求选项
+## **请求选项**
Ark 模型支持各种请求选项以自定义 API 调用的行为。以下是可用的选项:
@@ -183,147 +182,147 @@ Ark 模型支持各种请求选项以自定义 API 调用的行为。以下是
func WithCustomHeader(m map[string]string) model.Option {}
```
-## 示例
+## **示例**
-### 文本生成
+### **文本生成**
```go
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- inMsgs := []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- }
-
- msg, err := chatModel.Generate(ctx, inMsgs)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("generate output:\n")
- log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s \n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s \n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ inMsgs := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ }
+
+ msg, err := chatModel.Generate(ctx, inMsgs)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("generate output:\n")
+ log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s \n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s \n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
```
-### 流式生成
+### **流式生成**
```go
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/arkbot"
+ "github.com/cloudwego/eino-ext/components/model/arkbot"
)
func main() {
- ctx := context.Background()
-
- // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
- chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
- APIKey: os.Getenv("ARK_API_KEY"),
- Model: os.Getenv("ARK_MODEL_ID"),
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What's the weather in Beijing?",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close() // do not forget to close the stream
-
- msgs := make([]*schema.Message, 0)
-
- log.Printf("stream output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- msgs = append(msgs, msg)
- if err != nil {
- log.Printf("stream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Printf("ConcatMessages failed, err=%v", err)
- return
- }
-
- log.Printf("generate output: \n")
- log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
- if bu, ok := arkbot.GetBotUsage(msg); ok {
- bbu, _ := json.Marshal(bu)
- log.Printf(" bot_usage: %s \n", string(bbu))
- }
- if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
- bRef, _ := json.Marshal(ref)
- log.Printf(" bot_chat_result_reference: %s \n", bRef)
- }
- respBody, _ := json.MarshalIndent(msg, " ", " ")
- log.Printf(" body: %s \n", string(respBody))
+ ctx := context.Background()
+
+ // Get ARK_API_KEY and ARK_MODEL_ID: https://www.volcengine.com/docs/82379/1399008
+ chatModel, err := arkbot.NewChatModel(ctx, &arkbot.Config{
+ APIKey: os.Getenv("ARK_API_KEY"),
+ Model: os.Getenv("ARK_MODEL_ID"),
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What's the weather in Beijing?",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close() // do not forget to close the stream
+
+ msgs := make([]*schema.Message, 0)
+
+ log.Printf("stream output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ msgs = append(msgs, msg)
+ if err != nil {
+ log.Printf("stream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Printf("ConcatMessages failed, err=%v", err)
+ return
+ }
+
+ log.Printf("generate output: \n")
+ log.Printf(" request_id: %s \n", arkbot.GetArkRequestID(msg))
+ if bu, ok := arkbot.GetBotUsage(msg); ok {
+ bbu, _ := json.Marshal(bu)
+ log.Printf(" bot_usage: %s \n", string(bbu))
+ }
+ if ref, ok := arkbot.GetBotChatResultReference(msg); ok {
+ bRef, _ := json.Marshal(ref)
+ log.Printf(" bot_chat_result_reference: %s \n", bRef)
+ }
+ respBody, _ := json.MarshalIndent(msg, " ", " ")
+ log.Printf(" body: %s \n", string(respBody))
}
```
-## 更多信息
+## **更多信息**
- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
- [Volcengine Ark Model Documentation](https://www.volcengine.com/docs/82379/1263272)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
index 5083bfe6cc9..eb107e445e2 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md
@@ -1,12 +1,14 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - claude
+title: ChatModel - Claude
weight: 0
---
+# Claude 模型
+
一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Claude 模型,它实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
## 特性
@@ -33,76 +35,76 @@ go get github.com/cloudwego/eino-ext/components/model/claude@latest
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- thinking, ok := claude.GetThinking(resp)
- fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ thinking, ok := claude.GetThinking(resp)
+ fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
```
@@ -197,76 +199,76 @@ type Config struct {
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- thinking, ok := claude.GetThinking(resp)
- fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ thinking, ok := claude.GetThinking(resp)
+ fmt.Printf("Thinking(have: %v): %s\n", ok, thinking)
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total)\n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
```
@@ -276,81 +278,81 @@ func main() {
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- imageBinary, err := os.ReadFile("examples/test.jpg")
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(imageBinary)),
- MIMEType: "image/jpeg",
- },
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ imageBinary, err := os.ReadFile("examples/test.jpg")
+ if err != nil {
+ log.Fatalf("read file failed, err=%v", err)
+ }
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: of(base64.StdEncoding.EncodeToString(imageBinary)),
+ MIMEType: "image/jpeg",
+ },
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](v T) *T {
- return &v
+ return &v
}
```
@@ -360,96 +362,96 @@ func of[T any](v T) *T {
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino-ext/components/model/claude"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- messages := []*schema.Message{
- schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
- {
- Role: schema.User,
- Content: "Write a short poem about spring, word by word.",
- },
- }
-
- stream, err := cm.Stream(ctx, messages, claude.WithThinking(&claude.Thinking{
- Enable: true,
- BudgetTokens: 1024,
- }))
- if err != nil {
- log.Printf("Stream error: %v", err)
- return
- }
- isFirstThinking := false
- isFirstContent := false
-
- fmt.Print("Assistant: ----------\n")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("Stream receive error: %v", err)
- return
- }
-
- thinkingContent, ok := claude.GetThinking(resp)
- if ok {
- if !isFirstThinking {
- isFirstThinking = true
- fmt.Print("\nThinking: ----------\n")
- }
- fmt.Print(thinkingContent)
- }
-
- if len(resp.Content) > 0 {
- if !isFirstContent {
- isFirstContent = true
- fmt.Print("\nContent: ----------\n")
- }
- fmt.Print(resp.Content)
- }
- }
- fmt.Println("\n----------")
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ messages := []*schema.Message{
+ schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring, word by word.",
+ },
+ }
+
+ stream, err := cm.Stream(ctx, messages, claude.WithThinking(&claude.Thinking{
+ Enable: true,
+ BudgetTokens: 1024,
+ }))
+ if err != nil {
+ log.Printf("Stream error: %v", err)
+ return
+ }
+ isFirstThinking := false
+ isFirstContent := false
+
+ fmt.Print("Assistant: ----------\n")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("Stream receive error: %v", err)
+ return
+ }
+
+ thinkingContent, ok := claude.GetThinking(resp)
+ if ok {
+ if !isFirstThinking {
+ isFirstThinking = true
+ fmt.Print("\nThinking: ----------\n")
+ }
+ fmt.Print(thinkingContent)
+ }
+
+ if len(resp.Content) > 0 {
+ if !isFirstContent {
+ isFirstContent = true
+ fmt.Print("\nContent: ----------\n")
+ }
+ fmt.Print(resp.Content)
+ }
+ }
+ fmt.Println("\n----------")
}
```
@@ -459,122 +461,122 @@ func main() {
package main
import (
- "context"
- "fmt"
+ "context"
+ "fmt"
- "io"
- "log"
- "os"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/claude"
- "github.com/cloudwego/eino/schema"
- "github.com/eino-contrib/jsonschema"
- orderedmap "github.com/wk8/go-ordered-map/v2"
+ "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino/schema"
+ "github.com/eino-contrib/jsonschema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
- if apiKey == "" {
- log.Fatal("CLAUDE_API_KEY environment variable is not set")
- }
-
- var baseURLPtr *string = nil
- if len(baseURL) > 0 {
- baseURLPtr = &baseURL
- }
-
- // Create a Claude model
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: baseURLPtr,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- _, err = cm.WithTools([]*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get current weather information for a city",
- ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&jsonschema.Schema{
- Type: "object",
- Properties: orderedmap.New[string, *jsonschema.Schema](orderedmap.WithInitialData[string, *jsonschema.Schema](
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "city",
- Value: &jsonschema.Schema{
- Type: "string",
- Description: "The city name",
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "unit",
- Value: &jsonschema.Schema{
- Type: "string",
- Enum: []interface{}{"celsius", "fahrenheit"},
- },
- },
- )),
- Required: []string{"city"},
- }),
- },
- })
- if err != nil {
- log.Printf("Bind tools error: %v", err)
- return
- }
-
- streamResp, err := cm.Stream(ctx, []*schema.Message{
- schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
- schema.UserMessage("call 'get_weather' to query what's the weather like in Paris today? Please use Celsius."),
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- msgs := make([]*schema.Message, 0)
- for {
- msg, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Stream receive error: %v", err)
- }
- msgs = append(msgs, msg)
- }
- resp, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("Concat error: %v", err)
- }
-
- fmt.Printf("assistant content:\n %v\n----------\n", resp.Content)
- if len(resp.ToolCalls) > 0 {
- fmt.Printf("Function called: %s\n", resp.ToolCalls[0].Function.Name)
- fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
-
- weatherResp, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("What's the weather like in Paris today? Please use Celsius."),
- resp,
- schema.ToolMessage(`{"temperature": 18, "condition": "sunny"}`, resp.ToolCalls[0].ID),
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Final response: %s\n", weatherResp.Content)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+
+ // Create a Claude model
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ _, err = cm.WithTools([]*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get current weather information for a city",
+ ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&jsonschema.Schema{
+ Type: "object",
+ Properties: orderedmap.New[string, *jsonschema.Schema](orderedmap.WithInitialData[string, *jsonschema.Schema](
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "city",
+ Value: &jsonschema.Schema{
+ Type: "string",
+ Description: "The city name",
+ },
+ },
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "unit",
+ Value: &jsonschema.Schema{
+ Type: "string",
+ Enum: []interface{}{"celsius", "fahrenheit"},
+ },
+ },
+ )),
+ Required: []string{"city"},
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("Bind tools error: %v", err)
+ return
+ }
+
+ streamResp, err := cm.Stream(ctx, []*schema.Message{
+ schema.SystemMessage("You are a helpful AI assistant. Be concise in your responses."),
+ schema.UserMessage("call 'get_weather' to query what's the weather like in Paris today? Please use Celsius."),
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ msgs := make([]*schema.Message, 0)
+ for {
+ msg, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream receive error: %v", err)
+ }
+ msgs = append(msgs, msg)
+ }
+ resp, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("Concat error: %v", err)
+ }
+
+ fmt.Printf("assistant content:\n %v\n----------\n", resp.Content)
+ if len(resp.ToolCalls) > 0 {
+ fmt.Printf("Function called: %s\n", resp.ToolCalls[0].Function.Name)
+ fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
+
+ weatherResp, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("What's the weather like in Paris today? Please use Celsius."),
+ resp,
+ schema.ToolMessage(`{"temperature": 18, "condition": "sunny"}`, resp.ToolCalls[0].ID),
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Final response: %s\n", weatherResp.Content)
+ }
}
```
@@ -584,277 +586,557 @@ func main() {
package main
import (
- "context"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/claude"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/claude"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- systemCache()
- //toolInfoCache()
- //sessionCache()
+ systemCache()
+ //toolInfoCache()
+ //sessionCache()
}
func systemCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- breakpoint := claude.SetMessageBreakpoint(&schema.Message{
- Role: schema.System,
- Content: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
- })
-
- for i := 0; i < 2; i++ {
- now := time.Now()
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are an AI assistant tasked with analyzing literary works. " +
- "Your goal is to provide insightful commentary on themes.",
- },
- breakpoint,
- {
- Role: schema.User,
- Content: "Analyze the major themes in the content.",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
- }
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ breakpoint := claude.SetMessageBreakpoint(&schema.Message{
+ Role: schema.System,
+ Content: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
+ })
+
+ for i := 0; i < 2; i++ {
+ now := time.Now()
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are an AI assistant tasked with analyzing literary works. " +
+ "Your goal is to provide insightful commentary on themes.",
+ },
+ breakpoint,
+ {
+ Role: schema.User,
+ Content: "Analyze the major themes in the content.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
+ }
}
func toolInfoCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- breakpoint := claude.SetToolInfoBreakpoint(&schema.ToolInfo{
- Name: "get_time",
- Desc: "Get the current time in a given time zone",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "timezone": {
- Required: true,
- Type: "string",
- Desc: "The IANA time zone name, e.g. America/Los_Angeles",
- },
- },
- )},
- )
-
- mockTools := []*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get the current weather in a given location",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "location": {
- Type: "string",
- Desc: "The city and state, e.g. San Francisco, CA",
- },
- "unit": {
- Type: "string",
- Enum: []string{"celsius", "fahrenheit"},
- Desc: "The unit of temperature, either celsius or fahrenheit",
- },
- },
- ),
- },
- {
- Name: "mock_tool_1",
- Desc: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "mock parameter",
- },
- },
- ),
- },
- {
- Name: "mock_tool_2",
- Desc: "For the administration, the biggest gain was Japan's commitment to investing $550 billion in the United States, particularly with funds to be directed to sectors deemed strategic, ranging from semiconductors and pharmaceuticals to energy infrastructure and critical minerals.The White House highlighted that the deal represents what it called the \"single largest foreign investment commitment ever secured by any country.\" It touted the agreement as a \"strategic realignment of the U.S.-Japan economic relationship\" that will advance the mutual interests of both countries.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "Bessent said it will check the extent of Japan's compliance every quarter and \"if the president is unhappy, then they will boomerang back to the 25 percent tariff rates, both on cars and the rest of their products.\"",
- },
- },
- ),
- },
- {
- Name: "mock_tool_3",
- Desc: "Of the amount, up to 100,000 tons is imported for direct consumption. Most of the remainder is used for animal feed and processing into other food products. Any rice imported to Japan beyond the special quota is subject to a tariff of 341 yen ($2.3) per kilogram.\n\n\n In fiscal 2024, the United States shipped roughly 346,000 tons of rice to Japan, compared with 286,000 tons exported by Thailand and 70,000 tons by Australia, under the quota system, according to Japan's farm ministry.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "mock_param": {
- Required: true,
- Type: "string",
- Desc: "Currently, Japan imports about 770,000 tons of tariff-free rice a year under the WTO framework, with the United States the No. 1 exporter, accounting for nearly half of the total, followed by Thailand and Australia.",
- },
- },
- ),
- },
- breakpoint,
- }
-
- chatModelWithTools, err := cm.WithTools(mockTools)
- if err != nil {
- log.Fatalf("WithTools failed, err=%v", err)
- }
-
- for i := 0; i < 2; i++ {
- now := time.Now()
-
- resp, err := chatModelWithTools.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a tool calling assistant.",
- },
- {
- Role: schema.User,
- Content: "Mock the get_weather tool input yourself, and then call get_weather",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
- }
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ breakpoint := claude.SetToolInfoBreakpoint(&schema.ToolInfo{
+ Name: "get_time",
+ Desc: "Get the current time in a given time zone",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "timezone": {
+ Required: true,
+ Type: "string",
+ Desc: "The IANA time zone name, e.g. America/Los_Angeles",
+ },
+ },
+ )},
+ )
+
+ mockTools := []*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get the current weather in a given location",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "location": {
+ Type: "string",
+ Desc: "The city and state, e.g. San Francisco, CA",
+ },
+ "unit": {
+ Type: "string",
+ Enum: []string{"celsius", "fahrenheit"},
+ Desc: "The unit of temperature, either celsius or fahrenheit",
+ },
+ },
+ ),
+ },
+ {
+ Name: "mock_tool_1",
+ Desc: "The film Dongji Rescue, based on a true historical event, tells the story of Chinese fishermen braving turbulent seas to save strangers — a testament to the nation's capacity to create miracles in the face of adversity.\n\nEighty-three years ago, in 1942, the Japanese military seized the Lisbon Maru ship to transport 1,816 British prisoners of war from Hong Kong to Japan. Passing through the waters near Zhoushan, Zhejiang province, it was torpedoed by a US submarine. Fishermen from Zhoushan rescued 384 prisoners of war and hid them from Japanese search parties.\n\nActor Zhu Yilong, in an exclusive interview with China Daily, said he was deeply moved by the humanity shown in such extreme conditions when he accepted his role. \"This historical event proves that in dire circumstances, Chinese people can extend their goodness to protect both themselves and others,\" he said.\n\nLast Friday, a themed event for Dongji Rescue was held in Zhoushan, a city close to where the Lisbon Maru sank. Descendants of the British POWs and the rescuing fishermen gathered to watch the film and share their reflections.\n\nIn the film, the British POWs are aided by a group of fishermen from Dongji Island, whose courage and compassion cut a path through treacherous waves. After the screening, many descendants were visibly moved.\n\n\"I want to express my deepest gratitude to the Chinese people and the descendants of Chinese fishermen. When the film is released in the UK, I will bring my family and friends to watch it. Heroic acts like this deserve to be known worldwide,\" said Denise Wynne, a descendant of a British POW.\n\n\"I felt the profound friendship between the Chinese and British people through the film,\" said Li Hui, a descendant of a rescuer. Many audience members were brought to tears — some by the fishermen's bravery, others by the enduring spirit of \"never abandoning those in peril\".\n\n\"In times of sea peril, rescue is a must,\" said Wu Buwei, another rescuer's descendant, noting that this value has long been a part of Dongji fishermen's traditions.\n\nCoincidentally, on the morning of Aug 5, a real-life rescue unfolded on the shores of Dongfushan in Dongji town. Two tourists from Shanghai fell into the sea and were swept away by powerful waves. Without hesitation, two descendants of Dongji fishermen — Wang Yubin and Yang Xiaoping — leaped in to save them, braving a wind force of 9 to 10 before pulling them to safety.\n\n\"Our forebearers once rescued many British POWs here. As their descendants, we should carry forward their bravery,\" Wang said. Both rescuers later joined the film's cast and crew at the Zhoushan event, sharing the stage with actors who portrayed the fishermen in a symbolic \"reunion across 83 years\".\n\nChinese actor Wu Lei, who plays A Dang in the film, expressed his respect for the two rescuing fishermen on-site. In the film, A Dang saves a British POW named Thomas Newman. Though they share no common language, they manage to connect. In an interview with China Daily, Wu said, \"They connected through a small globe. A Dang understood when Newman pointed out his home, the UK, on the globe.\"\n\nThe film's director Fei Zhenxiang noted that the Eastern Front in World War II is often overlooked internationally, despite China's 14 years of resistance tying down large numbers of Japanese troops. \"Every Chinese person is a hero, and their righteousness should not be forgotten,\" he said.\n\nGuan Hu, codirector, said, \"We hope that through this film, silent kindness can be seen by the world\", marking the 80th anniversary of victory in the Chinese People's War of Resistance Against Japanese Aggression (1931-45) and the World Anti-Fascist War.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "mock_param": {
+ Required: true,
+ Type: "string",
+ Desc: "mock parameter",
+ },
+ },
+ ),
+ },
+ {
+ Name: "mock_tool_2",
+ Desc: "For the administration, the biggest gain was Japan's commitment to investing $550 billion in the United States, particularly with funds to be directed to sectors deemed strategic, ranging from semiconductors and pharmaceuticals to energy infrastructure and critical minerals.The White House highlighted that the deal represents what it called the \"single largest foreign investment commitment ever secured by any country.\" It touted the agreement as a \"strategic realignment of the U.S.-Japan economic relationship\" that will advance the mutual interests of both countries.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "mock_param": {
+ Required: true,
+ Type: "string",
+ Desc: "Bessent said it will check the extent of Japan's compliance every quarter and \"if the president is unhappy, then they will boomerang back to the 25 percent tariff rates, both on cars and the rest of their products.\"",
+ },
+ },
+ ),
+ },
+ {
+ Name: "mock_tool_3",
+ Desc: "Of the amount, up to 100,000 tons is imported for direct consumption. Most of the remainder is used for animal feed and processing into other food products. Any rice imported to Japan beyond the special quota is subject to a tariff of 341 yen ($2.3) per kilogram.\n\n\n In fiscal 2024, the United States shipped roughly 346,000 tons of rice to Japan, compared with 286,000 tons exported by Thailand and 70,000 tons by Australia, under the quota system, according to Japan's farm ministry.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "mock_param": {
+ Required: true,
+ Type: "string",
+ Desc: "Currently, Japan imports about 770,000 tons of tariff-free rice a year under the WTO framework, with the United States the No. 1 exporter, accounting for nearly half of the total, followed by Thailand and Australia.",
+ },
+ },
+ ),
+ },
+ breakpoint,
+ }
+
+ chatModelWithTools, err := cm.WithTools(mockTools)
+ if err != nil {
+ log.Fatalf("WithTools failed, err=%v", err)
+ }
+
+ for i := 0; i < 2; i++ {
+ now := time.Now()
+
+ resp, err := chatModelWithTools.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a tool calling assistant.",
+ },
+ {
+ Role: schema.User,
+ Content: "Mock the get_weather tool input yourself, and then call get_weather",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ log.Printf("time_consume=%f, output: \n%v", time.Now().Sub(now).Seconds(), resp)
+ }
}
func sessionCache() {
- apiKey := os.Getenv("CLAUDE_API_KEY")
- modelName := os.Getenv("CLAUDE_MODEL")
- baseURL := os.Getenv("CLAUDE_BASE_URL")
-
- ctx := context.Background()
-
- cm, err := claude.NewChatModel(ctx, &claude.Config{
- // if you want to use Aws Bedrock Service, set these four field.
- // ByBedrock: true,
- // AccessKey: "",
- // SecretAccessKey: "",
- // Region: "us-west-2",
- APIKey: apiKey,
- // Model: "claude-3-5-sonnet-20240620",
- BaseURL: &baseURL,
- Model: modelName,
- MaxTokens: 3000,
- })
- if err != nil {
- log.Fatalf("NewChatModel of claude failed, err=%v", err)
- }
-
- opts := []model.Option{
- claude.WithEnableAutoCache(true),
- }
-
- mockTools := []*schema.ToolInfo{
- {
- Name: "get_weather",
- Desc: "Get the current weather in a given location",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "location": {
- Type: "string",
- Desc: "The city and state, e.g. San Francisco, CA",
- },
- "unit": {
- Type: "string",
- Enum: []string{"celsius", "fahrenheit"},
- Desc: "The unit of temperature, either celsius or fahrenheit",
- },
- },
- ),
- },
- }
-
- chatModelWithTools, err := cm.WithTools(mockTools)
- if err != nil {
- log.Fatalf("WithTools failed, err=%v", err)
- return
- }
-
- input := []*schema.Message{
- schema.SystemMessage("You are a tool calling assistant."),
- schema.UserMessage("What tools can you call?"),
- }
-
- resp, err := chatModelWithTools.Generate(ctx, input, opts...)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- return
- }
- log.Printf("output_1: \n%v\n\n", resp)
-
- input = append(input, resp, schema.UserMessage("What am I asking last time?"))
-
- resp, err = chatModelWithTools.Generate(ctx, input, opts...)
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output_2: \n%v\n\n", resp)
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+
+ ctx := context.Background()
+
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // if you want to use Aws Bedrock Service, set these four field.
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+ APIKey: apiKey,
+ // Model: "claude-3-5-sonnet-20240620",
+ BaseURL: &baseURL,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of claude failed, err=%v", err)
+ }
+
+ opts := []model.Option{
+ claude.WithEnableAutoCache(true),
+ }
+
+ mockTools := []*schema.ToolInfo{
+ {
+ Name: "get_weather",
+ Desc: "Get the current weather in a given location",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "location": {
+ Type: "string",
+ Desc: "The city and state, e.g. San Francisco, CA",
+ },
+ "unit": {
+ Type: "string",
+ Enum: []string{"celsius", "fahrenheit"},
+ Desc: "The unit of temperature, either celsius or fahrenheit",
+ },
+ },
+ ),
+ },
+ }
+
+ chatModelWithTools, err := cm.WithTools(mockTools)
+ if err != nil {
+ log.Fatalf("WithTools failed, err=%v", err)
+ return
+ }
+
+ input := []*schema.Message{
+ schema.SystemMessage("You are a tool calling assistant."),
+ schema.UserMessage("What tools can you call?"),
+ }
+
+ resp, err := chatModelWithTools.Generate(ctx, input, opts...)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ return
+ }
+ log.Printf("output_1: \n%v\n\n", resp)
+
+ input = append(input, resp, schema.UserMessage("What am I asking last time?"))
+
+ resp, err = chatModelWithTools.Generate(ctx, input, opts...)
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output_2: \n%v\n\n", resp)
+}
+```
+
+## **基本介绍**
+
+Claude 模型是 ChatModel 接口的一个实现,用于与 Anthropic 协议系列模型进行交互。该组件实现了 [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+
+目前已支持多模态输入(文本、图片),输出仅支持文本模态
+
+## **使用方式**
+
+### **组件初始化**
+
+Claude 模型通过 NewChatModel 函数进行初始化,主要配置参数如下:
+
+```go
+import (
+ "context"
+
+ "github.com/cloudwego/eino-ext/components/model/claude"
+)
+
+cm, err := claude.NewChatModel(ctx, &claude.Config{
+ // Aws Bedrock Service 配置(可选)
+ // ByBedrock: true,
+ // AccessKey: "",
+ // SecretAccessKey: "",
+ // Region: "us-west-2",
+
+ // 模型配置
+ APIKey: "your-key", // API 秘钥
+ Model: "claude-3-5-sonnet-20240620", // 模型名称
+ BaseURL: "your-url", // 基础 URL
+
+ // 生成参数
+ MaxTokens: &maxTokens,// 最大生成长度
+ Temperature: &temp, // 温度
+ TopP: &topP, // Top-P 采样
+ StopSequences: []string{},// 停止词
+ })
+```
+
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+// invoke模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+imgUrl := "https://example.com/image.jpg",
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: &imgUrl,
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ },
+ },
+}
+```
+
+### **工具调用**
+
+支持绑定工具和强制工具调用:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
}
+// 绑定可选工具
+err := model.BindTools(tools)
+// 绑定强制工具
+err := model.BindForcedTools(tools)
```
+> 工具相关信息,可以参考 [Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+### **完整使用示例**
+
+#### **直接对话**
+
+```go
+package main
+
+import (
+ "context"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+
+ "github.com/cloudwego/eino-ext/components/model/claude"
+)
+
+func main() {
+ // 初始化模型及参数
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ APIKey: apiKey,
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Printf("NewChatModel error: %v", err)
+ return
+ }
+
+ // 准备消息
+ imgUrl := "https://example.com/image.jpg",
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: &imgUrl,
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图里面有什么?",
+ },
+ },
+ },
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ fmt.Printf("Assistant: %s\n", resp)
+}
+```
+
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+
+ "github.com/cloudwego/eino-ext/components/model/claude"
+)
+
+func main() {
+ // 初始化模型及参数
+ ctx := context.Background()
+ apiKey := os.Getenv("CLAUDE_API_KEY")
+ modelName := os.Getenv("CLAUDE_MODEL")
+ baseURL := os.Getenv("CLAUDE_BASE_URL")
+ if apiKey == "" {
+ log.Fatal("CLAUDE_API_KEY environment variable is not set")
+ }
+
+ var baseURLPtr *string = nil
+ if len(baseURL) > 0 {
+ baseURLPtr = &baseURL
+ }
+ cm, err := claude.NewChatModel(ctx, &claude.Config{
+ APIKey: apiKey,
+ BaseURL: baseURLPtr,
+ Model: modelName,
+ MaxTokens: 3000,
+ })
+ if err != nil {
+ log.Printf("NewChatModel error: %v", err)
+ return
+ }
+
+ // 准备消息
+ imgUrl := "https://example.com/image.jpg",
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: &imgUrl,
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图里面有什么?",
+ },
+ },
+ },
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ fmt.Printf("Assistant: %s\n", chunk)
+ }
+}
+```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/claude/examples)
-## 更多信息
+## **相关文档**
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Claude Documentation](https://docs.claude.com/en/api/messages)
+- [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
index 64d5011e55d..7f127b92280 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md
@@ -1,15 +1,15 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: ChatModel - deepseek
+title: ChatModel - DeepSeek
weight: 0
---
一个针对 [Eino](https://github.com/cloudwego/eino) 的 DeepSeek 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/model.Model`
- 轻松与 Eino 的模型系统集成
@@ -19,13 +19,13 @@ weight: 0
- 支持自定义响应解析
- 灵活的模型配置
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/model/deepseek@latest
```
-## 快速开始
+## **快速开始**
以下是如何使用 DeepSeek 模型的快速示例:
@@ -33,68 +33,68 @@ go get github.com/cloudwego/eino-ext/components/model/deepseek@latest
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
-
-
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- reasoning, ok := deepseek.GetReasoningContent(resp)
- if !ok {
- fmt.Printf("Unexpected: non-reasoning")
- } else {
- fmt.Printf("Reasoning Content: %s \n", reasoning)
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMetaUsage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+
+
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ reasoning, ok := deepseek.GetReasoningContent(resp)
+ if !ok {
+ fmt.Printf("Unexpected: non-reasoning")
+ } else {
+ fmt.Printf("Reasoning Content: %s \n", reasoning)
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMetaUsage.TotalTokens)
+ }
}
```
-## 配置
+## **配置**
可以使用 `deepseek.ChatModelConfig` 结构体配置模型:
@@ -172,307 +172,299 @@ type ChatModelConfig struct {
}
```
-## 示例
+## **示例**
-### 文本生成
+### **文本生成**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
-
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a helpful AI assistant. Be concise in your responses.",
- },
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- }
-
- resp, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
-
- reasoning, ok := deepseek.GetReasoningContent(resp)
- if !ok {
- fmt.Printf("Unexpected: non-reasoning")
- } else {
- fmt.Printf("Reasoning Content: %s\n", reasoning)
- }
- fmt.Printf("Assistant: %s \n", resp.Content)
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a helpful AI assistant. Be concise in your responses.",
+ },
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ }
+
+ resp, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+
+ reasoning, ok := deepseek.GetReasoningContent(resp)
+ if !ok {
+ fmt.Printf("Unexpected: non-reasoning")
+ } else {
+ fmt.Printf("Reasoning Content: %s\n", reasoning)
+ }
+ fmt.Printf("Assistant: %s \n", resp.Content)
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
}
-
```
-### 带前缀文本生成
+### **带前缀文本生成**
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- schema.UserMessage("Please write quick sort code"),
- schema.AssistantMessage("```python \n", nil),
- }
- deepseek.SetPrefix(messages[1])
-
- result, err := cm.Generate(ctx, messages)
- if err != nil {
- log.Printf("Generate error: %v", err)
- }
-
- reasoningContent, ok := deepseek.GetReasoningContent(result)
- if !ok {
- fmt.Printf("No reasoning content")
- } else {
- fmt.Printf("Reasoning: %v \n", reasoningContent)
- }
- fmt.Printf("Content: %v\n", result)
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ schema.UserMessage("Please write quick sort code"),
+ schema.AssistantMessage("```python \n", nil),
+ }
+ deepseek.SetPrefix(messages[1])
+
+ result, err := cm.Generate(ctx, messages)
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ }
+
+ reasoningContent, ok := deepseek.GetReasoningContent(result)
+ if !ok {
+ fmt.Printf("No reasoning content")
+ } else {
+ fmt.Printf("Reasoning: %v \n", reasoningContent)
+ }
+ fmt.Printf("Content: %v\n", result)
}
-
```
-### 流式生成
-```go
+### **流式生成**
+```go
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- messages := []*schema.Message{
- {
- Role: schema.User,
- Content: "Write a short poem about spring, word by word.",
- },
- }
-
- stream, err := cm.Stream(ctx, messages)
- if err != nil {
- log.Printf("Stream error: %v", err)
- return
- }
-
- fmt.Print("Assistant: ")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("Stream receive error: %v", err)
- return
- }
- if reasoning, ok := deepseek.GetReasoningContent(resp); ok {
- fmt.Printf("Reasoning Content: %s\n", reasoning)
- }
- if len(resp.Content) > 0 {
- fmt.Printf("Content: %s\n", resp.Content)
- }
- if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
- fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
- resp.ResponseMeta.Usage.PromptTokens,
- resp.ResponseMeta.Usage.CompletionTokens,
- resp.ResponseMeta.Usage.TotalTokens)
- }
- }
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ messages := []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring, word by word.",
+ },
+ }
+
+ stream, err := cm.Stream(ctx, messages)
+ if err != nil {
+ log.Printf("Stream error: %v", err)
+ return
+ }
+
+ fmt.Print("Assistant: ")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("Stream receive error: %v", err)
+ return
+ }
+ if reasoning, ok := deepseek.GetReasoningContent(resp); ok {
+ fmt.Printf("Reasoning Content: %s\n", reasoning)
+ }
+ if len(resp.Content) > 0 {
+ fmt.Printf("Content: %s\n", resp.Content)
+ }
+ if resp.ResponseMeta != nil && resp.ResponseMeta.Usage != nil {
+ fmt.Printf("Tokens used: %d (prompt) + %d (completion) = %d (total) \n",
+ resp.ResponseMeta.Usage.PromptTokens,
+ resp.ResponseMeta.Usage.CompletionTokens,
+ resp.ResponseMeta.Usage.TotalTokens)
+ }
+ }
}
-
```
-### 工具调用
+### **工具调用**
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
- "time"
-
- "github.com/cloudwego/eino-ext/components/model/deepseek"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/deepseek"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- apiKey := os.Getenv("DEEPSEEK_API_KEY")
- if apiKey == "" {
- log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
- }
- cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
- APIKey: apiKey,
- Model: os.Getenv("MODEL_NAME"),
- BaseURL: "https://api.deepseek.com/beta",
- Timeout: 30 * time.Second,
- })
- if err != nil {
- log.Fatal(err)
- }
-
- _, err = cm.WithTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Retrieve the user's company and position based on their name and email.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"}}),
- }, {
- Name: "user_salary",
- Desc: "Retrieve the user's salary based on their name and email.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"},
- }),
- }})
- if err != nil {
- log.Fatalf("BindTools of deepseek failed, err=%v", err)
- }
- resp, err := cm.Generate(ctx, []*schema.Message{{
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- }})
- if err != nil {
- log.Fatalf("Generate of deepseek failed, err=%v", err)
- }
- fmt.Printf("output:%v \n", resp)
-
- streamResp, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- },
- })
- if err != nil {
- log.Fatalf("Stream of deepseek failed, err=%v", err)
- }
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
- resp, err = schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of deepseek failed, err=%v", err)
- }
- fmt.Printf("stream output:%v \n", resp)
+ ctx := context.Background()
+ apiKey := os.Getenv("DEEPSEEK_API_KEY")
+ if apiKey == "" {
+ log.Fatal("DEEPSEEK_API_KEY environment variable is not set")
+ }
+ cm, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
+ APIKey: apiKey,
+ Model: os.Getenv("MODEL_NAME"),
+ BaseURL: "https://api.deepseek.com/beta",
+ Timeout: 30 * time.Second,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ _, err = cm.WithTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Retrieve the user's company and position based on their name and email.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"}}),
+ }, {
+ Name: "user_salary",
+ Desc: "Retrieve the user's salary based on their name and email.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"},
+ }),
+ }})
+ if err != nil {
+ log.Fatalf("BindTools of deepseek failed, err=%v", err)
+ }
+ resp, err := cm.Generate(ctx, []*schema.Message{{
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ }})
+ if err != nil {
+ log.Fatalf("Generate of deepseek failed, err=%v", err)
+ }
+ fmt.Printf("output:%v \n", resp)
+
+ streamResp, err := cm.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream of deepseek failed, err=%v", err)
+ }
+ var messages []*schema.Message
+ for {
+ chunk, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamResp failed, err=%v", err)
+ }
+ messages = append(messages, chunk)
+ }
+ resp, err = schema.ConcatMessages(messages)
+ if err != nil {
+ log.Fatalf("ConcatMessages of deepseek failed, err=%v", err)
+ }
+ fmt.Printf("stream output:%v \n", resp)
}
-
```
-
-## 更多信息
+## **更多信息**
- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
- [DeepSeek Documentation](https://api-docs.deepseek.com/api/create-chat-completion)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
index c9761a36204..4de3ef6860c 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md
@@ -1,12 +1,14 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - gemini
+title: ChatModel - Gemini
weight: 0
---
+# Google Gemini
+
一个针对 [Eino](https://github.com/cloudwego/eino) 的 Google Gemini 实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
## 特性
@@ -34,87 +36,87 @@ go get github.com/cloudwego/eino-ext/components/model/gemini@latest
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: "gemini-1.5-flash",
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- // If you are using a model that supports image understanding (e.g., gemini-1.5-flash-image-preview),
- // you can provide both image and text input like this:
- /*
- image, err := os.ReadFile("./path/to/your/image.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- */
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- fmt.Printf("Assistant: %s\n", resp.Content)
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
+ apiKey := os.Getenv("GEMINI_API_KEY")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: "gemini-1.5-flash",
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ // If you are using a model that supports image understanding (e.g., gemini-1.5-flash-image-preview),
+ // you can provide both image and text input like this:
+ /*
+ image, err := os.ReadFile("./path/to/your/image.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ */
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
}
```
@@ -124,65 +126,65 @@ func main() {
```go
type Config struct {
- // Client is the Gemini API client instance
- // Required for making API calls to Gemini
- Client *genai.Client
-
- // Model specifies which Gemini model to use
- // Examples: "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"
- Model string
-
- // MaxTokens limits the maximum number of tokens in the response
- // Optional. Example: maxTokens := 100
- MaxTokens *int
-
- // Temperature controls randomness in responses
- // Range: [0.0, 1.0], where 0.0 is more focused and 1.0 is more creative
- // Optional. Example: temperature := float32(0.7)
- Temperature *float32
-
- // TopP controls diversity via nucleus sampling
- // Range: [0.0, 1.0], where 1.0 disables nucleus sampling
- // Optional. Example: topP := float32(0.95)
- TopP *float32
-
- // TopK controls diversity by limiting the top K tokens to sample from
- // Optional. Example: topK := int32(40)
- TopK *int32
-
- // ResponseSchema defines the structure for JSON responses
- // Optional. Used when you want structured output in JSON format
- ResponseSchema *openapi3.Schema
-
- // EnableCodeExecution allows the model to execute code
- // Warning: Be cautious with code execution in production
- // Optional. Default: false
- EnableCodeExecution bool
-
- // SafetySettings configures content filtering for different harm categories
- // Controls the model's filtering behavior for potentially harmful content
- // Optional.
- SafetySettings []*genai.SafetySetting
-
- ThinkingConfig *genai.ThinkingConfig
-
- // ResponseModalities specifies the modalities the model can return.
- // Optional.
- ResponseModalities []
-
- MediaResolution genai.MediaResolution
-
- // Cache controls prefix cache settings for the model.
- // Optional. used to CreatePrefixCache for reused inputs.
- Cache *CacheConfig
+ // Client is the Gemini API client instance
+ // Required for making API calls to Gemini
+ Client *genai.Client
+
+ // Model specifies which Gemini model to use
+ // Examples: "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"
+ Model string
+
+ // MaxTokens limits the maximum number of tokens in the response
+ // Optional. Example: maxTokens := 100
+ MaxTokens *int
+
+ // Temperature controls randomness in responses
+ // Range: [0.0, 1.0], where 0.0 is more focused and 1.0 is more creative
+ // Optional. Example: temperature := float32(0.7)
+ Temperature *float32
+
+ // TopP controls diversity via nucleus sampling
+ // Range: [0.0, 1.0], where 1.0 disables nucleus sampling
+ // Optional. Example: topP := float32(0.95)
+ TopP *float32
+
+ // TopK controls diversity by limiting the top K tokens to sample from
+ // Optional. Example: topK := int32(40)
+ TopK *int32
+
+ // ResponseSchema defines the structure for JSON responses
+ // Optional. Used when you want structured output in JSON format
+ ResponseSchema *openapi3.Schema
+
+ // EnableCodeExecution allows the model to execute code
+ // Warning: Be cautious with code execution in production
+ // Optional. Default: false
+ EnableCodeExecution bool
+
+ // SafetySettings configures content filtering for different harm categories
+ // Controls the model's filtering behavior for potentially harmful content
+ // Optional.
+ SafetySettings []*genai.SafetySetting
+
+ ThinkingConfig *genai.ThinkingConfig
+
+ // ResponseModalities specifies the modalities the model can return.
+ // Optional.
+ ResponseModalities []
+
+ MediaResolution genai.MediaResolution
+
+ // Cache controls prefix cache settings for the model.
+ // Optional. used to CreatePrefixCache for reused inputs.
+ Cache *CacheConfig
}
// CacheConfig controls prefix cache settings for the model.
type CacheConfig struct {
- // TTL specifies how long cached resources remain valid (now + TTL).
- TTL time.Duration `json:"ttl,omitempty"`
- // ExpireTime sets the absolute expiration timestamp for cached resources.
- ExpireTime time.Time `json:"expireTime,omitempty"`
+ // TTL specifies how long cached resources remain valid (now + TTL).
+ TTL time.Duration `json:"ttl,omitempty"`
+ // ExpireTime sets the absolute expiration timestamp for cached resources.
+ ExpireTime time.Time `json:"expireTime,omitempty"`
}
```
@@ -194,652 +196,947 @@ type CacheConfig struct {
- 隐式缓存:由 Gemini 自身管理。服务可能会自动重用先前的请求或响应。到期和重用由 Gemini 控制,无法配置。
下面的示例展示了如何创建前缀缓存并在后续调用中重用它。
+
```go
toolInfoList := []*schema.ToolInfo{
- {
- Name: "tool_a",
- Desc: "desc",
- ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{}),
- },
+ {
+ Name: "tool_a",
+ Desc: "desc",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{}),
+ },
}
cacheInfo, _ := cm.CreatePrefixCache(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: `aaa`,
- },
- {
- Role: schema.User,
- Content: `bbb`,
- },
- }, model.WithTools(toolInfoList))
+ {
+ Role: schema.System,
+ Content: `aaa`,
+ },
+ {
+ Role: schema.User,
+ Content: `bbb`,
+ },
+ }, model.WithTools(toolInfoList))
msg, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "give a very short summary about this transcript",
- },
- }, gemini.WithCachedContentName(cacheInfo.Name))
+ {
+ Role: schema.User,
+ Content: "give a very short summary about this transcript",
+ },
+ }, gemini.WithCachedContentName(cacheInfo.Name))
```
-
## 示例
### 文本生成
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "What is the capital of France?",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- fmt.Printf("Assistant: %s\n", resp.Content)
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "What is the capital of France?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ fmt.Printf("Assistant: %s\n", resp.Content)
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
}
-
```
### 多模态支持(图片理解)
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- imageStr := base64.StdEncoding.EncodeToString(image)
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &imageStr,
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
-
```
### 携带前缀缓存文本生成
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino/components/model"
- "github.com/cloudwego/eino/components/tool/utils"
- "github.com/cloudwego/eino/schema"
- "google.golang.org/genai"
-
- "github.com/cloudwego/eino-ext/components/model/gemini"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
- ctx := context.Background()
-
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: os.Getenv("GEMINI_API_KEY"),
- })
- if err != nil {
- log.Fatalf("genai.NewClient failed: %v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Model: os.Getenv("GEMINI_MODEL"),
- Client: client,
- })
- if err != nil {
- log.Fatalf("gemini.NewChatModel failed: %v", err)
- }
-
- type toolCallInput struct {
- Answer int `json:"answer" jsonschema_description:"the answer of the question"`
- }
- answerTool, err := utils.InferTool("answer_to_user",
- "answer to user",
- func(ctx context.Context, in *toolCallInput) (string, error) {
- return fmt.Sprintf("answer: %v", in.Answer), nil
- })
- if err != nil {
- log.Fatalf("utils.InferTool failed: %v", err)
- }
-
- info, err := answerTool.Info(ctx)
- if err != nil {
- log.Fatalf("get tool info failed: %v", err)
- }
-
- // this file is from gemini cache usage example
- fileData, err := os.ReadFile("./a11.test.txt")
- if err != nil {
- log.Fatalf("os.ReadFile failed: %v", err)
- }
-
- txtFileBase64 := base64.StdEncoding.EncodeToString(fileData)
- cacheInfo, err := cm.CreatePrefixCache(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: `You are an expert at analyzing transcripts.
+ ctx := context.Background()
+
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: os.Getenv("GEMINI_API_KEY"),
+ })
+ if err != nil {
+ log.Fatalf("genai.NewClient failed: %v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Model: os.Getenv("GEMINI_MODEL"),
+ Client: client,
+ })
+ if err != nil {
+ log.Fatalf("gemini.NewChatModel failed: %v", err)
+ }
+
+ type toolCallInput struct {
+ Answer int `json:"answer" jsonschema_description:"the answer of the question"`
+ }
+ answerTool, err := utils.InferTool("answer_to_user",
+ "answer to user",
+ func(ctx context.Context, in *toolCallInput) (string, error) {
+ return fmt.Sprintf("answer: %v", in.Answer), nil
+ })
+ if err != nil {
+ log.Fatalf("utils.InferTool failed: %v", err)
+ }
+
+ info, err := answerTool.Info(ctx)
+ if err != nil {
+ log.Fatalf("get tool info failed: %v", err)
+ }
+
+ // this file is from gemini cache usage example
+ fileData, err := os.ReadFile("./a11.test.txt")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed: %v", err)
+ }
+
+ txtFileBase64 := base64.StdEncoding.EncodeToString(fileData)
+ cacheInfo, err := cm.CreatePrefixCache(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: `You are an expert at analyzing transcripts.
answer the question with the tool "answer_to_user"
always include the start_time and end_time of the transcript in the output`,
- },
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeFileURL,
- File: &schema.MessageInputFile{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &txtFileBase64,
- MIMEType: "text/plain",
- },
- },
- },
- },
- },
- }, model.WithTools([]*schema.ToolInfo{info}), model.WithToolChoice(schema.ToolChoiceForced))
- if err != nil {
- log.Fatalf("CreatePrefixCache failed: %v", err)
- }
-
- data, _ := sonic.MarshalIndent(cacheInfo, "", " ")
- log.Printf("prefix cache info:\n%v\n", string(data))
-
- msg, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "give a very short summary about this transcript",
- },
- }, gemini.WithCachedContentName(cacheInfo.Name),
- model.WithTools([]*schema.ToolInfo{info}),
- model.WithToolChoice(schema.ToolChoiceForced))
- if err != nil {
- log.Fatalf("Generate failed: %v", err)
- }
- msgData, _ := sonic.MarshalIndent(msg, "", " ")
- log.Printf("model output:\n%v\n", string(msgData))
+ },
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeFileURL,
+ File: &schema.MessageInputFile{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &txtFileBase64,
+ MIMEType: "text/plain",
+ },
+ },
+ },
+ },
+ },
+ }, model.WithTools([]*schema.ToolInfo{info}), model.WithToolChoice(schema.ToolChoiceForced))
+ if err != nil {
+ log.Fatalf("CreatePrefixCache failed: %v", err)
+ }
+
+ data, _ := sonic.MarshalIndent(cacheInfo, "", " ")
+ log.Printf("prefix cache info:\n%v\n", string(data))
+
+ msg, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "give a very short summary about this transcript",
+ },
+ }, gemini.WithCachedContentName(cacheInfo.Name),
+ model.WithTools([]*schema.ToolInfo{info}),
+ model.WithToolChoice(schema.ToolChoiceForced))
+ if err != nil {
+ log.Fatalf("Generate failed: %v", err)
+ }
+ msgData, _ := sonic.MarshalIndent(msg, "", " ")
+ log.Printf("model output:\n%v\n", string(msgData))
}
-
```
### 流式生成
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
- stream, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Write a short poem about spring.",
- },
- })
- if err != nil {
- log.Fatalf("Stream error: %v", err)
- }
-
- fmt.Println("Assistant: ")
- for {
- resp, err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Stream receive error: %v", err)
- }
-
- fmt.Println("frame: ")
- if len(resp.Content) > 0 {
- fmt.Println("content: ", resp.Content)
- }
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
- }
- fmt.Println()
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+ stream, err := cm.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Write a short poem about spring.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream error: %v", err)
+ }
+
+ fmt.Println("Assistant: ")
+ for {
+ resp, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Stream receive error: %v", err)
+ }
+
+ fmt.Println("frame: ")
+ if len(resp.Content) > 0 {
+ fmt.Println("content: ", resp.Content)
+ }
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
+ }
+ fmt.Println()
}
-
```
### 工具调用
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ThinkingConfig: &genai.ThinkingConfig{
- IncludeThoughts: true,
- ThinkingBudget: nil,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "book_recommender",
- Desc: "Recommends books based on user preferences and provides purchase links",
- ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
- "genre": {
- Type: "string",
- Desc: "Preferred book genre",
- Enum: []string{"fiction", "sci-fi", "mystery", "biography", "business"},
- },
- "max_pages": {
- Type: "integer",
- Desc: "Maximum page length (0 for no limit)",
- },
- "min_rating": {
- Type: "number",
- Desc: "Minimum user rating (0-5 scale)",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("Bind tools error: %v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
-
- if len(resp.ToolCalls) > 0 {
- fmt.Printf("Function called: \n")
- if len(resp.ReasoningContent) > 0 {
- fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
- }
- fmt.Println("Name: ", resp.ToolCalls[0].Function.Name)
- fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
- } else {
- log.Printf("Function called without tool calls: %s\n", resp.Content)
- }
-
- resp, err = cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
- },
- resp,
- {
- Role: schema.Tool,
- ToolCallID: resp.ToolCalls[0].ID,
- Content: "{\"book name\":\"Microeconomics for Managers\"}",
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- fmt.Printf("Function call final result: %s\n", resp.Content)
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ThinkingConfig: &genai.ThinkingConfig{
+ IncludeThoughts: true,
+ ThinkingBudget: nil,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+ err = cm.BindTools([]*schema.ToolInfo{
+ {
+ Name: "book_recommender",
+ Desc: "Recommends books based on user preferences and provides purchase links",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "genre": {
+ Type: "string",
+ Desc: "Preferred book genre",
+ Enum: []string{"fiction", "sci-fi", "mystery", "biography", "business"},
+ },
+ "max_pages": {
+ Type: "integer",
+ Desc: "Maximum page length (0 for no limit)",
+ },
+ "min_rating": {
+ Type: "number",
+ Desc: "Minimum user rating (0-5 scale)",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Fatalf("Bind tools error: %v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+
+ if len(resp.ToolCalls) > 0 {
+ fmt.Printf("Function called: \n")
+ if len(resp.ReasoningContent) > 0 {
+ fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
+ }
+ fmt.Println("Name: ", resp.ToolCalls[0].Function.Name)
+ fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
+ } else {
+ log.Printf("Function called without tool calls: %s\n", resp.Content)
+ }
+
+ resp, err = cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
+ },
+ resp,
+ {
+ Role: schema.Tool,
+ ToolCallID: resp.ToolCalls[0].ID,
+ Content: "{\"book name\":\"Microeconomics for Managers\"}",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ fmt.Printf("Function call final result: %s\n", resp.Content)
}
-
```
### 图片生成
```go
-
package main
import (
- "context"
- "encoding/json"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "log"
+ "os"
- "google.golang.org/genai"
+ "google.golang.org/genai"
- "github.com/cloudwego/eino-ext/components/model/gemini"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- apiKey := os.Getenv("GEMINI_API_KEY")
- modelName := os.Getenv("GEMINI_MODEL")
-
- ctx := context.Background()
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: apiKey,
- })
- if err != nil {
- log.Fatalf("NewClient of gemini failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Client: client,
- Model: modelName,
- ResponseModalities: []gemini.GeminiResponseModality{
- gemini.GeminiResponseModalityText,
- gemini.GeminiResponseModalityImage,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel of gemini failed, err=%v", err)
- }
-
- /*
- The generated multimodal content is stored in the `AssistantGenMultiContent` field.
- For this example, the resulting message will have a structure similar to this:
-
- resp := &schema.Message{
- Role: schema.Assistant,
- AssistantGenMultiContent: []schema.MessageOutputPart{
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageOutputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: &base64String, // The base64 encoded image data
- MIMEType: "image/png",
- },
- },
- },
- },
- }
- */
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "Generate an image of a cat",
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate error: %v", err)
- }
- log.Printf("\ngenerate output: \n")
- respBody, _ := json.MarshalIndent(resp, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ modelName := os.Getenv("GEMINI_MODEL")
+
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: modelName,
+ ResponseModalities: []gemini.GeminiResponseModality{
+ gemini.GeminiResponseModalityText,
+ gemini.GeminiResponseModalityImage,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of gemini failed, err=%v", err)
+ }
+
+ /*
+ The generated multimodal content is stored in the `AssistantGenMultiContent` field.
+ For this example, the resulting message will have a structure similar to this:
+
+ resp := &schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent: []schema.MessageOutputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &base64String, // The base64 encoded image data
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate error: %v", err)
+ }
+ log.Printf("\ngenerate output: \n")
+ respBody, _ := json.MarshalIndent(resp, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
-
```
### React Agent 模式示例
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino/adk"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/components/tool/utils"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
- "google.golang.org/genai"
-
- "github.com/cloudwego/eino-ext/components/model/gemini"
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino/adk"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/components/tool/utils"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
- ctx := context.Background()
-
- client, err := genai.NewClient(ctx, &genai.ClientConfig{
- APIKey: os.Getenv("GEMINI_API_KEY"),
- })
- if err != nil {
- log.Fatalf("genai.NewClient failed, err=%v", err)
- }
-
- cm, err := gemini.NewChatModel(ctx, &gemini.Config{
- Model: os.Getenv("GEMINI_MODEL"),
- Client: client,
- })
- if err != nil {
- log.Fatalf("gemini.NewChatModel failed, err=%v", err)
- }
-
- type toolCallInput struct {
- LastCount int `json:"last_count" jsonschema_description:"the last count"`
- }
- countsTool, err := utils.InferTool("count_tool_call",
- "count the number of tool calls",
- func(ctx context.Context, in *toolCallInput) (string, error) {
- counts := in.LastCount + 1
- return fmt.Sprintf("tool call counts: %v", counts), nil
- })
- if err != nil {
- log.Fatalf("utils.InferTool failed, err=%v", err)
- }
-
- agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
- Name: "react_agent",
- Description: "react_agent",
- Instruction: `call count_tool_call 5 times, then say 'done'`,
- Model: cm,
- ToolsConfig: adk.ToolsConfig{
- ToolsNodeConfig: compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{
- countsTool,
- },
- },
- },
- })
- if err != nil {
- log.Fatalf("adk.NewChatModelAgent failed, err=%v", err)
- }
-
- iter := agent.Run(ctx, &adk.AgentInput{
- Messages: []adk.Message{
- {
- Role: schema.User,
- Content: "start to count",
- },
- },
- })
- idx := 0
- for {
- event, ok := iter.Next()
- if !ok {
- break
- }
-
- if event.Err != nil {
- log.Fatalf("agent.Run failed, err=%v", event.Err)
- }
-
- msg, err_ := event.Output.MessageOutput.GetMessage()
- if err_ != nil {
- log.Fatalf("GetMessage failed, err=%v", err_)
- }
-
- idx++
- msgData, _ := sonic.MarshalIndent(msg, "", " ")
- log.Printf("\nmessage %v:\n%v\n", idx, string(msgData))
- }
+ ctx := context.Background()
+
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: os.Getenv("GEMINI_API_KEY"),
+ })
+ if err != nil {
+ log.Fatalf("genai.NewClient failed, err=%v", err)
+ }
+
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Model: os.Getenv("GEMINI_MODEL"),
+ Client: client,
+ })
+ if err != nil {
+ log.Fatalf("gemini.NewChatModel failed, err=%v", err)
+ }
+
+ type toolCallInput struct {
+ LastCount int `json:"last_count" jsonschema_description:"the last count"`
+ }
+ countsTool, err := utils.InferTool("count_tool_call",
+ "count the number of tool calls",
+ func(ctx context.Context, in *toolCallInput) (string, error) {
+ counts := in.LastCount + 1
+ return fmt.Sprintf("tool call counts: %v", counts), nil
+ })
+ if err != nil {
+ log.Fatalf("utils.InferTool failed, err=%v", err)
+ }
+
+ agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
+ Name: "react_agent",
+ Description: "react_agent",
+ Instruction: `call count_tool_call 5 times, then say 'done'`,
+ Model: cm,
+ ToolsConfig: adk.ToolsConfig{
+ ToolsNodeConfig: compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{
+ countsTool,
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("adk.NewChatModelAgent failed, err=%v", err)
+ }
+
+ iter := agent.Run(ctx, &adk.AgentInput{
+ Messages: []adk.Message{
+ {
+ Role: schema.User,
+ Content: "start to count",
+ },
+ },
+ })
+ idx := 0
+ for {
+ event, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ if event.Err != nil {
+ log.Fatalf("agent.Run failed, err=%v", event.Err)
+ }
+
+ msg, err_ := event.Output.MessageOutput.GetMessage()
+ if err_ != nil {
+ log.Fatalf("GetMessage failed, err=%v", err_)
+ }
+
+ idx++
+ msgData, _ := sonic.MarshalIndent(msg, "", " ")
+ log.Printf("\nmessage %v:\n%v\n", idx, string(msgData))
+ }
}
+```
+
+## **基本介绍**
+
+~~Gemini 模型是 ChatModel 接口的一个实现,用于与 Google 的 GenAI 系列模型进行交互。该组件实现了 ~~[Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+
+~~目前已支持多模态输入(文本、图片、视频、音频、文件)和多模态输出(文本、图片)~~
+## **使用方式**
+
+### **组件初始化**
+
+Gemini 模型通过 NewChatModel 函数进行初始化,主要配置参数如下:
+
+```go
+import (
+ "context"
+
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+)
+
+ctx := context.Background()
+client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: "your-api-key", // API Key 认证
+ HTTPClient: httpClient, // 自定义 HTTP 客户端
+})
+
+cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ // 模型及服务配置
+ Client: client, // 配置好的 client
+ Model: "gemini-2.5-flash", // 使用的模型
+
+ // 生成参数
+ MaxTokens: &maxTokens, // 最大生成长度
+ Temperature: &temp, // 温度
+ TopP: &topP, // Top-P 采样
+
+ // 多模态设置
+ ResponseModalities: []GeminiResponseModality{} // 指定模型返回的模态 支持 "TEXT", "IMAGE", "AUDIO"
+})
```
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+// invoke模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import (
+ "encoding/base64"
+ "os"
+
+ "github.com/cloudwego/eino/schema"
+)
+
+// 生成 base64 格式的图片数据
+image, err := os.ReadFile("./examples/image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+imageStr := base64.StdEncoding.EncodeToString(image)
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ },
+ },
+}
+```
+
+### **工具调用**
+
+支持绑定工具和强制工具调用:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+// 绑定可选工具
+err := model.BindTools(tools)
+
+// 绑定强制工具
+err := model.BindForcedTools(tools)
+```
+
+> 工具相关信息,可以参考 [Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+### **完整使用示例**
+
+#### **直接对话**
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+)
+
+func main() {
+ // 初始化 client 及模型
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: "gemini-2.5-flash-image-preview",
+ ResponseModalities: []gemini.GeminiResponseModality{
+ gemini.GeminiResponseModalityText,
+ gemini.GeminiResponseModalityImage,
+ },
+ })
+ if err != nil {
+ log.Printf("NewChatModel error: %v", err)
+ return
+ }
+
+ // 生成 base64 格式的图片数据
+ image, err := os.ReadFile("./examples/image/cat.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个图片生成助手,可以仿照用户给定的图片生成一个风格近似的图片"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ /*
+ 生成的多模态内容存储在 AssistantGentMultiContent 字段中
+ 本例中最终生成的 message 形如:
+ AssistantMessage = schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent : []schema.MessageOutputPart{
+ {Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &DataStr,
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+ fmt.Printf("Assistant: %s\n", resp)
+}
+```
+
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/components/model"
+ "github.com/cloudwego/eino/schema"
+ "google.golang.org/genai"
+
+ "github.com/cloudwego/eino-ext/components/model/gemini"
+)
+
+func main() {
+ // 初始化 client 及模型
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ ctx := context.Background()
+ client, err := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if err != nil {
+ log.Fatalf("NewClient of gemini failed, err=%v", err)
+ }
+ cm, err := gemini.NewChatModel(ctx, &gemini.Config{
+ Client: client,
+ Model: "gemini-2.5-flash-image-preview",
+ ResponseModalities: []gemini.GeminiResponseModality{
+ gemini.GeminiResponseModalityText,
+ gemini.GeminiResponseModalityImage,
+ },
+ })
+ if err != nil {
+ log.Printf("NewChatModel error: %v", err)
+ return
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ fmt.Printf("Assistant: %s\n", chunk)
+ }
+}
+```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini/examples)
-## 更多信息
+## **相关文档**
-- [Eino Documentation](https://github.com/cloudwego/eino)
-- [Gemini API Documentation](https://ai.google.dev/api/generate-content?hl=zh-cn#v1beta.GenerateContentResponse)
+- [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [Eino: ToolsNode&Tool 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
index 6747136744e..d553a1fde54 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md
@@ -1,12 +1,14 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - ollama
+title: ChatModel - Ollama
weight: 0
---
+# Ollama 模型
+
一个针对 [Eino](https://github.com/cloudwego/eino) 的 Ollama 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
## 特性
@@ -33,49 +35,47 @@ go get github.com/cloudwego/eino-ext/components/model/ollama@latest
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
-
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output: \n%v\n", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output: \n%v\n", resp)
}
-
```
## 配置
可以使用 `ollama.ChatModelConfig` 结构体配置模型:
-
```go
type ChatModelConfig struct {
BaseURL string `json:"base_url"`
@@ -131,317 +131,509 @@ type ThinkValue struct {
// Value can be a bool or string
Value interface{}
}
-
```
-
## 示例
### 文本生成
```go
-
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output: \n%v\n", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output: \n%v\n", resp)
}
-
```
### 多模态支持(图片理解)
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- multiModalMsg := &schema.Message{
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "this picture is a landscape photo, what's the picture's content",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ multiModalMsg := &schema.Message{
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "this picture is a landscape photo, what's the picture's content",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ multiModalMsg,
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](a T) *T {
- return &a
+ return &a
}
-
```
### 流式生成
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- defer streamMsgs.Close()
-
- log.Println("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Printf("\nstream.Recv failed, err=%v", err)
- return
- }
- fmt.Print(msg.Content)
- }
-
- fmt.Print("\n")
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ defer streamMsgs.Close()
+
+ log.Println("typewriter output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Printf("\nstream.Recv failed, err=%v", err)
+ return
+ }
+ fmt.Print(msg.Content)
+ }
+
+ fmt.Print("\n")
}
-
```
### 工具调用
```go
-
package main
import (
- "context"
- "log"
- "os"
+ "context"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v", err)
- return
- }
-
- err = chatModel.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Printf("BindForcedTools failed, err=%v", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Printf("Generate failed, err=%v", err)
- return
- }
-
- log.Printf("output: \n%+v", resp)
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v", err)
+ return
+ }
+
+ err = chatModel.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Printf("BindForcedTools failed, err=%v", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Printf("Generate failed, err=%v", err)
+ return
+ }
+
+ log.Printf("output: \n%+v", resp)
+}
+```
+
+### 开启 Thinking 模式
+
+```go
+package main
+
+import (
+ "context"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino/schema"
+ ollamaapi "github.com/eino-contrib/ollama/api"
+
+ "github.com/cloudwego/eino-ext/components/model/ollama"
+)
+
+func main() {
+ ctx := context.Background()
+ modelName := os.Getenv("MODEL_NAME")
+ thinking := ollamaapi.ThinkValue{Value: true}
+ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Model: modelName,
+ Thinking: &thinking,
+ })
+ if err != nil {
+ log.Printf("NewChatModel failed, err=%v\n", err)
+ return
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Printf("Generate failed, err=%v\n", err)
+ return
+ }
+
+ log.Printf("output thinking: \n%v\n", resp.ReasoningContent)
+ log.Printf("output content: \n%v\n", resp.Content)
}
+```
+
+##
+
+## **基本介绍**
+
+~~Ollama 模型是 ChatModel 接口的一个实现,用于与 Ollama 本地大语言模型服务进行交互,Ollama 是一个开源的本地大语言模型运行框架,支持多种开源模型(如 Llama、Mistral 等),提供简单的 API 接口和完整的性能监控。。该组件实现了 ~~[Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+
+## **使用方式**
+
+### **组件初始化**
+
+Ollama 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下:
+
+```go
+import (
+ "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/ollama/ollama/api"
+)
+model, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ // 基础配置
+ BaseURL: "http://localhost:11434", // Ollama 服务地址
+ Timeout: 30 * time.Second, // 请求超时时间
+
+ // 模型配置
+ Model: "llama2", // 模型名称
+ Format: json.RawMessage("json"), // 输出格式(可选)
+ KeepAlive: &keepAlive, // 保持连接时间
+
+ // 模型参数
+ Options: &api.Options{
+ Runner: api.Runner{
+ NumCtx: 4096, // 上下文窗口大小
+ NumGPU: 1, // GPU 数量
+ NumThread: 4, // CPU 线程数
+ },
+ Temperature: 0.7, // 温度
+ TopP: 0.9, // Top-P 采样
+ TopK: 40, // Top-K 采样
+ Seed: 42, // 随机种子
+ NumPredict: 100, // 最大生成长度
+ Stop: []string{}, // 停止词
+ RepeatPenalty: 1.1, // 重复惩罚
+ },
+})
```
-### 开启Thinking模式
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+// 普通模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 用户消息
+ schema.UserMessage("你好")
+}
+```
+
+### **工具调用**
+
+支持绑定工具:
+
+> 注意,仅有支持 function call 的模型才能使用这个能力
```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+
+// 绑定工具
+err := model.BindTools(tools)
+```
+
+### **完整使用示例**
+
+#### **基本对话**
+```go
package main
import (
- "context"
- "log"
- "os"
-
- "github.com/cloudwego/eino/schema"
- ollamaapi "github.com/eino-contrib/ollama/api"
+ "context"
+ "time"
- "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino/schema"
+ "github.com/ollama/ollama/api"
)
func main() {
- ctx := context.Background()
- modelName := os.Getenv("MODEL_NAME")
- thinking := ollamaapi.ThinkValue{Value: true}
- chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
- BaseURL: "http://localhost:11434",
- Model: modelName,
- Thinking: &thinking,
- })
- if err != nil {
- log.Printf("NewChatModel failed, err=%v\n", err)
- return
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Printf("Generate failed, err=%v\n", err)
- return
- }
-
- log.Printf("output thinking: \n%v\n", resp.ReasoningContent)
- log.Printf("output content: \n%v\n", resp.Content)
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Timeout: 30 * time.Second,
+ Model: "llama2",
+ Options: &api.Options{
+ Temperature: 0.7,
+ NumPredict: 100,
+ },
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("介绍一下 Ollama"),
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ println(response.Content)
}
+```
+
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/ollama"
+ "github.com/cloudwego/eino/schema"
+)
+func main() {
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
+ BaseURL: "http://localhost:11434",
+ Timeout: 30 * time.Second,
+ Model: "llama2",
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("讲个笑话"),
+ }
+
+ // 获取流式回复
+ stream, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer stream.Close() // 注意关闭 reader
+
+ // 处理流式内容
+ for {
+ chunk, err := stream.Recv()
+ if err != nil {
+ break
+ }
+ print(chunk.Content)
+ }
+}
```
-## 更多信息
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/ollama/examples)
+
+## **相关文档**
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [Ollama Documentation](https://ollama.readthedocs.io/api/#generate-a-chat-completion)
+- [[🚧]Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [[🚧]ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai)
+- [[🚧]Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [Ollama 模型库](https://ollama.ai/library)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
index 1b33df9797a..aad68498cb5 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md
@@ -1,85 +1,84 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - openai
+title: ChatModel - OpenAI
weight: 0
---
-一个针对 [Eino](https://github.com/cloudwego/eino) 的 OpenAI 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
+## **OpenAI 模型**
-## 特性
+一个针对 [Eino](https://github.com/cloudwego/eino) 的 OpenAI 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力
-- Implements `github.com/cloudwego/eino/components/model.Model`
-- Easy integration with Eino's model system
-- Configurable model parameters
-- Support for chat completion
-- Support for streaming responses
-- Custom response parsing support
-- Flexible model configuration
+## **特性**
+
+- 实现了 `github.com/cloudwego/eino/components/model.Model`
+- 轻松与 Eino 的模型系统集成
+- 可配置的模型参数
+- 支持聊天补全
+- 支持流式响应
+- 支持自定义响应解析
+- 灵活的模型配置
## 安装
-```bash
+```go
go get github.com/cloudwego/eino-ext/components/model/openai@latest
```
-## 快速开始
+## 快速开始[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai/#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
以下是如何使用 OpenAI 模型的快速示例:
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
+ // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
+ // ByAzure: true,
+ // APIVersion: "2024-06-01",
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+ fmt.Printf("output: \n%v", resp)
}
-
-
```
## 配置
@@ -87,7 +86,6 @@ func main() {
可以使用 `openai.ChatModelConfig` 结构体配置模型:
```go
-
type ChatModelConfig struct {
// APIKey is your authentication key
// Use OpenAI API key or Azure API key depending on the service
@@ -200,469 +198,743 @@ Audio *Audio `json:"audio,omitempty"`
}
```
-
## 示例
-### 文本生成
+### 文本生成[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai/#%E6%96%87%E6%9C%AC%E7%94%9F%E6%88%90)
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
+ // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
+ // ByAzure: true,
+ // APIVersion: "2024-06-01",
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+ fmt.Printf("output: \n%v", resp)
}
-
```
-### 多模态支持(图片理解)
+### 多模态支持(图片理解)[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai/#%E5%A4%9A%E6%A8%A1%E6%80%81%E6%94%AF%E6%8C%81%E5%9B%BE%E7%89%87%E7%90%86%E8%A7%A3)
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
-
- }
-
- multiModalMsg := &schema.Message{
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "this picture is a landscape photo, what's the picture's content",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- multiModalMsg,
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+
+ }
+
+ multiModalMsg := &schema.Message{
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "this picture is a landscape photo, what's the picture's content",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ URL: of("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT11qEDxU4X_MVKYQVU5qiAVFidA58f8GG0bQ&s"),
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ multiModalMsg,
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](a T) *T {
- return &a
+ return &a
}
-
```
### 流式生成
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel of openai failed, err=%v", err)
- }
-
- streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.User,
- Content: "as a machine, how do you answer user's question?",
- },
- })
-
- if err != nil {
- log.Fatalf("Stream of openai failed, err=%v", err)
- }
-
- defer streamMsgs.Close()
-
- fmt.Printf("typewriter output:")
- for {
- msg, err := streamMsgs.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamMsgs failed, err=%v", err)
- }
- fmt.Print(msg.Content)
- }
-
- fmt.Print("\n")
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of openai failed, err=%v", err)
+ }
+
+ streamMsgs, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ Content: "as a machine, how do you answer user's question?",
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("Stream of openai failed, err=%v", err)
+ }
+
+ defer streamMsgs.Close()
+
+ fmt.Printf("typewriter output:")
+ for {
+ msg, err := streamMsgs.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamMsgs failed, err=%v", err)
+ }
+ fmt.Print(msg.Content)
+ }
+
+ fmt.Print("\n")
}
-
```
-### 工具调用
+### 工具调用[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai/#%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8)
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- })
- if err != nil {
- log.Fatalf("NewChatModel of openai failed, err=%v", err)
- }
- err = chatModel.BindForcedTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Retrieve the user's company and position based on their name and email.",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"}}),
- }, {
- Name: "user_salary",
- Desc: "Retrieve the user's salary based on their name and email.\n",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {Type: "string", Desc: "user's name"},
- "email": {Type: "string", Desc: "user's email"},
- }),
- }})
- if err != nil {
- log.Fatalf("BindForcedTools of openai failed, err=%v", err)
- }
- resp, err := chatModel.Generate(ctx, []*schema.Message{{
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- }})
- if err != nil {
- log.Fatalf("Generate of openai failed, err=%v", err)
- }
- fmt.Printf("output: \n%v", resp)
-
- streamResp, err := chatModel.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
- }, {
- Role: schema.User,
- Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
- },
- })
- if err != nil {
- log.Fatalf("Stream of openai failed, err=%v", err)
- }
- var messages []*schema.Message
- for {
- chunk, err := streamResp.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("Recv of streamResp failed, err=%v", err)
- }
- messages = append(messages, chunk)
- }
- resp, err = schema.ConcatMessages(messages)
- if err != nil {
- log.Fatalf("ConcatMessages of openai failed, err=%v", err)
- }
- fmt.Printf("stream output: \n%v", resp)
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of openai failed, err=%v", err)
+ }
+ err = chatModel.BindForcedTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Retrieve the user's company and position based on their name and email.",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"}}),
+ }, {
+ Name: "user_salary",
+ Desc: "Retrieve the user's salary based on their name and email.\n",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {Type: "string", Desc: "user's name"},
+ "email": {Type: "string", Desc: "user's email"},
+ }),
+ }})
+ if err != nil {
+ log.Fatalf("BindForcedTools of openai failed, err=%v", err)
+ }
+ resp, err := chatModel.Generate(ctx, []*schema.Message{{
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ }})
+ if err != nil {
+ log.Fatalf("Generate of openai failed, err=%v", err)
+ }
+ fmt.Printf("output: \n%v", resp)
+
+ streamResp, err := chatModel.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "As a real estate agent, provide relevant property information based on the user's salary and job using the user_company and user_salary APIs. An email address is required.",
+ }, {
+ Role: schema.User,
+ Content: "My name is John and my email is john@abc.com,Please recommend some houses that suit me.",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream of openai failed, err=%v", err)
+ }
+ var messages []*schema.Message
+ for {
+ chunk, err := streamResp.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ log.Fatalf("Recv of streamResp failed, err=%v", err)
+ }
+ messages = append(messages, chunk)
+ }
+ resp, err = schema.ConcatMessages(messages)
+ if err != nil {
+ log.Fatalf("ConcatMessages of openai failed, err=%v", err)
+ }
+ fmt.Printf("stream output: \n%v", resp)
}
-
```
-### 音频生成
+### 音频生成[ ](http://localhost:1313/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai/#%E9%9F%B3%E9%A2%91%E7%94%9F%E6%88%90)
```go
-
package main
import (
- "context"
+ "context"
- "log"
- "os"
+ "log"
+ "os"
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/model/openai"
- "github.com/cloudwego/eino/schema"
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
- // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
- // ByAzure: true,
- // APIVersion: "2024-06-01",
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ReasoningEffort: openai.ReasoningEffortLevelHigh,
- Modalities: []openai.Modality{openai.AudioModality, openai.TextModality},
- Audio: &openai.Audio{
- Format: openai.AudioFormatMp3,
- Voice: openai.AudioVoiceAlloy,
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {Type: schema.ChatMessagePartTypeText, Text: "help me convert the following text to speech"},
- {Type: schema.ChatMessagePartTypeText, Text: "Hello, what can I help you with?"},
- },
- },
- })
- if err != nil {
- log.Fatalf("Generate failed, err=%v", err)
- }
-
- respBody, _ := sonic.MarshalIndent(resp, " ", " ")
- log.Printf(" body: %s\n", string(respBody))
+ ctx := context.Background()
+
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // 如果您想使用 Azure OpenAI 服务,请设置这两个字段。
+ // BaseURL: "https://{RESOURCE_NAME}.openai.azure.com",
+ // ByAzure: true,
+ // APIVersion: "2024-06-01",
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ ReasoningEffort: openai.ReasoningEffortLevelHigh,
+ Modalities: []openai.Modality{openai.AudioModality, openai.TextModality},
+ Audio: &openai.Audio{
+ Format: openai.AudioFormatMp3,
+ Voice: openai.AudioVoiceAlloy,
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {Type: schema.ChatMessagePartTypeText, Text: "help me convert the following text to speech"},
+ {Type: schema.ChatMessagePartTypeText, Text: "Hello, what can I help you with?"},
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("Generate failed, err=%v", err)
+ }
+
+ respBody, _ := sonic.MarshalIndent(resp, " ", " ")
+ log.Printf(" body: %s\n", string(respBody))
}
-
```
### 结构化输出
```go
-
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "os"
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
- "github.com/eino-contrib/jsonschema"
- orderedmap "github.com/wk8/go-ordered-map/v2"
+ "github.com/eino-contrib/jsonschema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino/schema"
- "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino-ext/components/model/openai"
)
func main() {
- type Person struct {
- Name string `json:"name"`
- Height int `json:"height"`
- Weight int `json:"weight"`
- }
-
- js := &jsonschema.Schema{
- Type: string(schema.Object),
- Properties: orderedmap.New[string, *jsonschema.Schema](
- orderedmap.WithInitialData[string, *jsonschema.Schema](
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "name",
- Value: &jsonschema.Schema{
- Type: string(schema.String),
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "height",
- Value: &jsonschema.Schema{
- Type: string(schema.Integer),
- },
- },
- orderedmap.Pair[string, *jsonschema.Schema]{
- Key: "weight",
- Value: &jsonschema.Schema{
- Type: string(schema.Integer),
- },
- },
- ),
- ),
- }
-
- ctx := context.Background()
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- APIKey: os.Getenv("OPENAI_API_KEY"),
- Model: os.Getenv("OPENAI_MODEL"),
- BaseURL: os.Getenv("OPENAI_BASE_URL"),
- ByAzure: func() bool {
- if os.Getenv("OPENAI_BY_AZURE") == "true" {
- return true
- }
- return false
- }(),
- ResponseFormat: &openai.ChatCompletionResponseFormat{
- Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
- JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
- Name: "person",
- Description: "data that describes a person",
- Strict: false,
- JSONSchema: js,
- },
- },
- })
- if err != nil {
- log.Fatalf("NewChatModel failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "Parse the user input into the specified json struct",
- },
- {
- Role: schema.User,
- Content: "John is one meter seventy tall and weighs sixty kilograms",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of openai failed, err=%v", err)
- }
-
- result := &Person{}
- err = json.Unmarshal([]byte(resp.Content), result)
- if err != nil {
- log.Fatalf("Unmarshal of openai failed, err=%v", err)
- }
- fmt.Printf("%+v", *result)
+ type Person struct {
+ Name string `json:"name"`
+ Height int `json:"height"`
+ Weight int `json:"weight"`
+ }
+
+ js := &jsonschema.Schema{
+ Type: string(schema.Object),
+ Properties: orderedmap.New[string, *jsonschema.Schema](
+ orderedmap.WithInitialData[string, *jsonschema.Schema](
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "name",
+ Value: &jsonschema.Schema{
+ Type: string(schema.String),
+ },
+ },
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "height",
+ Value: &jsonschema.Schema{
+ Type: string(schema.Integer),
+ },
+ },
+ orderedmap.Pair[string, *jsonschema.Schema]{
+ Key: "weight",
+ Value: &jsonschema.Schema{
+ Type: string(schema.Integer),
+ },
+ },
+ ),
+ ),
+ }
+
+ ctx := context.Background()
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ Model: os.Getenv("OPENAI_MODEL"),
+ BaseURL: os.Getenv("OPENAI_BASE_URL"),
+ ByAzure: func() bool {
+ if os.Getenv("OPENAI_BY_AZURE") == "true" {
+ return true
+ }
+ return false
+ }(),
+ ResponseFormat: &openai.ChatCompletionResponseFormat{
+ Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
+ JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
+ Name: "person",
+ Description: "data that describes a person",
+ Strict: false,
+ JSONSchema: js,
+ },
+ },
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "Parse the user input into the specified json struct",
+ },
+ {
+ Role: schema.User,
+ Content: "John is one meter seventy tall and weighs sixty kilograms",
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of openai failed, err=%v", err)
+ }
+
+ result := &Person{}
+ err = json.Unmarshal([]byte(resp.Content), result)
+ if err != nil {
+ log.Fatalf("Unmarshal of openai failed, err=%v", err)
+ }
+ fmt.Printf("%+v", *result)
+}
+```
+
+## **使用方式**
+
+### **组件初始化**
+
+OpenAI 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下:
+
+```go
+import "github.com/cloudwego/eino-ext/components/model/openai"
+
+model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ // Azure OpenAI Service 配置(可选)
+ ByAzure: false, // 是否使用 Azure OpenAI
+ BaseURL: "your-url", // Azure API 基础 URL
+ APIVersion: "2023-05-15", // Azure API 版本
+
+ // 基础配置
+ APIKey: "your-key", // API 密钥
+ Timeout: 30 * time.Second, // 超时时间
+
+ // 模型参数
+ Model: "gpt-4", // 模型名称
+ MaxTokens: &maxTokens,// 最大生成长度
+ Temperature: &temp, // 温度
+ TopP: &topP, // Top-P 采样
+ Stop: []string{},// 停止词
+ PresencePenalty: &pp, // 存在惩罚
+ FrequencyPenalty: &fp, // 频率惩罚
+
+ // 高级参数
+ ResponseFormat: &format, // 响应格式
+ Seed: &seed, // 随机种子
+ LogitBias: map[string]int{}, // Token 偏置
+ User: &user, // 用户标识
+
+ ReasoningEffort:openai.ReasoningEffortLevelHigh, // 推理级别, 默认 "medium"
+
+ Modalities: make([]openai.Modality, 0), // 模型回复模态类型: ["text","audio"] 默认 text
+
+ Audio: &openai.Audio{ // 音频输出参数,当模态存在audio时,此字段必填
+ Format: openai.AudioFormatMp3,
+ Voice: openai.AudioVoiceAlloy,
+ },
+
+ ExtraFields: map[string]any{}, // 额外字段,此字段将新增或者覆盖访问请求字段,用于实验性验证
+
+})
+```
+
+> - 参数具体含义,可以参考: [https://platform.openai.com/docs/api-reference/chat/create](https://platform.openai.com/docs/api-reference/chat/create)
+> - azure 相关服务,可以参考: [https://learn.microsoft.com/en-us/azure/ai-services/openai/](https://learn.microsoft.com/en-us/azure/ai-services/openai/)
+
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+// invoke模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import (
+ "os"
+ "encoding/base64"
+
+ "github.com/cloudwego/eino/schema"
+)
+
+// base64 格式的图片数据
+image, err := os.ReadFile("./examples/image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+imageStr := base64.StdEncoding.EncodeToString(image)
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ },
+ },
}
+```
+
+### **工具调用**
+
+支持绑定工具和强制工具调用:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+// 绑定可选工具
+err := model.BindTools(tools)
+
+// 绑定强制工具
+err := model.BindForcedTools(tools)
+```
+
+> 工具相关信息,可以参考 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+### **完整使用示例**
+
+#### **直接对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
+)
+func main() {
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: "your-api-key", // required
+ Timeout: 30 * time.Second,
+ Model: "gpt-4", // required
+
+ // 如果模型支持语音生成,并且有需求生成语音时,需要进行如下配置
+ // Modalities: []openai.Modality{openai.AudioModality, openai.TextModality},
+ //Audio: &openai.Audio{
+ // Format: openai.AudioFormatMp3,
+ // Voice: openai.AudioVoiceAlloy,
+ //},
+},
+
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // base64 格式的图片数据
+ image, err := os.ReadFile("./examples/image/cat.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ // 请求消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个图片生成助手,可以仿照用户给定的图片生成一个风格近似的图片"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ /*
+ 生成的多模态内容存储在 response.AssistantGentMultiContent 字段中
+ 本例中最终生成的 message 形如:
+ AssistantMessage = schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent : []schema.MessageOutputPart{
+ {Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &DataStr,
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+
+ fmt.Printf("Assistant: %s\n", resp)
+}
```
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ APIKey: "your-api-key",
+ Timeout: 30 * time.Second,
+ Model: "gpt-4",
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("写一个故事"),
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ print(chunk.Content)
+ }
+}
+```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/openai/examples)
-## 更多信息
+## **相关文档**
-- [Eino Documentation](https://www.cloudwego.io/zh/docs/eino/)
-- [OpenAI Documentation](https://platform.openai.com/docs/api-reference/chat/create)
+- [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
index 19c6b2c5063..0dd13931dc9 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md
@@ -1,12 +1,14 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - qianfan
+title: ChatModel - Qianfan
weight: 0
---
+## **Qianfan 模型**
+
一个针对 [Eino](https://github.com/cloudwego/eino) 的千帆模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
## 特性
@@ -29,54 +31,52 @@ go get github.com/cloudwego/eino-ext/components/model/qianfan@latest
以下是如何使用千帆模型的快速示例:
-
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- ir, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(ir)
- // assistant: 你好!我是文心一言,很高兴与你交流。请问你有什么想问我的吗?无论是关于知识、创作还是其他任何问题,我都会尽力回答你。
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ ir, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(ir)
+ // assistant: 你好!我是文心一言,很高兴与你交流。请问你有什么想问我的吗?无论是关于知识、创作还是其他任何问题,我都会尽力回答你。
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
@@ -84,362 +84,614 @@ func of[T any](t T) *T {
可以使用 `qianfan.ChatModelConfig` 结构体配置模型:
-
```go
// ChatModelConfig config for qianfan chat completion
// see: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Wm3fhy2vb
type ChatModelConfig struct {
- // Model is the model to use for the chat completion.
- Model string
+ // Model is the model to use for the chat completion.
+ Model string
- // LLMRetryCount is the number of times to retry a failed request.
- LLMRetryCount *int
+ // LLMRetryCount is the number of times to retry a failed request.
+ LLMRetryCount *int
- // LLMRetryTimeout is the timeout for each retry attempt.
- LLMRetryTimeout *float32
+ // LLMRetryTimeout is the timeout for each retry attempt.
+ LLMRetryTimeout *float32
- // LLMRetryBackoffFactor is the backoff factor for retries.
- LLMRetryBackoffFactor *float32
+ // LLMRetryBackoffFactor is the backoff factor for retries.
+ LLMRetryBackoffFactor *float32
- // Temperature controls the randomness of the output. A higher value makes the output more random, while a lower value makes it more focused and deterministic. Default is 0.95, range (0, 1.0].
- Temperature *float32
+ // Temperature controls the randomness of the output. A higher value makes the output more random, while a lower value makes it more focused and deterministic. Default is 0.95, range (0, 1.0].
+ Temperature *float32
- // TopP controls the diversity of the output. A higher value increases the diversity of the generated text. Default is 0.7, range [0, 1.0].
- TopP *float32
+ // TopP controls the diversity of the output. A higher value increases the diversity of the generated text. Default is 0.7, range [0, 1.0].
+ TopP *float32
- // PenaltyScore reduces the generation of repetitive tokens by adding a penalty. A higher value means a larger penalty. Range: [1.0, 2.0].
- PenaltyScore *float64
+ // PenaltyScore reduces the generation of repetitive tokens by adding a penalty. A higher value means a larger penalty. Range: [1.0, 2.0].
+ PenaltyScore *float64
- // MaxCompletionTokens is the maximum number of tokens to generate in the completion. Range [2, 2048].
- MaxCompletionTokens *int
+ // MaxCompletionTokens is the maximum number of tokens to generate in the completion. Range [2, 2048].
+ MaxCompletionTokens *int
- // Seed is the random seed for generation. Range (0, 2147483647).
- Seed *int
+ // Seed is the random seed for generation. Range (0, 2147483647).
+ Seed *int
- // Stop is a list of strings that will stop the generation when the model generates a token that is a suffix of one of the strings.
- Stop []string
+ // Stop is a list of strings that will stop the generation when the model generates a token that is a suffix of one of the strings.
+ Stop []string
- // User is a unique identifier representing the end-user.
- User *string
+ // User is a unique identifier representing the end-user.
+ User *string
- // FrequencyPenalty specifies the frequency penalty to control the repetition of generated text. Range [-2.0, 2.0].
- FrequencyPenalty *float64
+ // FrequencyPenalty specifies the frequency penalty to control the repetition of generated text. Range [-2.0, 2.0].
+ FrequencyPenalty *float64
- // PresencePenalty specifies the presence penalty to control the repetition of generated text. Range [-2.0, 2.0].
- PresencePenalty *float64
+ // PresencePenalty specifies the presence penalty to control the repetition of generated text. Range [-2.0, 2.0].
+ PresencePenalty *float64
- // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true.
- ParallelToolCalls *bool
+ // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true.
+ ParallelToolCalls *bool
- // ResponseFormat specifies the format of the response.
- ResponseFormat *ResponseFormat
+ // ResponseFormat specifies the format of the response.
+ ResponseFormat *ResponseFormat
}
```
-
-
-
-
-
-
-
## 示例
### 文本生成
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
-
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- ir, err := cm.Generate(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(ir)
+ ctx := context.Background()
+
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ ir, err := cm.Generate(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(ir)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 多模态理解(图片理解)
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- Seed: of(0),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(image)),
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ Seed: of(0),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: of(base64.StdEncoding.EncodeToString(image)),
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 流式生成
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- sr, err := cm.Stream(ctx, []*schema.Message{
- schema.UserMessage("hello"),
- })
-
- if err != nil {
- log.Fatalf("Stream of qianfan failed, err=%v", err)
- }
-
- var ms []*schema.Message
- for {
- m, err := sr.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
-
- log.Fatalf("Stream of qianfan failed, err=%v", err)
- }
-
- fmt.Println(m)
- ms = append(ms, m)
- }
- sm, err := schema.ConcatMessages(ms)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- fmt.Println(sm)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ sr, err := cm.Stream(ctx, []*schema.Message{
+ schema.UserMessage("hello"),
+ })
+
+ if err != nil {
+ log.Fatalf("Stream of qianfan failed, err=%v", err)
+ }
+
+ var ms []*schema.Message
+ for {
+ m, err := sr.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+
+ log.Fatalf("Stream of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(m)
+ ms = append(ms, m)
+ }
+ sm, err := schema.ConcatMessages(ms)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ fmt.Println(sm)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 工具调用
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qianfan"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- qcfg := qianfan.GetQianfanSingletonConfig()
- // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
- qcfg.AccessKey = "your_access_key"
- qcfg.SecretKey = "your_secret_key"
-
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
- Model: modelName,
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- MaxCompletionTokens: of(1024),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
- }
-
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "Query the user's company and position information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "Query the user's salary information based on their name and email",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "The user's name",
- },
- "email": {
- Type: "string",
- Desc: "The user's email",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("BindTools of qianfan failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
- },
- {
- Role: schema.User,
- Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of qianfan failed, err=%v", err)
- }
-
- fmt.Println(resp)
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: modelName,
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ MaxCompletionTokens: of(1024),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qianfan failed, err=%v", err)
+ }
+
+ err = cm.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "Query the user's company and position information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "Query the user's salary information based on their name and email",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "The user's name",
+ },
+ "email": {
+ Type: "string",
+ Desc: "The user's email",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Fatalf("BindTools of qianfan failed, err=%v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "You are a real estate agent. Use the user_company and user_salary APIs to provide relevant property information based on the user's salary and job. Email is required",
+ },
+ {
+ Role: schema.User,
+ Content: "My name is zhangsan, and my email is zhangsan@bytedance.com. Please recommend some suitable houses for me.",
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qianfan failed, err=%v", err)
+ }
+
+ fmt.Println(resp)
}
func of[T any](t T) *T {
- return &t
+ return &t
+}
+```
+
+## **使用方式**
+
+### **组件初始化**
+
+Qianfan 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下:
+
+```go
+import "github.com/cloudwego/eino-ext/components/model/qianfan"
+ctx := context.Background()
+qcfg := qianfan.GetQianfanSingletonConfig()
+// How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+qcfg.AccessKey = "your_access_key"
+qcfg.SecretKey = "your_secret_key"
+
+model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: "ernie-3.5-8k", // 模型名称, 指定使用的模型,例如 ERNIE-Bot-4
+ Temperature: of(float32(0.7)), // 温度,较高的值(如 0.8)会使输出更随机,而较低的值(如 0.2)会使其更具确定性。默认 0.95,范围 (0, 1.0]
+ TopP: of(float32(0.7)), // Top-p,影响输出文本的多样性,取值越大,生成文本的候选词越多。默认 0.7,范围 [0, 1.0]
+ MaxCompletionTokens: of(1024), // 生成结果的最大 token 数量, 范围 [2, 2048]
+ Seed: of(0), // 随机种子, 用于生成可复现结果, 范围 (0, 2147483647)
+ Stop: make([]string, 0), // 停止符, 一个字符串列表,当模型生成其中任何一个字符串时,会停止生成
+ PenaltyScore: nil, // 重复惩罚, 通过对已生成的 token 增加惩罚,减少重复。较高的值意味着较大的惩罚。范围 [1.0, 2.0]
+ LLMRetryCount: of(0), // 请求失败的重试次数
+ LLMRetryTimeout: nil, // 每次重试请求的超时时间(秒)
+ LLMRetryBackoffFactor: nil, // 重试之间的等待时间的增长因子
+
+ FrequencyPenalty: nil, // 频率惩罚, 根据 token 在文本中出现的频率来惩罚新 token。较高的值会鼓励模型使用较不频繁的 token
+ PresencePenalty: nil, // 存在惩罚, 根据 token 是否已在文本中出现来惩罚新 token。较高的值会鼓励模型引入新概念
+ ParallelToolCalls: nil, // 并行工具调用, 是否允许模型并行调用多个工具
+ ResponseFormat: &qianfan.ResponseFormat{}, // 指定响应的格式,例如 JSON
+})
+```
+
+> - 参数具体含义,可以参考: [https://cloud.baidu.com/doc/qianfan-api/s/3m7of64lb](https://cloud.baidu.com/doc/qianfan-api/s/3m7of64lb)
+
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+
+```go
+// invoke模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import (
+ "os"
+ "encoding/base64"
+
+ "github.com/cloudwego/eino/schema"
+)
+
+// base64 格式的图片数据
+image, err := os.ReadFile("./examples/image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+imageStr := base64.StdEncoding.EncodeToString(image)
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ },
+ },
}
+```
+
+### **工具调用**
+
+支持绑定工具和强制工具调用:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+// 绑定可选工具
+err := model.BindTools(tools)
+
+// 绑定强制工具
+err := model.BindForcedTools(tools)
+```
+
+> 工具相关信息,可以参考 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+### **完整使用示例**
+
+#### **直接对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
+)
+func main() {
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+
+ model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: "ernie-3.5-8k",
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // base64 格式的图片数据
+ image, err := os.ReadFile("./examples/image/cat.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ // 请求消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个图片生成助手,可以仿照用户给定的图片生成一个风格近似的图片"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ /*
+ 生成的多模态内容存储在 response.AssistantGentMultiContent 字段中
+ 本例中最终生成的 message 形如:
+ AssistantMessage = schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent : []schema.MessageOutputPart{
+ {Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &DataStr,
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+
+ fmt.Printf("Assistant: %s\n", resp)
+}
```
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/qianfan"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+ qcfg := qianfan.GetQianfanSingletonConfig()
+ // How to get Access Key/Secret Key: https://cloud.baidu.com/doc/Reference/s/9jwvz2egb
+ qcfg.AccessKey = "your_access_key"
+ qcfg.SecretKey = "your_secret_key"
+
+ model, err := qianfan.NewChatModel(ctx, &qianfan.ChatModelConfig{
+ Model: "ernie-3.5-8k",
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("写一个故事"),
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ print(chunk.Content)
+ }
+}
+```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/qianfan/examples)
-## 更多信息
+## **相关文档**
-- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/)
-- [千帆文档](https://cloud.baidu.com/doc/qianfan-api/s/rm7u7qdiq)
+- [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
index d7d2faf0c1b..e31ae95b7cd 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md
@@ -1,12 +1,14 @@
---
Description: ""
-date: "2025-12-02"
+date: "2025-12-03"
lastmod: ""
tags: []
-title: ChatModel - qwen
+title: ChatModel - Qwen
weight: 0
---
+## **Qwen 模型**
+
一个针对 [Eino](https://github.com/cloudwego/eino) 的 Qwen 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。
## 特性
@@ -43,43 +45,44 @@ import (
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("as a machine, how do you answer user's question?"),
- })
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("as a machine, how do you answer user's question?"),
+ })
+ if err != nil {
+ log.Fatalf("Generate of qwen failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
```
## 配置
可以使用 `qwen.ChatModelConfig` 结构体配置模型:
+
```go
type ChatModelConfig struct {
@@ -160,7 +163,6 @@ User *string `json:"user,omitempty"`
// Optional. Default: base on the Model
EnableThinking *bool `json:"enable_thinking,omitempty"`
}
-
```
## 示例
@@ -168,360 +170,632 @@ EnableThinking *bool `json:"enable_thinking,omitempty"`
### 文本生成
```go
-
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- schema.UserMessage("as a machine, how do you answer user's question?"),
- })
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Printf("output: \n%v", resp)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ schema.UserMessage("as a machine, how do you answer user's question?"),
+ })
+ if err != nil {
+ log.Fatalf("Generate of qwen failed, err=%v", err)
+ }
+
+ fmt.Printf("output: \n%v", resp)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 多模态理解(图片理解)
```go
-
package main
import (
- "context"
- "encoding/base64"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
-
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
- if err != nil {
- log.Fatalf("os.ReadFile failed, err=%v\n", err)
- }
-
- resp, err := chatModel.Generate(ctx, []*schema.Message{
- {
- Role: schema.User,
- UserInputMultiContent: []schema.MessageInputPart{
- {
- Type: schema.ChatMessagePartTypeText,
- Text: "What do you see in this image?",
- },
- {
- Type: schema.ChatMessagePartTypeImageURL,
- Image: &schema.MessageInputImage{
- MessagePartCommon: schema.MessagePartCommon{
- Base64Data: of(base64.StdEncoding.EncodeToString(image)),
- MIMEType: "image/jpeg",
- },
- Detail: schema.ImageURLDetailAuto,
- },
- },
- },
- },
- })
- if err != nil {
- log.Printf("Generate error: %v", err)
- return
- }
- fmt.Printf("Assistant: %s\n", resp.Content)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ chatModel, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ resp, err := chatModel.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "What do you see in this image?",
+ },
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: of(base64.StdEncoding.EncodeToString(image)),
+ MIMEType: "image/jpeg",
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ },
+ },
+ })
+ if err != nil {
+ log.Printf("Generate error: %v", err)
+ return
+ }
+ fmt.Printf("Assistant: %s\n", resp.Content)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 流式生成
```go
-
package main
import (
- "context"
- "fmt"
- "io"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- sr, err := cm.Stream(ctx, []*schema.Message{
- schema.UserMessage("你好"),
- })
- if err != nil {
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- var msgs []*schema.Message
- for {
- msg, err := sr.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
-
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- fmt.Println(msg)
- // assistant: 你好
- // finish_reason:
- // : !
- // finish_reason:
- // : 有什么
- // finish_reason:
- // : 可以帮助
- // finish_reason:
- // : 你的吗?
- // finish_reason:
- // :
- // finish_reason: stop
- // usage: &{9 7 16}
- msgs = append(msgs, msg)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
-
- fmt.Println(msg)
- // assistant: 你好!有什么可以帮助你的吗?
- // finish_reason: stop
- // usage: &{9 7 16}
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ sr, err := cm.Stream(ctx, []*schema.Message{
+ schema.UserMessage("你好"),
+ })
+ if err != nil {
+ log.Fatalf("Stream of qwen failed, err=%v", err)
+ }
+
+ var msgs []*schema.Message
+ for {
+ msg, err := sr.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+
+ log.Fatalf("Stream of qwen failed, err=%v", err)
+ }
+
+ fmt.Println(msg)
+ // assistant: 你好
+ // finish_reason:
+ // : !
+ // finish_reason:
+ // : 有什么
+ // finish_reason:
+ // : 可以帮助
+ // finish_reason:
+ // : 你的吗?
+ // finish_reason:
+ // :
+ // finish_reason: stop
+ // usage: &{9 7 16}
+ msgs = append(msgs, msg)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+
+ fmt.Println(msg)
+ // assistant: 你好!有什么可以帮助你的吗?
+ // finish_reason: stop
+ // usage: &{9 7 16}
}
func of[T any](t T) *T {
- return &t
+ return &t
}
-
```
### 工具调用
```go
-
package main
import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/model/qwen"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
)
func main() {
- ctx := context.Background()
- // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
- apiKey := os.Getenv("DASHSCOPE_API_KEY")
- modelName := os.Getenv("MODEL_NAME")
- cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
- BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
- APIKey: apiKey,
- Timeout: 0,
- Model: modelName,
- MaxTokens: of(2048),
- Temperature: of(float32(0.7)),
- TopP: of(float32(0.7)),
- })
- if err != nil {
- log.Fatalf("NewChatModel of qwen failed, err=%v", err)
- }
-
- err = cm.BindTools([]*schema.ToolInfo{
- {
- Name: "user_company",
- Desc: "根据用户的姓名和邮箱,查询用户的公司和职位信息",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "用户的姓名",
- },
- "email": {
- Type: "string",
- Desc: "用户的邮箱",
- },
- }),
- },
- {
- Name: "user_salary",
- Desc: "根据用户的姓名和邮箱,查询用户的薪酬信息",
- ParamsOneOf: schema.NewParamsOneOfByParams(
- map[string]*schema.ParameterInfo{
- "name": {
- Type: "string",
- Desc: "用户的姓名",
- },
- "email": {
- Type: "string",
- Desc: "用户的邮箱",
- },
- }),
- },
- })
- if err != nil {
- log.Fatalf("BindTools of qwen failed, err=%v", err)
- }
-
- resp, err := cm.Generate(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
- },
- {
- Role: schema.User,
- Content: "我的姓名是 zhangsan,我的邮箱是 zhangsan@bytedance.com,请帮我推荐一些适合我的房子。",
- },
- })
-
- if err != nil {
- log.Fatalf("Generate of qwen failed, err=%v", err)
- }
-
- fmt.Println(resp)
- // assistant:
- // tool_calls: [{0x14000275930 call_1e25169e05fc4596a55afb function {user_company {"email": "zhangsan@bytedance.com", "name": "zhangsan"}} map[]}]
- // finish_reason: tool_calls
- // usage: &{316 32 348}
-
- // ==========================
- // using stream
- fmt.Printf("\n\n======== Stream ========\n")
- sr, err := cm.Stream(ctx, []*schema.Message{
- {
- Role: schema.System,
- Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
- },
- {
- Role: schema.User,
- Content: "我的姓名是 lisi,我的邮箱是 lisi@bytedance.com,请帮我推荐一些适合我的房子。",
- },
- })
- if err != nil {
- log.Fatalf("Stream of qwen failed, err=%v", err)
- }
-
- msgs := make([]*schema.Message, 0)
- for {
- msg, err := sr.Recv()
- if err != nil {
- break
- }
- jsonMsg, err := json.Marshal(msg)
- if err != nil {
- log.Fatalf("json.Marshal failed, err=%v", err)
- }
- fmt.Printf("%s\n", jsonMsg)
- msgs = append(msgs, msg)
- }
-
- msg, err := schema.ConcatMessages(msgs)
- if err != nil {
- log.Fatalf("ConcatMessages failed, err=%v", err)
- }
- jsonMsg, err := json.Marshal(msg)
- if err != nil {
- log.Fatalf("json.Marshal failed, err=%v", err)
- }
- fmt.Printf("final: %s\n", jsonMsg)
+ ctx := context.Background()
+ // get api key: https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key?spm=a2c4g.11186623.help-menu-2400256.d_3_0.1ebc47bb0ClCgF
+ apiKey := os.Getenv("DASHSCOPE_API_KEY")
+ modelName := os.Getenv("MODEL_NAME")
+ cm, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ APIKey: apiKey,
+ Timeout: 0,
+ Model: modelName,
+ MaxTokens: of(2048),
+ Temperature: of(float32(0.7)),
+ TopP: of(float32(0.7)),
+ })
+ if err != nil {
+ log.Fatalf("NewChatModel of qwen failed, err=%v", err)
+ }
+
+ err = cm.BindTools([]*schema.ToolInfo{
+ {
+ Name: "user_company",
+ Desc: "根据用户的姓名和邮箱,查询用户的公司和职位信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "用户的姓名",
+ },
+ "email": {
+ Type: "string",
+ Desc: "用户的邮箱",
+ },
+ }),
+ },
+ {
+ Name: "user_salary",
+ Desc: "根据用户的姓名和邮箱,查询用户的薪酬信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(
+ map[string]*schema.ParameterInfo{
+ "name": {
+ Type: "string",
+ Desc: "用户的姓名",
+ },
+ "email": {
+ Type: "string",
+ Desc: "用户的邮箱",
+ },
+ }),
+ },
+ })
+ if err != nil {
+ log.Fatalf("BindTools of qwen failed, err=%v", err)
+ }
+
+ resp, err := cm.Generate(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
+ },
+ {
+ Role: schema.User,
+ Content: "我的姓名是 zhangsan,我的邮箱是 zhangsan@bytedance.com,请帮我推荐一些适合我的房子。",
+ },
+ })
+
+ if err != nil {
+ log.Fatalf("Generate of qwen failed, err=%v", err)
+ }
+
+ fmt.Println(resp)
+ // assistant:
+ // tool_calls: [{0x14000275930 call_1e25169e05fc4596a55afb function {user_company {"email": "zhangsan@bytedance.com", "name": "zhangsan"}} map[]}]
+ // finish_reason: tool_calls
+ // usage: &{316 32 348}
+
+ // ==========================
+ // using stream
+ fmt.Printf("\n\n======== Stream ========\n")
+ sr, err := cm.Stream(ctx, []*schema.Message{
+ {
+ Role: schema.System,
+ Content: "你是一名房产经纪人,结合用户的薪酬和工作,使用 user_company、user_salary 两个 API,为其提供相关的房产信息。邮箱是必须的",
+ },
+ {
+ Role: schema.User,
+ Content: "我的姓名是 lisi,我的邮箱是 lisi@bytedance.com,请帮我推荐一些适合我的房子。",
+ },
+ })
+ if err != nil {
+ log.Fatalf("Stream of qwen failed, err=%v", err)
+ }
+
+ msgs := make([]*schema.Message, 0)
+ for {
+ msg, err := sr.Recv()
+ if err != nil {
+ break
+ }
+ jsonMsg, err := json.Marshal(msg)
+ if err != nil {
+ log.Fatalf("json.Marshal failed, err=%v", err)
+ }
+ fmt.Printf("%s\n", jsonMsg)
+ msgs = append(msgs, msg)
+ }
+
+ msg, err := schema.ConcatMessages(msgs)
+ if err != nil {
+ log.Fatalf("ConcatMessages failed, err=%v", err)
+ }
+ jsonMsg, err := json.Marshal(msg)
+ if err != nil {
+ log.Fatalf("json.Marshal failed, err=%v", err)
+ }
+ fmt.Printf("final: %s\n", jsonMsg)
}
func of[T any](t T) *T {
- return &t
+ return &t
}
+```
+
+## **使用方式**
+
+### **组件初始化**
+
+Qwen 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下:
+
+```go
+import "github.com/cloudwego/eino-ext/components/model/qwen"
+
+apiKey := os.Getenv("DASHSCOPE_API_KEY")
+model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", // URL
+ // 基础配置
+ APIKey: "api-key", // API 密钥
+ Timeout: 30 * time.Second, // 超时时间
+
+ // 模型参数
+ Model: "qwen-ma", // 模型名称
+ MaxTokens: &maxTokens,// 最大生成长度
+ Temperature: &temp, // 温度
+ TopP: &topP, // Top-P 采样
+ Stop: []string{},// 停止词
+ PresencePenalty: &pp, // 存在惩罚
+ FrequencyPenalty: &fp, // 频率惩罚
+
+ // 高级参数
+ ResponseFormat: &format, // 响应格式
+ Seed: &seed, // 随机种子
+ LogitBias: map[string]int{}, // Token 偏置
+ User: &user, // 用户标识
+ EnableThinking: of(false), // 是否开启思考模式
+ Modalities: make([]qwen.Modality, 0), // 模型回复模态类型: ["text","audio"] 默认 text
+
+ Audio: &qwen.Audio{ // 音频输出参数,当模态存在audio时,此字段必填
+ Format: qwen._AudioFormatWav_,
+ Voice: qwen._AudioVoiceChelsie_,
+ },
+
+})
+```
+
+> - 参数具体含义,可以参考: [https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api](https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api)
+
+### **生成对话**
+
+对话生成支持普通模式和流式模式:
+```go
+// invoke模式
+response, err := model.Generate(ctx, messages)
+
+// 流式模式
+stream, err := model.Stream(ctx, messages)
+```
+
+消息格式示例:
+
+```go
+import (
+ "os"
+ "encoding/base64"
+
+ "github.com/cloudwego/eino/schema"
+)
+
+// base64 格式的图片数据
+image, err := os.ReadFile("./examples/image/eino.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+imageStr := base64.StdEncoding.EncodeToString(image)
+
+messages := []*schema.Message{
+ // 系统消息
+ schema.SystemMessage("你是一个助手"),
+
+ // 多模态消息(包含图片)
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "这张图片是什么?",
+ },
+ },
+ },
+}
+```
+
+### **工具调用**
+
+支持绑定工具和强制工具调用:
+
+```go
+import "github.com/cloudwego/eino/schema"
+
+// 定义工具
+tools := []*schema.ToolInfo{
+ {
+ Name: "search",
+ Desc: "搜索信息",
+ ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
+ "query": {
+ Type: schema.String,
+ Desc: "搜索关键词",
+ Required: true,
+ },
+ }),
+ },
+}
+// 绑定可选工具
+err := model.BindTools(tools)
+
+// 绑定强制工具
+err := model.BindForcedTools(tools)
+```
+
+> 工具相关信息,可以参考 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+
+### **完整使用示例**
+
+#### **直接对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ APIKey: "your-api-key", // required
+ Timeout: 30 * time.Second,
+ Model: "qwen-max", // required
+
+ // 如果模型支持语音生成,并且有需求生成语音时,需要配置如下配置
+ // Modalities: []qwen.Modality{qwen.AudioModality, qwen.TextModality},
+ //Audio: &qwen.Audio{
+ // Format: qwen.AudioFormatMp3,
+ // Voice: qwen.AudioVoiceAlloy,
+ //},
+},
+
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // base64 格式的图片数据
+ image, err := os.ReadFile("./examples/image/cat.png")
+ if err != nil {
+ log.Fatalf("os.ReadFile failed, err=%v\n", err)
+ }
+
+ imageStr := base64.StdEncoding.EncodeToString(image)
+
+ // 请求消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个图片生成助手,可以仿照用户给定的图片生成一个风格近似的图片"),
+ {
+ Role: schema.User,
+ UserInputMultiContent: []schema.MessageInputPart{
+ {
+ Type: schema.ChatMessagePartTypeImage,
+ Image: &schema.MessageInputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &imageStr,
+ MIMEType: "image/png", // required when use Base64Data
+ },
+ Detail: schema.ImageURLDetailAuto,
+ },
+ {
+ Type: schema.ChatMessagePartTypeText,
+ Text: "Generate an image of a cat",
+ },
+ },
+ },
+ }
+
+ // 生成回复
+ response, err := model.Generate(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+
+ // 处理回复
+ /*
+ 生成的多模态内容存储在 response.AssistantGentMultiContent 字段中
+ 本例中最终生成的 message 形如:
+ AssistantMessage = schema.Message{
+ Role: schema.Assistant,
+ AssistantGenMultiContent : []schema.MessageOutputPart{
+ {Type: schema.ChatMessagePartTypeImageURL,
+ Image: &schema.MessageOutputImage{
+ MessagePartCommon: schema.MessagePartCommon{
+ Base64Data: &DataStr,
+ MIMEType: "image/png",
+ },
+ },
+ },
+ },
+ }
+ */
+
+ fmt.Printf("Assistant: %s\n", resp)
+}
+```
+
+#### **流式对话**
+
+```go
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/eino-ext/components/model/qwen"
+ "github.com/cloudwego/eino/schema"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // 初始化模型
+ model, err := qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
+ APIKey: "your-api-key",
+ Timeout: 30 * time.Second,
+ Model: "qwen-max",
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // 准备消息
+ messages := []*schema.Message{
+ schema.SystemMessage("你是一个助手"),
+ schema.UserMessage("写一个故事"),
+ }
+
+ // 获取流式回复
+ reader, err := model.Stream(ctx, messages)
+ if err != nil {
+ panic(err)
+ }
+ defer reader.Close() // 注意要关闭
+
+ // 处理流式内容
+ for {
+ chunk, err := reader.Recv()
+ if err != nil {
+ break
+ }
+ print(chunk.Content)
+ }
+}
```
+### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/qwen/examples)
+## **相关文档**
-## 更多信息
-- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/)
-- [Qwen 文档](https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api)
+- [Eino: ChatModel 使用说明](/zh/docs/eino/core_modules/components/chat_model_guide)
+- [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)
+- [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark)
+- [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
diff --git a/content/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md b/content/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
index 34c9b17c050..b07e5e2a0aa 100644
--- a/content/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
+++ b/content/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-11-20"
lastmod: ""
tags: []
title: ChatTemplate - MCP
@@ -11,13 +11,13 @@ weight: 0
Model Context Protocol(MCP)是 Anthropic 推出的供模型访问的资源的标准化开放协议,Eino 提供了相关封装,使用这些封装可以直接访问已有 MCP Server 上的资源。
-本节介绍 MCPPrompt 的封装,封装实现了 Eino ChatTemplate 接口(详见 [Eino: ChatTemplate 使用说明](/zh/docs/eino/core_modules/components/chat_template_guide))。
+本节介绍 MCPPrompt 的封装,封装实现了 Eino ChatTemplate 接口([Eino: ChatTemplate 使用说明](/zh/docs/eino/core_modules/components/chat_template_guide))。
-
+
其他封装参见:
-[Tool - MCP](/zh/docs/eino/ecosystem_integration/tool/tool_mcp)
+[[WIP]Tool - MCP](/zh/docs/eino/ecosystem_integration/tool/tool_mcp)
## 使用方式
diff --git a/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md b/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md
index e48cba1203f..fcdd008412d 100644
--- a/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md
+++ b/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md
@@ -28,11 +28,11 @@ local file 文件加载器是 Document Loader 接口的一个实现,用于从
```go
import (
- "github.com/cloudwego/eino-ext/components/document/loader/file"
+ "github.com/cloudwego/eino/components/document/loader/file"
)
func main() {
- loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{
+ loader, err := file.NewFileLoader(ctx, &FileLoaderConfig{
UseNameAsID: true, // 是否使用文件名作为文档ID
Parser: &parser.TextParser{}, // 可选:指定自定义解析器
})
diff --git a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md b/content/zh/docs/eino/ecosystem_integration/document/parser_html.md
index b465e23c0b2..5197b8e95de 100644
--- a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md
+++ b/content/zh/docs/eino/ecosystem_integration/document/parser_html.md
@@ -79,7 +79,7 @@ func main() {
}
// HTML 内容
- document := `
+ html := `
示例页面
@@ -96,7 +96,7 @@ func main() {
`
// 解析文档
- docs, err := p.Parse(ctx, strings.NewReader(document),
+ docs, err := p.Parse(ctx, strings.NewReader(html),
parser.WithURI("https://example.com"),
parser.WithExtraMeta(map[string]any{
"custom": "value",
@@ -108,10 +108,10 @@ func main() {
// 使用解析结果
doc := docs[0]
- fmt.Println("内容:", doc.Content)
- fmt.Println("标题:", doc.MetaData[html.MetaKeyTitle])
- fmt.Println("描述:", doc.MetaData[html.MetaKeyDesc])
- fmt.Println("语言:", doc.MetaData[html.MetaKeyLang])
+ println("内容:", doc.Content)
+ println("标题:", doc.MetaData[html.MetaKeyTitle])
+ println("描述:", doc.MetaData[html.MetaKeyDesc])
+ println("语言:", doc.MetaData[html.MetaKeyLang])
}
```
diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md
index a80c334183e..c1594208b20 100644
--- a/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md
+++ b/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md
@@ -48,14 +48,14 @@ package main
import (
"context"
-
+
"github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
"github.com/cloudwego/eino/schema"
)
func main() {
ctx := context.Background()
-
+
// 初始化分割器
splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{
Headers: map[string]string{
@@ -68,13 +68,12 @@ func main() {
if err != nil {
panic(err)
}
-
+
// 准备要分割的文档
docs := []*schema.Document{
{
ID: "doc1",
- Content: `
-# 文档标题
+ Content: `# 文档标题
这是介绍部分的内容。
@@ -90,16 +89,19 @@ func main() {
这是第二章的内容。
-` + "```\n# 这是代码块中的注释,不会被识别为标题\n```",
+\`\`\`
+# 这是代码块中的注释,不会被识别为标题
+\`\`\`
+`,
},
}
-
+
// 执行分割
results, err := splitter.Transform(ctx, docs)
if err != nil {
panic(err)
}
-
+
// 处理分割结果
for i, doc := range results {
println("片段", i+1, ":", doc.Content)
@@ -111,7 +113,6 @@ func main() {
}
}
}
-
```
## **特性说明**
diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
index 66f7b5aa4ab..a92f2b6c1e5 100644
--- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
+++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Embedding - dashscope
diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
index b47dda85336..3d041a3e2bb 100644
--- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
+++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md
@@ -1,83 +1,85 @@
---
Description: ""
-date: "2025-07-30"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Embedding - Ollama
weight: 0
---
-## 基本介绍
+## **基本介绍**
+
这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Ollama Embedding 组件,实现了 `Embedder` 接口,可无缝集成到 Eino 的 embedding 系统中,提供文本向量化能力。
-## 特性
+## **特性**
+
- 实现 `github.com/cloudwego/eino/components/embedding.Embedder` 接口
-- 易于集成到 Eino的工作流
+- 易于集成到 Eino 的工作流
- 支持自定义 Ollama 服务端点和模型
-- Eino内置回调支持
+- Eino 内置回调支持
+
+## **安装**
-## 安装
```bash
- go get github.com/cloudwego/eino-ext/components/embedding/ollama
+go get github.com/cloudwego/eino-ext/components/embedding/ollama
```
-
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "log"
- "os"
- "time"
+ "context"
+ "log"
+ "os"
+ "time"
- "github.com/cloudwego/eino-ext/components/embedding/ollama"
+ "github.com/cloudwego/eino-ext/components/embedding/ollama"
)
func main() {
- ctx := context.Background()
-
- baseURL := os.Getenv("OLLAMA_BASE_URL")
- if baseURL == "" {
- baseURL = "http://localhost:11434" // 默认本地
- }
- model := os.Getenv("OLLAMA_EMBED_MODEL")
- if model == "" {
- model = "nomic-embed-text"
- }
-
- embedder, err := ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
- BaseURL: baseURL,
- Model: model,
- Timeout: 10 * time.Second,
- })
- if err != nil {
- log.Fatalf("NewEmbedder of ollama error: %v", err)
- return
- }
-
- log.Printf("===== call Embedder directly =====")
-
- vectors, err := embedder.EmbedStrings(ctx, []string{"hello", "how are you"})
- if err != nil {
- log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
- }
-
- log.Printf("vectors : %v", vectors)
-
- // you can use WithModel to specify the model
- vectors, err = embedder.EmbedStrings(ctx, []string{"hello", "how are you"}, embedding.WithModel(model))
- if err != nil {
- log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
- }
-
- log.Printf("vectors : %v", vectors)
+ ctx := context.Background()
+
+ baseURL := os.Getenv("OLLAMA_BASE_URL")
+ if baseURL == "" {
+ baseURL = "http://localhost:11434" // 默认本地
+ }
+ model := os.Getenv("OLLAMA_EMBED_MODEL")
+ if model == "" {
+ model = "nomic-embed-text"
+ }
+
+ embedder, err := ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
+ BaseURL: baseURL,
+ Model: model,
+ Timeout: 10 * time.Second,
+ })
+ if err != nil {
+ log.Fatalf("NewEmbedder of ollama error: %v", err)
+ return
+ }
+
+ log.Printf("===== call Embedder directly =====")
+
+ vectors, err := embedder.EmbedStrings(ctx, []string{"hello", "how are you"})
+ if err != nil {
+ log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
+ }
+
+ log.Printf("vectors : %v", vectors)
+
+ // you can use WithModel to specify the model
+ vectors, err = embedder.EmbedStrings(ctx, []string{"hello", "how are you"}, embedding.WithModel(model))
+ if err != nil {
+ log.Fatalf("EmbedStrings of Ollama failed, err=%v", err)
+ }
+
+ log.Printf("vectors : %v", vectors)
}
```
-## 配置说明
+## **配置说明**
embedder 可以通过 `EmbeddingConfig` 结构体进行配置:
diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
index e81b1064575..166acdce774 100644
--- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
+++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md
@@ -1,9 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Embedding - qianfan
+title: Embedding - Qianfan
weight: 0
---
diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
index ecb6cce446d..33ea6777742 100644
--- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
+++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md
@@ -1,17 +1,17 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Embedding - tencentcloud
+title: Embedding - TencentCloud
weight: 0
---
-## 腾讯云混元 Embedding
+## **腾讯云混元 Embedding**
这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的腾讯云混元 Embedding 组件,实现了 `Embedder` 接口。它可以无缝集成到 Eino 的 embedding 系统中,提供文本向量化能力。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/embedding.Embedder` 接口
- 易于集成到 Eino 的 rag 工作流中
@@ -19,13 +19,13 @@ weight: 0
- 自动处理大规模文本数组的批处理
- 内置回调支持
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/embedding/tencentcloud
```
-## 快速开始
+## **快速开始**
```go
package main
@@ -64,7 +64,7 @@ func main() {
}
```
-## 配置说明
+## **配置说明**
embedder 可以通过 `EmbeddingConfig` 结构体进行配置:
@@ -76,26 +76,28 @@ type EmbeddingConfig struct {
}
```
-## 功能详情
+## **功能详情**
-### 自动批处理
+### **自动批处理**
embedder 会自动处理大规模文本数组的批处理。根据腾讯云 API 的限制,每个请求最多可以处理 200 个文本。embedder 会自动将较大的数组分割成适当的批次进行处理。
-### Token 使用量追踪
+### **Token 使用量追踪**
embedder 通过 Eino 的回调系统追踪 token 使用量。token 使用信息包括:
+
- 输入 token 数量
- 总 token 数量
-### 回调支持
+### **回调支持**
embedder 完全支持 Eino 的回调系统,支持:
+
- 错误追踪
- 开始/结束事件监控
- Token 使用统计
-## 更多信息
+## **更多信息**
- [腾讯云混元 API 文档](https://cloud.tencent.com/document/product/1729/102832)
-- [Eino 文档](https://github.com/cloudwego/eino)
\ No newline at end of file
+- [Eino 文档](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md
index ea88dd287de..1f13a3fce0e 100644
--- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md
+++ b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md
@@ -1,140 +1,140 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - es8
+title: Indexer - ES8
weight: 0
---
-## ES8 索引器
+## **ES8 索引器**
这是一个 [Eino](https://github.com/cloudwego/eino) 的 Elasticsearch 8.x 索引器实现,它实现了 `Indexer` 接口。这使得与 Eino 的向量存储和检索系统无缝集成,从而增强了语义搜索能力。
-## 特性
+## **特性**
- - 实现了 `github.com/cloudwego/eino/components/indexer.Indexer`
- - 易于与 Eino 的索引系统集成
- - 可配置的 Elasticsearch 参数
- - 支持向量相似度搜索
- - 批量索引操作
- - 支持自定义字段映射
- - 灵活的文档向量化
+- 实现了 `github.com/cloudwego/eino/components/indexer.Indexer`
+- 易于与 Eino 的索引系统集成
+- 可配置的 Elasticsearch 参数
+- 支持向量相似度搜索
+- 批量索引操作
+- 支持自定义字段映射
+- 灵活的文档向量化
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/indexer/es8@latest
```
-## 快速开始
+## **快速开始**
这是一个如何使用索引器的快速示例,你可以阅读 `components/indexer/es8/examples/indexer/add_documents.go` 获取更多细节:
```go
import (
- "github.com/cloudwego/eino/components/embedding"
- "github.com/cloudwego/eino/schema"
- "github.com/elastic/go-elasticsearch/v8"
- "github.com/cloudwego/eino-ext/components/indexer/es8" // 导入 es8 索引器
+ "github.com/cloudwego/eino/components/embedding"
+ "github.com/cloudwego/eino/schema"
+ "github.com/elastic/go-elasticsearch/v8"
+ "github.com/cloudwego/eino-ext/components/indexer/es8" // 导入 es8 索引器
)
const (
- indexName = "eino_example"
- fieldContent = "content"
- fieldContentVector = "content_vector"
- fieldExtraLocation = "location"
- docExtraLocation = "location"
+ indexName = "eino_example"
+ fieldContent = "content"
+ fieldContentVector = "content_vector"
+ fieldExtraLocation = "location"
+ docExtraLocation = "location"
)
func main() {
- ctx := context.Background()
-
- // es 支持多种连接方式
- username := os.Getenv("ES_USERNAME")
- password := os.Getenv("ES_PASSWORD")
- httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
-
- cert, err := os.ReadFile(httpCACertPath)
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
-
- client, err := elasticsearch.NewClient(elasticsearch.Config{
- Addresses: []string{"https://localhost:9200"},
- Username: username,
- Password: password,
- CACert: cert,
- })
- if err != nil {
- log.Panicf("connect es8 failed, err=%v", err)
- }
-
- // 创建 embedding 组件
- emb := createYourEmbedding()
-
- // 加载文档
- docs := loadYourDocs()
-
- // 创建 es 索引器组件
- indexer, err := es8.NewIndexer(ctx, &es8.IndexerConfig{
- Client: client,
- Index: indexName,
- BatchSize: 10,
- DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es8.FieldValue, err error) {
- return map[string]es8.FieldValue{
- fieldContent: {
- Value: doc.Content,
- EmbedKey: fieldContentVector, // 对文档内容进行向量化并保存向量到 "content_vector" 字段
- },
- fieldExtraLocation: {
- Value: doc.MetaData[docExtraLocation],
- },
- }, nil
- },
- Embedding: emb, // 替换为真实的 embedding 组件
- })
- if err != nil {
- log.Panicf("create indexer failed, err=%v", err)
- }
-
- ids, err := indexer.Store(ctx, docs)
- if err != nil {
- log.Panicf("create docs failed, err=%v", err)
- }
-
- fmt.Println(ids)
- // 与 Eino 系统一起使用
- // ... 配置并与 Eino 一起使用
+ ctx := context.Background()
+
+ // es 支持多种连接方式
+ username := os.Getenv("ES_USERNAME")
+ password := os.Getenv("ES_PASSWORD")
+ httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
+
+ cert, err := os.ReadFile(httpCACertPath)
+ if err != nil {
+ log.Fatalf("read file failed, err=%v", err)
+ }
+
+ client, err := elasticsearch.NewClient(elasticsearch.Config{
+ Addresses: []string{"https://localhost:9200"},
+ Username: username,
+ Password: password,
+ CACert: cert,
+ })
+ if err != nil {
+ log.Panicf("connect es8 failed, err=%v", err)
+ }
+
+ // 创建 embedding 组件
+ emb := createYourEmbedding()
+
+ // 加载文档
+ docs := loadYourDocs()
+
+ // 创建 es 索引器组件
+ indexer, err := es8.NewIndexer(ctx, &es8.IndexerConfig{
+ Client: client,
+ Index: indexName,
+ BatchSize: 10,
+ DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es8.FieldValue, err error) {
+ return map[string]es8.FieldValue{
+ fieldContent: {
+ Value: doc.Content,
+ EmbedKey: fieldContentVector, // 对文档内容进行向量化并保存向量到 "content_vector" 字段
+ },
+ fieldExtraLocation: {
+ Value: doc.MetaData[docExtraLocation],
+ },
+ }, nil
+ },
+ Embedding: emb, // 替换为真实的 embedding 组件
+ })
+ if err != nil {
+ log.Panicf("create indexer failed, err=%v", err)
+ }
+
+ ids, err := indexer.Store(ctx, docs)
+ if err != nil {
+ log.Panicf("create docs failed, err=%v", err)
+ }
+
+ fmt.Println(ids)
+ // 与 Eino 系统一起使用
+ // ... 配置并与 Eino 一起使用
}
```
-## 配置
+## **配置**
索引器可以通过 `IndexerConfig` 结构体进行配置:
```go
type IndexerConfig struct {
- Client *elasticsearch.Client // 必填:Elasticsearch 客户端实例
- Index string // 必填:存储文档的索引名称
- BatchSize int // 可选:embedding 的最大文本大小(默认:5)
-
- // 必填:将文档字段映射到 Elasticsearch 字段的函数
- DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error)
-
- // 可选:仅当需要向量化时才需要
- Embedding embedding.Embedder
+ Client *elasticsearch.Client // 必填:Elasticsearch 客户端实例
+ Index string // 必填:存储文档的索引名称
+ BatchSize int // 可选:embedding 的最大文本大小(默认:5)
+
+ // 必填:将文档字段映射到 Elasticsearch 字段的函数
+ DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error)
+
+ // 可选:仅当需要向量化时才需要
+ Embedding embedding.Embedder
}
// FieldValue 定义了字段应如何存储和向量化
type FieldValue struct {
- Value any // 要存储的原始值
- EmbedKey string // 如果设置,Value 将被向量化并保存
- Stringify func(val any) (string, error) // 可选:自定义字符串转换
+ Value any // 要存储的原始值
+ EmbedKey string // 如果设置,Value 将被向量化并保存
+ Stringify func(val any) (string, error) // 可选:自定义字符串转换
}
```
-## 更多详情
+## **更多详情**
- - [Eino 文档](https://github.com/cloudwego/eino)
- - [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch)
+- [Eino 文档](https://github.com/cloudwego/eino)
+- [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch)
diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
index 6e17b01a10c..e3f036a3d5a 100644
--- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
+++ b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus.md
@@ -1,163 +1,155 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - milvus
+title: Indexer - Milvus
weight: 0
---
-## Milvus 存储
+## **Milvus 存储**
-基于 Milvus 2.x 的向量存储实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Indexer` 接口的存储方案。该组件可无缝集成
-Eino 的向量存储和检索系统,增强语义搜索能力。
+基于 Milvus 2.x 的向量存储实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Indexer` 接口的存储方案。该组件可无缝集成 Eino 的向量存储和检索系统,增强语义搜索能力。
-## 快速开始
+## **快速开始**
-### 安装
+### **安装**
```bash
go get github.com/cloudwego/eino-ext/components/indexer/milvus
```
-### 创建 Milvus 存储
+### **创建 Milvus 存储**
```go
package main
import (
- "context"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/embedding/ark"
- "github.com/cloudwego/eino/schema"
- "github.com/milvus-io/milvus-sdk-go/v2/client"
-
- "github.com/cloudwego/eino-ext/components/indexer/milvus"
+ "context"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/embedding/ark"
+ "github.com/cloudwego/eino/schema"
+ "github.com/milvus-io/milvus-sdk-go/v2/client"
+
+ "github.com/cloudwego/eino-ext/components/indexer/milvus"
)
func main() {
- // Get the environment variables
- addr := os.Getenv("MILVUS_ADDR")
- username := os.Getenv("MILVUS_USERNAME")
- password := os.Getenv("MILVUS_PASSWORD")
- arkApiKey := os.Getenv("ARK_API_KEY")
- arkModel := os.Getenv("ARK_MODEL")
-
- // Create a client
- ctx := context.Background()
- cli, err := client.NewClient(ctx, client.Config{
- Address: addr,
- Username: username,
- Password: password,
- })
- if err != nil {
- log.Fatalf("Failed to create client: %v", err)
- return
- }
- defer cli.Close()
-
- // Create an embedding model
- emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
- APIKey: arkApiKey,
- Model: arkModel,
- })
- if err != nil {
- log.Fatalf("Failed to create embedding: %v", err)
- return
- }
-
- // Create an indexer
- indexer, err := milvus.NewIndexer(ctx, &milvus.IndexerConfig{
- Client: cli,
- Embedding: emb,
- })
- if err != nil {
- log.Fatalf("Failed to create indexer: %v", err)
- return
- }
- log.Printf("Indexer created success")
-
- // Store documents
- docs := []*schema.Document{
- {
- ID: "milvus-1",
- Content: "milvus is an open-source vector database",
- MetaData: map[string]any{
- "h1": "milvus",
- "h2": "open-source",
- "h3": "vector database",
- },
- },
- {
- ID: "milvus-2",
- Content: "milvus is a distributed vector database",
- },
- }
- ids, err := indexer.Store(ctx, docs)
- if err != nil {
- log.Fatalf("Failed to store: %v", err)
- return
- }
- log.Printf("Store success, ids: %v", ids)
+ // Get the environment variables
+ addr := os.Getenv("MILVUS_ADDR")
+ username := os.Getenv("MILVUS_USERNAME")
+ password := os.Getenv("MILVUS_PASSWORD")
+ arkApiKey := os.Getenv("ARK_API_KEY")
+ arkModel := os.Getenv("ARK_MODEL")
+
+ // Create a client
+ ctx := context.Background()
+ cli, err := client.NewClient(ctx, client.Config{
+ Address: addr,
+ Username: username,
+ Password: password,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create client: %v", err)
+ return
+ }
+ defer cli.Close()
+
+ // Create an embedding model
+ emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
+ APIKey: arkApiKey,
+ Model: arkModel,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create embedding: %v", err)
+ return
+ }
+
+ // Create an indexer
+ indexer, err := milvus.NewIndexer(ctx, &milvus.IndexerConfig{
+ Client: cli,
+ Embedding: emb,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create indexer: %v", err)
+ return
+ }
+ log.Printf("Indexer created success")
+
+ // Store documents
+ docs := []*schema.Document{
+ {
+ ID: "milvus-1",
+ Content: "milvus is an open-source vector database",
+ MetaData: map[string]any{
+ "h1": "milvus",
+ "h2": "open-source",
+ "h3": "vector database",
+ },
+ },
+ {
+ ID: "milvus-2",
+ Content: "milvus is a distributed vector database",
+ },
+ }
+ ids, err := indexer.Store(ctx, docs)
+ if err != nil {
+ log.Fatalf("Failed to store: %v", err)
+ return
+ }
+ log.Printf("Store success, ids: %v", ids)
}
```
-## Configuration
+## **Configuration**
```go
type IndexerConfig struct {
- // Client 是要调用的 milvus 客户端
- // 必需
- Client client.Client
-
- // 默认集合配置
- // Collection 是 milvus 数据库中的集合名称
- // 可选,默认值为 "eino_collection"
+ // Client 是要调用的 milvus 客户端
+ // 必需
+ Client client.Client
+
+ // 默认集合配置
+ // Collection 是 milvus 数据库中的集合名称
+ // 可选,默认值为 "eino_collection"
// 如果你想使用这个配置,必须加上Field配置,否则无法正常运行
- Collection string
- // Description 是集合的描述
- // 可选,默认值为 "the collection for eino"
- Description string
- // PartitionNum 是集合分区数量
- // 可选,默认值为 1(禁用)
- // 如果分区数量大于 1,表示使用分区,并且必须在 Fields 中有一个分区键
- PartitionNum int64
- // Fields 是集合字段
- // 可选,默认值为默认字段
- Fields []*entity.Field
- // SharedNum 是创建集合所需的 milvus 参数
- // 可选,默认值为 1
- SharedNum int32
- // ConsistencyLevel 是 milvus 集合一致性策略
- // 可选,默认级别为 ClBounded(有界一致性级别,默认容忍度为 5 秒)
- ConsistencyLevel ConsistencyLevel
- // EnableDynamicSchema 表示集合是否启用动态模式
- // 可选,默认值为 false
- // 启用动态模式可能会影响 milvus 性能
- EnableDynamicSchema bool
-
- // DocumentConverter 是将 schema.Document 转换为行数据的函数
- // 可选,默认值为 defaultDocumentConverter
- DocumentConverter func(ctx context.Context, docs []*schema.Document, vectors [][]float64) ([]interface{}, error)
-
- // 向量列的索引配置
- // MetricType 是向量的度量类型
- // 可选,默认类型为 HAMMING
- MetricType MetricType
-
- // Embedding 是从 schema.Document 的内容中嵌入值所需的向量化方法
- // 必需
- Embedding embedding.Embedder
+ Collection string
+ // Description 是集合的描述
+ // 可选,默认值为 "the collection for eino"
+ Description string
+ // PartitionNum 是集合分区数量
+ // 可选,默认值为 1(禁用)
+ // 如果分区数量大于 1,表示使用分区,并且必须在 Fields 中有一个分区键
+ PartitionNum int64
+ // Fields 是集合字段
+ // 可选,默认值为默认字段
+ Fields []*entity.Field
+ // SharedNum 是创建集合所需的 milvus 参数
+ // 可选,默认值为 1
+ SharedNum int32
+ // ConsistencyLevel 是 milvus 集合一致性策略
+ // 可选,默认级别为 ClBounded(有界一致性级别,默认容忍度为 5 秒)
+ ConsistencyLevel ConsistencyLevel
+ // EnableDynamicSchema 表示集合是否启用动态模式
+ // 可选,默认值为 false
+ // 启用动态模式可能会影响 milvus 性能
+ EnableDynamicSchema bool
+
+ // DocumentConverter 是将 schema.Document 转换为行数据的函数
+ // 可选,默认值为 defaultDocumentConverter
+ DocumentConverter func(ctx context.Context, docs []*schema.Document, vectors [][]float64) ([]interface{}, error)
+
+ // 向量列的索引配置
+ // MetricType 是向量的度量类型
+ // 可选,默认类型为 HAMMING
+ MetricType MetricType
+
+ // Embedding 是从 schema.Document 的内容中嵌入值所需的向量化方法
+ // 必需
+ Embedding embedding.Embedder
}
```
-## 默认数据模型
-
-| 字段 | 数据类型 | 字段类型 | 索引类型 | 描述 | 备注 |
-|----------|----------------|--------------|----------------------------|--------|-------------|
-| id | string | varchar | | 文章唯一标识 | 最大长度: 255 |
-| content | string | varchar | | 文章内容 | 最大长度: 1024 |
-| vector | []byte | binary array | HAMMING(default) / JACCARD | 文章内容向量 | 默认维度: 81920 |
-| metadata | map[string]any | json | | 文章元数据 | |
+## **默认数据模型**
diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md
index 636c99bb457..e94dc64e4f3 100644
--- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md
+++ b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Indexer - redis
+title: Indexer - Redis
weight: 0
---
-you can use redis-stack as indexer.
+You can use redis-stack as an indexer.
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md
index 690c0c4d51e..829adeb6d53 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md
@@ -1,76 +1,76 @@
---
Description: ""
-date: "2025-01-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - dify
+title: Retriever - Dify
weight: 0
---
-## Dify 检索器
+## **Dify 检索器**
这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Dify 检索器,实现了 `Retriever` 接口。它能够与 Eino 的检索系统无缝集成,从 Dify 知识库中检索相关文档。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/retriever.Retriever` 接口
- 易于与 Eino 的检索系统集成
- 支持可配置的检索参数
- 支持重排序功能
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/dify
```
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
+ "context"
+ "fmt"
+ "log"
+ "os"
- "github.com/cloudwego/eino-ext/components/retriever/dify"
+ "github.com/cloudwego/eino-ext/components/retriever/dify"
)
func main() {
- APIKey := os.Getenv("DIFY_DATASET_API_KEY")
- Endpoint := os.Getenv("DIFY_ENDPOINT")
- DatasetID := os.Getenv("DIFY_DATASET_ID")
- // 创建基本的 Dify Retriever
- ctx := context.Background()
-
- // 创建基本的 Dify Retriever
- ret, err := dify.NewRetriever(ctx, &dify.RetrieverConfig{
- APIKey: APIKey,
- Endpoint: Endpoint,
- DatasetID: DatasetID,
- })
- if err != nil {
- log.Fatalf("Failed to create retriever: %v", err)
- }
-
- // 执行检索
- docs, err := ret.Retrieve(ctx, "test query")
- if err != nil {
- log.Fatalf("Failed to retrieve: %v", err)
- }
-
- // 处理检索结果
- for _, doc := range docs {
- fmt.Printf("doc id: %s\n", doc.ID)
- fmt.Printf("doc content: %s\n", doc.Content)
- fmt.Printf("score: %v\n\n", doc.Score())
- }
+ APIKey := os.Getenv("DIFY_DATASET_API_KEY")
+ Endpoint := os.Getenv("DIFY_ENDPOINT")
+ DatasetID := os.Getenv("DIFY_DATASET_ID")
+ // 创建基本的 Dify Retriever
+ ctx := context.Background()
+
+ // 创建基本的 Dify Retriever
+ ret, err := dify.NewRetriever(ctx, &dify.RetrieverConfig{
+ APIKey: APIKey,
+ Endpoint: Endpoint,
+ DatasetID: DatasetID,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create retriever: %v", err)
+ }
+
+ // 执行检索
+ docs, err := ret.Retrieve(ctx, "test query")
+ if err != nil {
+ log.Fatalf("Failed to retrieve: %v", err)
+ }
+
+ // 处理检索结果
+ for _, doc := range docs {
+ fmt.Printf("doc id: %s\n", doc.ID)
+ fmt.Printf("doc content: %s\n", doc.Content)
+ fmt.Printf("score: %v\n\n", doc.Score())
+ }
}
```
-## 配置
+## **配置**
检索器可以使用 `RetrieverConfig` 结构体进行配置:
@@ -95,7 +95,7 @@ type RetrievalModel struct {
}
```
-## 文档元数据
+## **文档元数据**
检索器会为检索到的文档添加以下元数据:
@@ -111,7 +111,7 @@ docName := dify.GetOrgDocName(doc)
keywords := dify.GetKeywords(doc)
```
-## 更多详情
+## **更多详情**
- [Dify 文档](https://github.com/langgenius/dify)
-- [Eino 文档](https://github.com/cloudwego/eino)
\ No newline at end of file
+- [Eino 文档](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md
index b50102683fe..e3a02977dee 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md
@@ -1,177 +1,177 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - es8
+title: Retriever - ES8
weight: 0
---
-## ES8 检索器
+## **ES8 检索器**
这是一个 [Eino](https://github.com/cloudwego/eino) 的 Elasticsearch 8.x 检索器实现,它实现了 `Retriever` 接口。这使得与 Eino 的向量检索系统无缝集成,从而增强了语义搜索能力。
-## 特性
+## **特性**
- - 实现了 `github.com/cloudwego/eino/components/retriever.Retriever`
- - 易于与 Eino 的检索系统集成
- - 可配置的 Elasticsearch 参数
- - 支持向量相似度搜索
- - 多种搜索模式,包括近似搜索
- - 支持自定义结果解析
- - 灵活的文档过滤
+- 实现了 `github.com/cloudwego/eino/components/retriever.Retriever`
+- 易于与 Eino 的检索系统集成
+- 可配置的 Elasticsearch 参数
+- 支持向量相似度搜索
+- 多种搜索模式,包括近似搜索
+- 支持自定义结果解析
+- 灵活的文档过滤
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/es8@latest
```
-## 快速开始
+## **快速开始**
这是一个如何在近似搜索模式下使用检索器的快速示例,你可以阅读 `components/retriever/es8/examples/approximate/approximate.go` 获取更多细节:
```go
import (
- "github.com/cloudwego/eino/components/embedding"
- "github.com/cloudwego/eino/schema"
- "github.com/elastic/go-elasticsearch/v8"
- "github.com/elastic/go-elasticsearch/v8/typedapi/types"
+ "github.com/cloudwego/eino/components/embedding"
+ "github.com/cloudwego/eino/schema"
+ "github.com/elastic/go-elasticsearch/v8"
+ "github.com/elastic/go-elasticsearch/v8/typedapi/types"
- "github.com/cloudwego/eino-ext/components/retriever/es8"
- "github.com/cloudwego/eino-ext/components/retriever/es8/search_mode"
+ "github.com/cloudwego/eino-ext/components/retriever/es8"
+ "github.com/cloudwego/eino-ext/components/retriever/es8/search_mode"
)
const (
- indexName = "eino_example"
- fieldContent = "content"
- fieldContentVector = "content_vector"
- fieldExtraLocation = "location"
- docExtraLocation = "location"
+ indexName = "eino_example"
+ fieldContent = "content"
+ fieldContentVector = "content_vector"
+ fieldExtraLocation = "location"
+ docExtraLocation = "location"
)
func main() {
- ctx := context.Background()
-
- // es 支持多种连接方式
- username := os.Getenv("ES_USERNAME")
- password := os.Getenv("ES_PASSWORD")
- httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
-
- cert, err := os.ReadFile(httpCACertPath)
- if err != nil {
- log.Fatalf("read file failed, err=%v", err)
- }
-
- client, err := elasticsearch.NewClient(elasticsearch.Config{
- Addresses: []string{"https://localhost:9200"},
- Username: username,
- Password: password,
- CACert: cert,
- })
- if err != nil {
- log.Panicf("connect es8 failed, err=%v", err)
- }
-
- // 创建检索器组件
- retriever, err := es8.NewRetriever(ctx, &es8.RetrieverConfig{
- Client: client,
- Index: indexName,
- TopK: 5,
- SearchMode: search_mode.SearchModeApproximate(&search_mode.ApproximateConfig{
- QueryFieldName: fieldContent,
- VectorFieldName: fieldContentVector,
- Hybrid: true,
- // RRF 仅在特定许可证下可用
- // 参见:https://www.elastic.co/subscriptions
- RRF: false,
- RRFRankConstant: nil,
- RRFWindowSize: nil,
- }),
- ResultParser: func(ctx context.Context, hit types.Hit) (doc *schema.Document, err error) {
- doc = &schema.Document{
- ID: *hit.Id_,
- Content: "",
- MetaData: map[string]any{},
- }
-
- var src map[string]any
- if err = json.Unmarshal(hit.Source_, &src); err != nil {
- return nil, err
- }
-
- for field, val := range src {
- switch field {
- case fieldContent:
- doc.Content = val.(string)
- case fieldContentVector:
- var v []float64
- for _, item := range val.([]interface{}) {
- v = append(v, item.(float64))
- }
- doc.WithDenseVector(v)
- case fieldExtraLocation:
- doc.MetaData[docExtraLocation] = val.(string)
- }
- }
-
- if hit.Score_ != nil {
- doc.WithScore(float64(*hit.Score_))
- }
-
- return doc, nil
- },
- Embedding: emb, // 你的 embedding 组件
- })
- if err != nil {
- log.Panicf("create retriever failed, err=%v", err)
- }
-
- // 无过滤条件搜索
- docs, err := retriever.Retrieve(ctx, "tourist attraction")
- if err != nil {
- log.Panicf("retrieve docs failed, err=%v", err)
- }
-
- // 带过滤条件搜索
- docs, err = retriever.Retrieve(ctx, "tourist attraction",
- es8.WithFilters([]types.Query{{
- Term: map[string]types.TermQuery{
- fieldExtraLocation: {
- CaseInsensitive: of(true),
- Value: "China",
- },
- },
- }}),
- )
- if err != nil {
- log.Panicf("retrieve docs failed, err=%v", err)
- }
+ ctx := context.Background()
+
+ // es 支持多种连接方式
+ username := os.Getenv("ES_USERNAME")
+ password := os.Getenv("ES_PASSWORD")
+ httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH")
+
+ cert, err := os.ReadFile(httpCACertPath)
+ if err != nil {
+ log.Fatalf("read file failed, err=%v", err)
+ }
+
+ client, err := elasticsearch.NewClient(elasticsearch.Config{
+ Addresses: []string{"https://localhost:9200"},
+ Username: username,
+ Password: password,
+ CACert: cert,
+ })
+ if err != nil {
+ log.Panicf("connect es8 failed, err=%v", err)
+ }
+
+ // 创建检索器组件
+ retriever, err := es8.NewRetriever(ctx, &es8.RetrieverConfig{
+ Client: client,
+ Index: indexName,
+ TopK: 5,
+ SearchMode: search_mode.SearchModeApproximate(&search_mode.ApproximateConfig{
+ QueryFieldName: fieldContent,
+ VectorFieldName: fieldContentVector,
+ Hybrid: true,
+ // RRF 仅在特定许可证下可用
+ // 参见:https://www.elastic.co/subscriptions
+ RRF: false,
+ RRFRankConstant: nil,
+ RRFWindowSize: nil,
+ }),
+ ResultParser: func(ctx context.Context, hit types.Hit) (doc *schema.Document, err error) {
+ doc = &schema.Document{
+ ID: *hit.Id_,
+ Content: "",
+ MetaData: map[string]any{},
+ }
+
+ var src map[string]any
+ if err = json.Unmarshal(hit.Source_, &src); err != nil {
+ return nil, err
+ }
+
+ for field, val := range src {
+ switch field {
+ case fieldContent:
+ doc.Content = val.(string)
+ case fieldContentVector:
+ var v []float64
+ for _, item := range val.([]interface{}) {
+ v = append(v, item.(float64))
+ }
+ doc.WithDenseVector(v)
+ case fieldExtraLocation:
+ doc.MetaData[docExtraLocation] = val.(string)
+ }
+ }
+
+ if hit.Score_ != nil {
+ doc.WithScore(float64(*hit.Score_))
+ }
+
+ return doc, nil
+ },
+ Embedding: emb, // 你的 embedding 组件
+ })
+ if err != nil {
+ log.Panicf("create retriever failed, err=%v", err)
+ }
+
+ // 无过滤条件搜索
+ docs, err := retriever.Retrieve(ctx, "tourist attraction")
+ if err != nil {
+ log.Panicf("retrieve docs failed, err=%v", err)
+ }
+
+ // 带过滤条件搜索
+ docs, err = retriever.Retrieve(ctx, "tourist attraction",
+ es8.WithFilters([]types.Query{{
+ Term: map[string]types.TermQuery{
+ fieldExtraLocation: {
+ CaseInsensitive: of(true),
+ Value: "China",
+ },
+ },
+ }}),
+ )
+ if err != nil {
+ log.Panicf("retrieve docs failed, err=%v", err)
+ }
}
```
-## 配置
+## **配置**
检索器可以通过 `RetrieverConfig` 结构体进行配置:
```go
type RetrieverConfig struct {
- Client *elasticsearch.Client // 必填:Elasticsearch 客户端实例
- Index string // 必填:要从中检索文档的索引名称
- TopK int // 必填:要返回的结果数量
-
- // 必填:搜索模式配置
- SearchMode search_mode.SearchMode
-
- // 必填:将 Elasticsearch 命中解析为 Document 的函数
- ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error)
-
- // 可选:仅当需要查询向量化时才需要
- Embedding embedding.Embedder
+ Client *elasticsearch.Client // 必填:Elasticsearch 客户端实例
+ Index string // 必填:要从中检索文档的索引名称
+ TopK int // 必填:要返回的结果数量
+
+ // 必填:搜索模式配置
+ SearchMode search_mode.SearchMode
+
+ // 必填:将 Elasticsearch 命中解析为 Document 的函数
+ ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error)
+
+ // 可选:仅当需要查询向量化时才需要
+ Embedding embedding.Embedder
}
```
-## 更多详情
+## **更多详情**
- - [Eino 文档](https://github.com/cloudwego/eino)
- - [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch)
+- [Eino 文档](https://github.com/cloudwego/eino)
+- [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch)
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
index 499c4367466..41b368dd7d8 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus.md
@@ -1,110 +1,109 @@
---
Description: ""
-date: "2025-01-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - milvus
+title: Retriever - Milvus
weight: 0
---
-## Milvus 搜索
+## **Milvus 搜索**
-基于 Milvus 2.x 的向量搜索实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Retriever` 接口的存储方案。该组件可无缝集成
-Eino 的向量存储和检索系统,增强语义搜索能力。
+基于 Milvus 2.x 的向量搜索实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Retriever` 接口的存储方案。该组件可无缝集成 Eino 的向量存储和检索系统,增强语义搜索能力。
-## 快速开始
+## **快速开始**
-### 安装
+### **安装**
```bash
go get github.com/cloudwego/eino-ext/components/retriever/milvus
```
-### 创建 Milvus 搜索
+### **创建 Milvus 搜索**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
-
- "github.com/cloudwego/eino-ext/components/embedding/ark"
- "github.com/milvus-io/milvus-sdk-go/v2/client"
-
- "github.com/cloudwego/eino-ext/components/retriever/milvus"
+ "context"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/cloudwego/eino-ext/components/embedding/ark"
+ "github.com/milvus-io/milvus-sdk-go/v2/client"
+
+ "github.com/cloudwego/eino-ext/components/retriever/milvus"
)
func main() {
- // Get the environment variables
- addr := os.Getenv("MILVUS_ADDR")
- username := os.Getenv("MILVUS_USERNAME")
- password := os.Getenv("MILVUS_PASSWORD")
- arkApiKey := os.Getenv("ARK_API_KEY")
- arkModel := os.Getenv("ARK_MODEL")
-
- // Create a client
- ctx := context.Background()
- cli, err := client.NewClient(ctx, client.Config{
- Address: addr,
- Username: username,
- Password: password,
- })
- if err != nil {
- log.Fatalf("Failed to create client: %v", err)
- return
- }
- defer cli.Close()
-
- // Create an embedding model
- emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
- APIKey: arkApiKey,
- Model: arkModel,
- })
-
- // Create a retriever
- retriever, err := milvus.NewRetriever(ctx, &milvus.RetrieverConfig{
- Client: cli,
- Collection: "",
- Partition: nil,
- VectorField: "",
- OutputFields: []string{
- "id",
- "content",
- "metadata",
- },
- DocumentConverter: nil,
- MetricType: "",
- TopK: 0,
- ScoreThreshold: 5,
- Sp: nil,
- Embedding: emb,
- })
- if err != nil {
- log.Fatalf("Failed to create retriever: %v", err)
- return
- }
-
- // Retrieve documents
- documents, err := retriever.Retrieve(ctx, "milvus")
- if err != nil {
- log.Fatalf("Failed to retrieve: %v", err)
- return
- }
-
- // Print the documents
- for i, doc := range documents {
- fmt.Printf("Document %d:\n", i)
- fmt.Printf("title: %s\n", doc.ID)
- fmt.Printf("content: %s\n", doc.Content)
- fmt.Printf("metadata: %v\n", doc.MetaData)
- }
+ // Get the environment variables
+ addr := os.Getenv("MILVUS_ADDR")
+ username := os.Getenv("MILVUS_USERNAME")
+ password := os.Getenv("MILVUS_PASSWORD")
+ arkApiKey := os.Getenv("ARK_API_KEY")
+ arkModel := os.Getenv("ARK_MODEL")
+
+ // Create a client
+ ctx := context.Background()
+ cli, err := client.NewClient(ctx, client.Config{
+ Address: addr,
+ Username: username,
+ Password: password,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create client: %v", err)
+ return
+ }
+ defer cli.Close()
+
+ // Create an embedding model
+ emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{
+ APIKey: arkApiKey,
+ Model: arkModel,
+ })
+
+ // Create a retriever
+ retriever, err := milvus.NewRetriever(ctx, &milvus.RetrieverConfig{
+ Client: cli,
+ Collection: "",
+ Partition: nil,
+ VectorField: "",
+ OutputFields: []string{
+ "id",
+ "content",
+ "metadata",
+ },
+ DocumentConverter: nil,
+ MetricType: "",
+ TopK: 0,
+ ScoreThreshold: 5,
+ Sp: nil,
+ Embedding: emb,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create retriever: %v", err)
+ return
+ }
+
+ // Retrieve documents
+ documents, err := retriever.Retrieve(ctx, "milvus")
+ if err != nil {
+ log.Fatalf("Failed to retrieve: %v", err)
+ return
+ }
+
+ // Print the documents
+ for i, doc := range documents {
+ fmt.Printf("Document %d:\n", i)
+ fmt.Printf("title: %s\n", doc.ID)
+ fmt.Printf("content: %s\n", doc.Content)
+ fmt.Printf("metadata: %v\n", doc.MetaData)
+ }
}
```
-## 配置
+## **配置**
```go
type RetrieverConfig struct {
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md
index 387a834c092..06ffc38894b 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md
@@ -1,9 +1,9 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - redis
+title: Retriever - Redis
weight: 0
---
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
index efe10a0051e..f418f566ddc 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-02-11"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Retriever - volc knowledge
+title: Retriever - volc Knowledge
weight: 0
---
-you can use volc knowledge as retriever.
+You can use volc knowledge as a retriever.
diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
index b6d3f719dcf..ac0fdd5a916 100644
--- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
+++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md
@@ -18,7 +18,7 @@ weight: 0
火山引擎 VikingDB 检索器通过 `NewRetriever` 函数进行初始化,主要配置参数如下:
```go
-import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
+import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb"
retriever, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{
// 服务配置
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/_index.md b/content/zh/docs/eino/ecosystem_integration/tool/_index.md
index d3d36ec0c16..4e31e926967 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/_index.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-07-21"
lastmod: ""
tags: []
title: Tool
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
index c392add991a..02d7638ec33 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md
@@ -1,63 +1,63 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
title: Tool - Bingsearch
weight: 0
---
-## Bing Search Tool
+## **Bing Search Tool**
这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Bing 搜索工具。该工具实现了 `InvokableTool` 接口,可以与 Eino 的 ChatModel 交互系统和 `ToolsNode` 无缝集成。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/tool.InvokableTool` 接口
- 易于与 Eino 工具系统集成
- 可配置的搜索参数
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/tool/bingsearch
```
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "log"
- "os"
- "time"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/tool/bingsearch"
+ "context"
+ "log"
+ "os"
+ "time"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/tool/bingsearch"
)
func main() {
- // 设置 Bing Search API 密钥
- bingSearchAPIKey := os.Getenv("BING_SEARCH_API_KEY")
-
- // 创建上下文
- ctx := context.Background()
-
- // 创建 Bing Search 工具
- bingSearchTool, err := bingsearch.NewTool(ctx, &bingsearch.Config{
- APIKey: bingSearchAPIKey,
- Cache: 5 * time.Minute,
- })
-
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
+ // 设置 Bing Search API 密钥
+ bingSearchAPIKey := os.Getenv("BING_SEARCH_API_KEY")
+
+ // 创建上下文
+ ctx := context.Background()
+
+ // 创建 Bing Search 工具
+ bingSearchTool, err := bingsearch.NewTool(ctx, &bingsearch.Config{
+ APIKey: bingSearchAPIKey,
+ Cache: 5 * time.Minute,
+ })
+
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
// ... 配置并使用 ToolsNode
```
-## 配置
+## **配置**
工具可以通过 `Config` 结构体进行配置:
@@ -83,9 +83,9 @@ type Config struct {
}
```
-## Search
+## **Search**
-### 请求 Schema
+### **请求 Schema**
```go
type SearchRequest struct {
@@ -94,7 +94,7 @@ type SearchRequest struct {
}
```
-### 响应 Schema
+### **响应 Schema**
```go
type SearchResponse struct {
@@ -108,7 +108,7 @@ type searchResult struct {
}
```
-## 更多详情
+## **更多详情**
-- [DuckDuckGo 搜索库文档](tool_duckduckgo_search/)
+- DuckDuckGo 搜索库文档
- [Eino 文档](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_browseruse.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_browseruse.md
index 6b6a0da7183..e4527d0d31f 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_browseruse.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_browseruse.md
@@ -1,30 +1,31 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Tool - Browseruse
+title: Tool - BrowserUse
weight: 0
---
-## BrowserUse Tool
+## **BrowserUse Tool**
A BrowserUse Tool implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Tool` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+
> **Note**: This implementation is inspired by and references the [OpenManus](https://github.com/mannaandpoem/OpenManus) project.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.BaseTool`
- Easy integration with Eino's tool system
- Support for executing browser actions
-## Installation
+## **Install****ation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/browseruse@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the browser-use tool:
@@ -32,36 +33,35 @@ Here's a quick example of how to use the browser-use tool:
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/cloudwego/eino-ext/components/tool/browseruse"
+ "github.com/cloudwego/eino-ext/components/tool/browseruse"
)
func main() {
- ctx := context.Background()
- but, err := browseruse.NewBrowserUseTool(ctx, &browseruse.Config{})
- if err != nil {
- log.Fatal(err)
- }
-
- url := "https://www.google.com"
- result, err := but.Execute(&browseruse.Param{
- Action: browseruse.ActionGoToURL,
- URL: &url,
- })
- if err != nil {
- log.Fatal(err)
- }
- fmt.Println(result)
- time.Sleep(10 * time.Second)
- but.Cleanup()
+ ctx := context.Background()
+ but, err := browseruse.NewBrowserUseTool(ctx, &browseruse.Config{})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ url := "https://www.google.com"
+ result, err := but.Execute(&browseruse.Param{
+ Action: browseruse.ActionGoToURL,
+ URL: &url,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(result)
+ time.Sleep(10 * time.Second)
+ but.Cleanup()
}
-
```
-## For More Details
+## **For More Details**
- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_commandline.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_commandline.md
index 529623d21e5..57cdb296c9a 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_commandline.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_commandline.md
@@ -1,95 +1,99 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Tool - Commandline
+title: Tool - CommandLine
weight: 0
---
-## CommandLine Tool
+## **CommandLine Tool**
CommandLine Tools implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Tool` interface. This enables seamless integration with Eino's LLM capabilities for enhanced natural language processing and generation.
+
> **Note**: This implementation is inspired by and references the [OpenManus](https://github.com/mannaandpoem/OpenManus) project.
-## Features
+## **Features**
- Implements `github.com/cloudwego/eino/components/tool.InvokableTool`
- Easy integration with Eino's tool system
- Support executing command-line instructions in Docker containers
-## Installation
+## **Install****ation**
```bash
go get github.com/cloudwego/eino-ext/components/tool/commandline@latest
```
-## Quick Start
+## **Quick Start**
Here's a quick example of how to use the commandline tool:
Editor:
+
```go
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- "github.com/cloudwego/eino-ext/components/tool/commandline"
- "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
+ "github.com/cloudwego/eino-ext/components/tool/commandline"
+ "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
)
func main() {
- ctx := context.Background()
-
- op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
- if err != nil {
- log.Fatal(err)
- }
- // you should ensure that docker has been started before create a docker container
- err = op.Create(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer op.Cleanup(ctx)
-
- sre, err := commandline.NewStrReplaceEditor(ctx, &commandline.Config{Operator: op})
- if err != nil {
- log.Fatal(err)
- }
-
- info, err := sre.Info(ctx)
- if err != nil {
- log.Fatal(err)
- }
- log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
-
- content := "hello world"
-
- log.Println("create file[test.txt]...")
- result, err := sre.Execute(ctx, &commandline.StrReplaceEditorParams{
- Command: commandline.CreateCommand,
- Path: "./test.txt",
- FileText: &content,
- })
- if err != nil {
- log.Fatal(err)
- }
- log.Println("create file result: ", result)
-
- log.Println("view file[test.txt]...")
- result, err = sre.Execute(ctx, &commandline.StrReplaceEditorParams{
- Command: commandline.ViewCommand,
- Path: "./test.txt",
- })
- if err != nil {
- log.Fatal(err)
- }
- log.Println("view file result: ", result)
+ ctx := context.Background()
+
+ op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
+ if err != nil {
+ log.Fatal(err)
+ }
+ // you should ensure that docker has been started before create a docker container
+ err = op.Create(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer op.Cleanup(ctx)
+
+ sre, err := commandline.NewStrReplaceEditor(ctx, &commandline.Config{Operator: op})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ info, err := sre.Info(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
+
+ content := "hello world"
+
+ log.Println("create file[test.txt]...")
+ result, err := sre.Execute(ctx, &commandline.StrReplaceEditorParams{
+ Command: commandline.CreateCommand,
+ Path: "./test.txt",
+ FileText: &content,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Println("create file result: ", result)
+
+ log.Println("view file[test.txt]...")
+ result, err = sre.Execute(ctx, &commandline.StrReplaceEditorParams{
+ Command: commandline.ViewCommand,
+ Path: "./test.txt",
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Println("view file result: ", result)
}
```
+
PyExecutor:
+
```go
/*
* Copyright 2025 CloudWeGo Authors
@@ -110,48 +114,47 @@ PyExecutor:
package main
import (
- "context"
- "log"
+ "context"
+ "log"
- "github.com/cloudwego/eino-ext/components/tool/commandline"
- "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
+ "github.com/cloudwego/eino-ext/components/tool/commandline"
+ "github.com/cloudwego/eino-ext/components/tool/commandline/sandbox"
)
func main() {
- ctx := context.Background()
- op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
- if err != nil {
- log.Fatal(err)
- }
- // you should ensure that docker has been started before create a docker container
- err = op.Create(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer op.Cleanup(ctx)
-
- exec, err := commandline.NewPyExecutor(ctx, &commandline.PyExecutorConfig{Operator: op}) // use python3 by default
- if err != nil {
- log.Fatal(err)
- }
-
- info, err := exec.Info(ctx)
- if err != nil {
- log.Fatal(err)
- }
- log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
-
- code := "print(\"hello world\")"
- log.Printf("execute code:\n%s", code)
- result, err := exec.Execute(ctx, &commandline.Input{Code: code})
- if err != nil {
- log.Fatal(err)
- }
- log.Println("result:\n", result)
+ ctx := context.Background()
+ op, err := sandbox.NewDockerSandbox(ctx, &sandbox.Config{})
+ if err != nil {
+ log.Fatal(err)
+ }
+ // you should ensure that docker has been started before create a docker container
+ err = op.Create(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer op.Cleanup(ctx)
+
+ exec, err := commandline.NewPyExecutor(ctx, &commandline.PyExecutorConfig{Operator: op}) // use python3 by default
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ info, err := exec.Info(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Printf("tool name: %s, tool desc: %s", info.Name, info.Desc)
+
+ code := "print(\"hello world\")"
+ log.Printf("execute code:\n%s", code)
+ result, err := exec.Execute(ctx, &commandline.Input{Code: code})
+ if err != nil {
+ log.Fatal(err)
+ }
+ log.Println("result:\n", result)
}
```
+## **For More Details**
-## For More Details
-
-- [Eino Documentation](https://github.com/cloudwego/eino)
\ No newline at end of file
+- [Eino Documentation](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
index b6442337df9..6be045c0c70 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-11-21"
+date: "2025-12-01"
lastmod: ""
tags: []
title: Tool - DuckDuckGoSearch
@@ -23,7 +23,7 @@ import "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2"
tool, err := duckduckgo.NewTextSearchTool(ctx, &duckduckgo.Config{
ToolName: "duckduckgo_search",
ToolDesc: "search for information by duckduckgo",
- Region: duckduckgo.RegionWT, // The geographical region for results.
+ Region: ddgsearch.RegionWT, // The geographical region for results.
MaxResults: 3, // Limit the number of results returned.
})
```
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
index 1be6b7ad9ae..07619d08ec5 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-11-20"
lastmod: ""
tags: []
title: Tool - Googlesearch
@@ -31,6 +31,8 @@ tool, err := googlesearch.NewTool(ctx, &googlesearch.Config{
})
```
+注:Google Search 的 APIKey、SearchEngineID,请到 Google 搜索引擎官网申请:[https://programmablesearchengine.google.com/controlpanel/all](https://programmablesearchengine.google.com/controlpanel/all)
+
### **搜索参数**
搜索请求支持以下参数:
@@ -82,7 +84,7 @@ func main() {
// prepare params
req := googlesearch.SearchRequest{
- Query: "Go concurrent programming",
+ Query: "Golang concurrent programming",
Num: 3,
Lang: "en",
}
@@ -123,7 +125,7 @@ func main() {
// 2. Title: Concurrency — An Introduction to Programming in Go | Go Resources
// Link: https://www.golang-book.com/books/intro/10
// Desc:
- // 3. Title: The Comprehensive Guide to Concurrency in Go | by Brandon ...
+ // 3. Title: The Comprehensive Guide to Concurrency in Golang | by Brandon ...
// Link: https://bwoff.medium.com/the-comprehensive-guide-to-concurrency-in-golang-aaa99f8bccf6
// Desc: Update (November 20, 2023) — This article has undergone a comprehensive revision for enhanced clarity and conciseness. I’ve streamlined the…
@@ -135,7 +137,7 @@ func main() {
```json
{
- "query": "Go concurrent programming",
+ "query": "Golang concurrent programming",
"items": [
{
"link": "https://example.com/article1",
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md
index 50d5630e724..dfa26dfc309 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md
@@ -1,24 +1,24 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Tool - httprequest
+title: Tool - HTTPRequest
weight: 0
---
-## HTTP 请求工具
+## **HTTP 请求工具**
一组为 [Eino](https://github.com/cloudwego/eino) 实现的 HTTP 请求工具,遵循 `InvokableTool` 接口。这些工具允许你轻松执行 GET、POST、PUT 和 DELETE 请求,并可与 Eino 的聊天模型交互系统及 `ToolsNode` 集成,实现更强大的功能。
-## 功能
+## **功能**
- 实现了 `github.com/cloudwego/eino/components/tool.InvokableTool` 接口
- 支持 GET、POST、PUT 和 DELETE 请求
- 可配置请求头和 HttpClient
- 可与 Eino 工具系统简单集成
-## 安装
+## **安装**
使用 `go get` 安装该包(请根据你的项目结构调整模块路径):
@@ -26,135 +26,134 @@ weight: 0
go get github.com/cloudwego/eino-ext/components/tool/httprequest
```
-## 快速开始
+## **快速开始**
下面分别展示了如何单独使用 GET 和 POST 工具的示例。
-### GET 请求示例
+### **GET 请求示例**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/bytedance/sonic"
- req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
+ "github.com/bytedance/sonic"
+ req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
)
func main() {
- // Configure the GET tool
- config := &req.Config{
- // Headers is optional
- Headers: map[string]string{
- "User-Agent": "MyCustomAgent",
- },
- // HttpClient is optional
- HttpClient: &http.Client{
- Timeout: 30 * time.Second,
- Transport: &http.Transport{},
- },
- }
-
- ctx := context.Background()
-
- // Create the GET tool
- tool, err := req.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
-
- // Prepare the GET request payload
- request := &req.GetRequest{
- URL: "https://jsonplaceholder.typicode.com/posts",
- }
-
- jsonReq, err := sonic.Marshal(request)
- if err != nil {
- log.Fatalf("Error marshaling JSON: %v", err)
- }
-
- // Execute the GET request using the InvokableTool interface
- resp, err := tool.InvokableRun(ctx, string(jsonReq))
- if err != nil {
- log.Fatalf("GET request failed: %v", err)
- }
-
- fmt.Println(resp)
+ // Configure the GET tool
+ config := &req.Config{
+ // Headers is optional
+ Headers: map[string]string{
+ "User-Agent": "MyCustomAgent",
+ },
+ // HttpClient is optional
+ HttpClient: &http.Client{
+ Timeout: 30 * time.Second,
+ Transport: &http.Transport{},
+ },
+ }
+
+ ctx := context.Background()
+
+ // Create the GET tool
+ tool, err := req.NewTool(ctx, config)
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
+
+ // Prepare the GET request payload
+ request := &req.GetRequest{
+ URL: "https://jsonplaceholder.typicode.com/posts",
+ }
+
+ jsonReq, err := sonic.Marshal(request)
+ if err != nil {
+ log.Fatalf("Error marshaling JSON: %v", err)
+ }
+
+ // Execute the GET request using the InvokableTool interface
+ resp, err := tool.InvokableRun(ctx, string(jsonReq))
+ if err != nil {
+ log.Fatalf("GET request failed: %v", err)
+ }
+
+ fmt.Println(resp)
}
```
-### POST 请求示例
+### **POST 请求示例**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "time"
+ "context"
+ "fmt"
+ "log"
+ "time"
- "github.com/bytedance/sonic"
- post "github.com/cloudwego/eino-ext/components/tool/httprequest/post"
+ "github.com/bytedance/sonic"
+ post "github.com/cloudwego/eino-ext/components/tool/httprequest/post"
)
func main() {
- config := &post.Config{}
+ config := &post.Config{}
- ctx := context.Background()
+ ctx := context.Background()
- tool, err := post.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create tool: %v", err)
- }
+ tool, err := post.NewTool(ctx, config)
+ if err != nil {
+ log.Fatalf("Failed to create tool: %v", err)
+ }
- request := &post.PostRequest{
- URL: "https://jsonplaceholder.typicode.com/posts",
- Body: `{"title": "my title","body": "my body","userId": 1}`,
- }
+ request := &post.PostRequest{
+ URL: "https://jsonplaceholder.typicode.com/posts",
+ Body: `{"title": "my title","body": "my body","userId": 1}`,
+ }
- jsonReq, err := sonic.Marshal(request)
+ jsonReq, err := sonic.Marshal(request)
- if err != nil {
- log.Fatalf("Error marshaling JSON: %v", err)
- }
+ if err != nil {
+ log.Fatalf("Error marshaling JSON: %v", err)
+ }
- resp, err := tool.InvokableRun(ctx, string(jsonReq))
- if err != nil {
- log.Fatalf("Post failed: %v", err)
- }
+ resp, err := tool.InvokableRun(ctx, string(jsonReq))
+ if err != nil {
+ log.Fatalf("Post failed: %v", err)
+ }
- fmt.Println(resp)
+ fmt.Println(resp)
}
-
```
-## 配置
+## **配置**
GET、POST、PUT 和 DELETE 工具共享类似的配置参数,这些参数在各自的 `Config` 结构体中定义。例如:
```go
// Config 表示 HTTP 请求工具的通用配置。
type Config struct {
- // 灵感来源于 LangChain 项目的 "Requests" 工具,特别是 RequestsGetTool。
- // 详情请见:https://python.langchain.com/docs/integrations/tools/requests/
- // 可选。默认值: "request_get"。
- ToolName string `json:"tool_name"`
- // 可选。默认值: "A portal to the internet. Use this tool when you need to fetch specific content from a website.
- // Input should be a URL (e.g., https://www.google.com). The output will be the text response from the GET request."
- ToolDesc string `json:"tool_desc"`
-
- // Headers 是 HTTP 头部名称与对应值的映射。
- // 这些头部会包含在工具发起的每个请求中。
- Headers map[string]string `json:"headers"`
-
- // HttpClient 用于执行请求的 HTTP 客户端。
- // 如果未提供,将初始化并使用一个默认的 30 秒超时和标准传输的客户端。
- HttpClient *http.Client
+ // 灵感来源于 LangChain 项目的 "Requests" 工具,特别是 RequestsGetTool。
+ // 详情请见:https://python.langchain.com/docs/integrations/tools/requests/
+ // 可选。默认值: "request_get"。
+ ToolName string `json:"tool_name"`
+ // 可选。默认值: "A portal to the internet. Use this tool when you need to fetch specific content from a website.
+ // Input should be a URL (e.g., https://www.google.com). The output will be the text response from the GET request."
+ ToolDesc string `json:"tool_desc"`
+
+ // Headers 是 HTTP 头部名称与对应值的映射。
+ // 这些头部会包含在工具发起的每个请求中。
+ Headers map[string]string `json:"headers"`
+
+ // HttpClient 用于执行请求的 HTTP 客户端。
+ // 如果未提供,将初始化并使用一个默认的 30 秒超时和标准传输的客户端。
+ HttpClient *http.Client
}
```
@@ -162,7 +161,7 @@ type Config struct {
```go
type GetRequest struct {
- URL string `json:"url" jsonschema_description:"要执行 GET 请求的 URL"`
+ URL string `json:"url" jsonschema_description:"要执行 GET 请求的 URL"`
}
```
@@ -170,102 +169,102 @@ type GetRequest struct {
```go
type PostRequest struct {
- URL string `json:"url" jsonschema_description:"要执行 POST 请求的 URL"`
- Body string `json:"body" jsonschema_description:"POST 请求要发送的请求体"`
+ URL string `json:"url" jsonschema_description:"要执行 POST 请求的 URL"`
+ Body string `json:"body" jsonschema_description:"POST 请求要发送的请求体"`
}
```
-## 与 agent 集成示例
+## **与 agent 集成示例**
```go
package main
import (
- "context"
- "fmt"
- "log"
- "os"
- "time"
-
- "github.com/bytedance/sonic"
- "github.com/cloudwego/eino-ext/components/model/openai"
- req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
- "github.com/cloudwego/eino/components/tool"
- "github.com/cloudwego/eino/compose"
- "github.com/cloudwego/eino/schema"
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/bytedance/sonic"
+ "github.com/cloudwego/eino-ext/components/model/openai"
+ req "github.com/cloudwego/eino-ext/components/tool/httprequest/get"
+ "github.com/cloudwego/eino/components/tool"
+ "github.com/cloudwego/eino/compose"
+ "github.com/cloudwego/eino/schema"
)
// float32Ptr is a helper to return a pointer for a float32 value.
func float32Ptr(f float32) *float32 {
- return &f
+ return &f
}
func main() {
- // Load OpenAI API key from environment variables.
- openAIAPIKey := os.Getenv("OPENAI_API_KEY")
- if openAIAPIKey == "" {
- log.Fatal("OPENAI_API_KEY not set")
- }
-
- ctx := context.Background()
-
- // Setup GET tool configuration.
- config := &req.Config{
- Headers: map[string]string{
- "User-Agent": "MyCustomAgent",
- },
- }
-
- // Instantiate the GET tool.
- getTool, err := req.NewTool(ctx, config)
- if err != nil {
- log.Fatalf("Failed to create GET tool: %v", err)
- }
-
- // Retrieve the tool info to bind it to the ChatModel.
- toolInfo, err := getTool.Info(ctx)
- if err != nil {
- log.Fatalf("Failed to get tool info: %v", err)
- }
-
- // Create the ChatModel using OpenAI.
- chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
- Model: "gpt-4o", // or another supported model
- APIKey: openAIAPIKey,
- Temperature: float32Ptr(0.7),
- })
- if err != nil {
- log.Fatalf("Failed to create ChatModel: %v", err)
- }
-
- // Bind the tool to the ChatModel.
- err = chatModel.BindTools([]*schema.ToolInfo{toolInfo})
- if err != nil {
- log.Fatalf("Failed to bind tool to ChatModel: %v", err)
- }
-
- // Create the Tools node with the GET tool.
- toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
- Tools: []tool.BaseTool{getTool},
- })
- if err != nil {
- log.Fatalf("Failed to create ToolNode: %v", err)
- }
-
- // Build the chain with the ChatModel and the Tools node.
- chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
- chain.
- AppendChatModel(chatModel, compose.WithNodeName("chat_model")).
- AppendToolsNode(toolsNode, compose.WithNodeName("tools"))
-
- // Compile the chain to obtain the agent.
- agent, err := chain.Compile(ctx)
- if err != nil {
- log.Fatalf("Failed to compile chain: %v", err)
- }
-
- // Define the API specification (api_spec) in OpenAPI (YAML) format.
- apiSpec := `
+ // Load OpenAI API key from environment variables.
+ openAIAPIKey := os.Getenv("OPENAI_API_KEY")
+ if openAIAPIKey == "" {
+ log.Fatal("OPENAI_API_KEY not set")
+ }
+
+ ctx := context.Background()
+
+ // Setup GET tool configuration.
+ config := &req.Config{
+ Headers: map[string]string{
+ "User-Agent": "MyCustomAgent",
+ },
+ }
+
+ // Instantiate the GET tool.
+ getTool, err := req.NewTool(ctx, config)
+ if err != nil {
+ log.Fatalf("Failed to create GET tool: %v", err)
+ }
+
+ // Retrieve the tool info to bind it to the ChatModel.
+ toolInfo, err := getTool.Info(ctx)
+ if err != nil {
+ log.Fatalf("Failed to get tool info: %v", err)
+ }
+
+ // Create the ChatModel using OpenAI.
+ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
+ Model: "gpt-4o", // or another supported model
+ APIKey: openAIAPIKey,
+ Temperature: float32Ptr(0.7),
+ })
+ if err != nil {
+ log.Fatalf("Failed to create ChatModel: %v", err)
+ }
+
+ // Bind the tool to the ChatModel.
+ err = chatModel.BindTools([]*schema.ToolInfo{toolInfo})
+ if err != nil {
+ log.Fatalf("Failed to bind tool to ChatModel: %v", err)
+ }
+
+ // Create the Tools node with the GET tool.
+ toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
+ Tools: []tool.BaseTool{getTool},
+ })
+ if err != nil {
+ log.Fatalf("Failed to create ToolNode: %v", err)
+ }
+
+ // Build the chain with the ChatModel and the Tools node.
+ chain := compose.NewChain[[]*schema.Message, []*schema.Message]()
+ chain.
+ AppendChatModel(chatModel, compose.WithNodeName("chat_model")).
+ AppendToolsNode(toolsNode, compose.WithNodeName("tools"))
+
+ // Compile the chain to obtain the agent.
+ agent, err := chain.Compile(ctx)
+ if err != nil {
+ log.Fatalf("Failed to compile chain: %v", err)
+ }
+
+ // Define the API specification (api_spec) in OpenAPI (YAML) format.
+ apiSpec := `
openapi: "3.0.0"
info:
title: JSONPlaceholder API
@@ -335,37 +334,38 @@ paths:
type: string
`
- // Create a system message that includes the API documentation.
- systemMessage := fmt.Sprintf(`You have access to an API to help answer user queries.
+ // Create a system message that includes the API documentation.
+ systemMessage := fmt.Sprintf(`You have access to an API to help answer user queries.
Here is documentation on the API:
%s`, apiSpec)
- // Define initial messages (system and user).
- messages := []*schema.Message{
- {
- Role: schema.System,
- Content: systemMessage,
- },
- {
- Role: schema.User,
- Content: "Fetch the top two posts. What are their titles?",
- },
- }
-
- // Invoke the agent with the messages.
- resp, err := agent.Invoke(ctx, messages)
- if err != nil {
- log.Fatalf("Failed to invoke agent: %v", err)
- }
-
- // Output the response messages.
- for idx, msg := range resp {
- fmt.Printf("Message %d: %s: %s\n", idx, msg.Role, msg.Content)
- }
+ // Define initial messages (system and user).
+ messages := []*schema.Message{
+ {
+ Role: schema.System,
+ Content: systemMessage,
+ },
+ {
+ Role: schema.User,
+ Content: "Fetch the top two posts. What are their titles?",
+ },
+ }
+
+ // Invoke the agent with the messages.
+ resp, err := agent.Invoke(ctx, messages)
+ if err != nil {
+ log.Fatalf("Failed to invoke agent: %v", err)
+ }
+
+ // Output the response messages.
+ for idx, msg := range resp {
+ fmt.Printf("Message %d: %s: %s\n", idx, msg.Role, msg.Content)
+ }
}
```
-## 更多详情
+## **更多详情**
+
- [Eino 文档](https://github.com/cloudwego/eino)
- [InvokableTool 接口参考](https://pkg.go.dev/github.com/cloudwego/eino/components/tool)
- [langchain_community 参考](https://python.langchain.com/docs/integrations/tools/requests/)
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_mcp.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_mcp.md
index 2b9ed45b030..0c1b43be39e 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_mcp.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_mcp.md
@@ -1,9 +1,9 @@
---
Description: ""
-date: "2025-03-12"
+date: "2025-11-20"
lastmod: ""
tags: []
-title: Tool - MCP
+title: Eino Tool - MCP
weight: 0
---
@@ -13,7 +13,7 @@ weight: 0
本节介绍 [MCPTool](https://modelcontextprotocol.io/docs/concepts/tools) 的封装,封装实现了 Eino InvokableTool 接口([Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide))。
-
+
其他封装参见:
@@ -37,7 +37,8 @@ cli, err := client.NewSSEMCPClient(myBaseURL)
// while stdio does not require it.
err = cli.Start(ctx)
```
-mcp-go 还支持了其他创建 Client 的方法(比如 InProcess),更多信息可以参考:https://mcp-go.dev/transports
+
+mcp-go 还支持了其他创建 Client 的方法(比如 InProcess),更多信息可以参考:[https://mcp-go.dev/transports](https://mcp-go.dev/transports)
考虑到 client 的复用,封装假设 client 已经完成和 Server 的 [Initialize](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/),用户需要自行完成 client 初始化:
@@ -58,7 +59,7 @@ _, err = cli.Initialize(ctx, initRequest)
```go
import "github.com/cloudwego/eino-ext/components/tool/mcp"
-mcpTools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli})
+tools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli})
```
tool 可以直接调用:
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
index 5c7a75e6ce4..e6769132b34 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md
@@ -1,26 +1,23 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Tool - sequentialthinking
+title: Tool - SequentialThinking
weight: 0
---
-## Sequential Thinking Tool
+## **Sequential Thinking Tool**
-**Sequential Thinking Tool**
-是一个用于支持动态和反思性问题解决的工具,通过结构化的思考流程帮助用户逐步分析并解决问题。灵感来自于[@modelcontextprotocol
-/sequentialthinking](https://github.com/modelcontextprotocol/servers/tree/HEAD/src/sequentialthinking)
-,它通过一系列问题引导大语言模型逐步思考问题。
+**Sequential Thinking Tool** 是一个用于支持动态和反思性问题解决的工具,通过结构化的思考流程帮助用户逐步分析并解决问题。灵感来自于 [@modelcontextprotocol /sequentialthinking](https://github.com/modelcontextprotocol/servers/tree/HEAD/src/sequentialthinking) ,它通过一系列问题引导大语言模型逐步思考问题。
-## 功能特性
+## **功能特性**
- 指导性的逐步思考流程。
- 动态提问与自我反思。
- 提升问题解决能力。
-## 使用场景
+## **使用场景**
Sequential Thinking 工具旨在用于:
@@ -31,53 +28,53 @@ Sequential Thinking 工具旨在用于:
- 任务需要在多个步骤中保持上下文
- 需要过滤不相关信息的情况
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/tool/sequentialthinking@latest
```
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "fmt"
-
- "github.com/bytedance/sonic"
-
- "github.com/cloudwego/eino-ext/components/tool/sequentialthinking"
+ "context"
+ "fmt"
+
+ "github.com/bytedance/sonic"
+
+ "github.com/cloudwego/eino-ext/components/tool/sequentialthinking"
)
func main() {
- ctx := context.Background()
-
- // Instantiate the tool
- tool, err := sequentialthinking.NewTool()
- if err != nil {
- panic(err)
- }
-
- args := &sequentialthinking.ThoughtRequest{
- Thought: "This is a test thought",
- ThoughtNumber: 1,
- TotalThoughts: 3,
- NextThoughtNeeded: true,
- }
-
- argsStr, _ := sonic.Marshal(args)
-
- // Use the tool
- // (This is just a placeholder; actual usage will depend on the tool's functionality)
- result, err := tool.InvokableRun(ctx, string(argsStr))
- if err != nil {
- panic(err)
- }
-
- // Process the result
- // (This is just a placeholder; actual processing will depend on the tool's output)
- fmt.Println(result)
+ ctx := context.Background()
+
+ // Instantiate the tool
+ tool, err := sequentialthinking.NewTool()
+ if err != nil {
+ panic(err)
+ }
+
+ args := &sequentialthinking.ThoughtRequest{
+ Thought: "This is a test thought",
+ ThoughtNumber: 1,
+ TotalThoughts: 3,
+ NextThoughtNeeded: true,
+ }
+
+ argsStr, _ := sonic.Marshal(args)
+
+ // Use the tool
+ // (This is just a placeholder; actual usage will depend on the tool's functionality)
+ result, err := tool.InvokableRun(ctx, string(argsStr))
+ if err != nil {
+ panic(err)
+ }
+
+ // Process the result
+ // (This is just a placeholder; actual processing will depend on the tool's output)
+ fmt.Println(result)
}
-```
\ No newline at end of file
+```
diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
index 052442319d3..8d357223c95 100644
--- a/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
+++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md
@@ -1,68 +1,68 @@
---
Description: ""
-date: "2025-03-20"
+date: "2025-12-11"
lastmod: ""
tags: []
-title: Tool - wikipedia
+title: Tool - Wikipedia
weight: 0
---
-## 维基百科搜索工具
+## **维基百科搜索工具**
这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的维基百科搜索工具。该工具实现了 `InvokableTool` 接口,可以与 Eino 的 ChatModel 交互系统和 `ToolsNode` 无缝集成。
-## 特性
+## **特性**
- 实现了 `github.com/cloudwego/eino/components/tool.InvokableTool` 接口
- 易于与 Eino 工具系统集成
- 可配置的搜索参数
-## 安装
+## **安装**
```bash
go get github.com/cloudwego/eino-ext/components/tool/wikipedia
```
-## 快速开始
+## **快速开始**
```go
package main
import (
- "context"
- "github.com/cloudwego/eino-ext/components/tool/wikipedia"
- "github.com/cloudwego/eino/components/tool"
- "log"
- "time"
+ "context"
+ "github.com/cloudwego/eino-ext/components/tool/wikipedia"
+ "github.com/cloudwego/eino/components/tool"
+ "log"
+ "time"
)
func main() {
- ctx := context.Background()
-
- // 创建工具配置
- // 下面所有这些参数都是默认值,仅作用法展示
- config := &wikipedia.Config{
- UserAgent: "eino (https://github.com/cloudwego/eino)",
- DocMaxChars: 2000,
- Timeout: 15 * time.Second,
- TopK: 3,
- MaxRedirect: 3,
- Language: "en",
- }
-
- // 创建搜索工具
- t, err := wikipedia.NewTool(ctx, config)
- if err != nil {
- log.Fatal("Failed to create tool:", err)
- }
-
- // 与 Eino 的 ToolsNode 一起使用
- tools := []tool.BaseTool{t}
- // ... 配置并使用 ToolsNode
+ ctx := context.Background()
+
+ // 创建工具配置
+ // 下面所有这些参数都是默认值,仅作用法展示
+ config := &wikipedia.Config{
+ UserAgent: "eino (https://github.com/cloudwego/eino)",
+ DocMaxChars: 2000,
+ Timeout: 15 * time.Second,
+ TopK: 3,
+ MaxRedirect: 3,
+ Language: "en",
+ }
+
+ // 创建搜索工具
+ t, err := wikipedia.NewTool(ctx, config)
+ if err != nil {
+ log.Fatal("Failed to create tool:", err)
+ }
+
+ // 与 Eino 的 ToolsNode 一起使用
+ tools := []tool.BaseTool{t}
+ // ... 配置并使用 ToolsNode
}
```
-## 配置
+## **配置**
工具可以通过 `Config` 结构体进行配置:
@@ -101,16 +101,15 @@ type Config struct {
// Language 是用于 Wikipedia 搜索的语言。
// 可选。默认值: "en"。
Language string `json:"language"`
-
+
ToolName string `json:"tool_name"` // 可选。默认值: "wikipedia_search"。
ToolDesc string `json:"tool_desc"` // 可选。默认值: "this tool provides quick and efficient access to information from the Wikipedia"。
}
-
```
-## Search
+## **Search**
-### 请求 Schema
+### **请求 Schema**
```go
type SearchRequest struct {
@@ -119,7 +118,7 @@ type SearchRequest struct {
}
```
-### 响应 Schema
+### **响应 Schema**
```go
type SearchResponse struct {
@@ -138,6 +137,6 @@ type SearchResult struct {
}
```
-## 更多详情
+## **更多详情**
- [Eino 文档](https://github.com/cloudwego/eino)
diff --git a/content/zh/docs/eino/overview/_index.md b/content/zh/docs/eino/overview/_index.md
index 19a4cadb331..bd91624b85a 100644
--- a/content/zh/docs/eino/overview/_index.md
+++ b/content/zh/docs/eino/overview/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 'Eino: 概述'
@@ -94,15 +94,19 @@ out, err := compiledGraph.Invoke(ctx, map[string]any{
```go
-wf := NewWorkflow[[]*Message, *Message]()
+wf := NewWorkflow[[]*schema.Message, *schema.Message]()
wf.AddChatModelNode("model", model).AddInput(START)
-wf.AddLambdaNode("l1", lambda1).AddInput("model", MapFields("Content", "Input"))
-wf.AddLambdaNode("l2", lambda2).AddInput("model", MapFields("Role", "Role"))
-wf.AddLambdaNode("l3", lambda3).AddInput("l1", MapFields("Output", "Query")).
- AddInput("l2", MapFields("Output", "MetaData"))
-wf.AddEnd("node_l3")
-runnable, _ := wf.Compile(ctx)
-runnable.Invoke(ctx, []*Message{UserMessage("kick start this workflow!")})
+wf.AddLambdaNode("lambda1", lambda1).AddInput("model", MapFields("Content", "Input"))
+wf.AddLambdaNode("lambda2", lambda2).AddInput("model", MapFields("Role", "Role"))
+wf.AddLambdaNode("lambda3", 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!")})
```
现在,咱们来创建一个 “ReAct” 智能体:一个 ChatModel 绑定了一些 Tool。它接收输入的消息,自主判断是调用 Tool 还是输出最终结果。Tool 的执行结果会再次成为聊天模型的输入消息,并作为下一轮自主判断的上下文。
@@ -122,14 +126,12 @@ Eino 会在上述代码背后自动完成一些重要工作:
```go
handler := NewHandlerBuilder().
OnStartFn(
- func(ctx context.Context, info *RunInfo, input CallbackInput) context.Context {
+ func(ctx context.Context, info *RunInfo, input CallbackInput) context.Context) {
log.Infof("onStart, runInfo: %v, input: %v", info, input)
- return ctx
}).
OnEndFn(
- func(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context {
+ func(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context) {
log.Infof("onEnd, runInfo: %v, out: %v", info, output)
- return ctx
}).
Build()
@@ -164,7 +166,7 @@ compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
### 强大的编排 (Graph/Chain/Workflow)
- 数据从 Retriever / Document Loader / ChatTemplate 流向 ChatModel,接着流向 Tool ,并被解析为最终答案。这种通过多个组件的有向、可控的数据流,可以通过**图编排**来实现。
-- 组件实例是图的 **节点(Node)** ,而 **边(Edge)** 则是数据流通道。
+- 组件实例是图的**节点(Node)**,而**边(Edge)**则是数据流通道。
- 图编排功能强大且足够灵活,能够实现复杂的业务逻辑:
- **类型检查、流处理、并发管理、切面注入和选项分配**都由框架处理。
- 在运行时进行**分支(Branch)**执行、读写全局**状态(State)**,或者使用工作流进行字段级别的数据映射。
@@ -197,7 +199,7 @@ compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1"))
## Eino 框架结构
-
+
Eino 框架由几个部分组成:
diff --git a/content/zh/docs/eino/overview/bytedance_eino_practice.md b/content/zh/docs/eino/overview/bytedance_eino_practice.md
index 4f51e59ecee..44710fded04 100644
--- a/content/zh/docs/eino/overview/bytedance_eino_practice.md
+++ b/content/zh/docs/eino/overview/bytedance_eino_practice.md
@@ -1,10 +1,10 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 字节跳动大模型应用 Go 开发框架 —— Eino 实践
-weight: 1
+weight: 2
---
## 前言
@@ -467,7 +467,7 @@ docker-compose up -d
如果在运行时,在 .env 文件中指定了 `APMPLUS_APP_KEY`,便可在 [火山引擎 APMPlus](https://console.volcengine.com/apmplus-server) 平台中,登录对应的账号,查看 Trace 以及 Metrics 详情。
-
+
##### Langfuse
diff --git a/content/zh/docs/eino/overview/eino_adk0_1.md b/content/zh/docs/eino/overview/eino_adk0_1.md
index 46e5c5e5f95..01ec570ec75 100644
--- a/content/zh/docs/eino/overview/eino_adk0_1.md
+++ b/content/zh/docs/eino/overview/eino_adk0_1.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-11-14"
+date: "2025-12-09"
lastmod: ""
tags: []
title: Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统
@@ -13,7 +13,7 @@ weight: 3
但技术演进中痛点也随之凸显,有的团队因不懂如何衔接 LLM 与业务系统,导致 Agent 只能 “空谈”;有的因状态管理缺失,让 Agent 执行任务时频频 “失忆”,复杂的交互流程也进一步增加了开发难度。
-为此,Eino ADK(Agent Development Kit)应运而生,为 Go 开发者提供了一套完整、灵活且强大的智能体开发框架,直接解决传统开发中的核心难题。
+为此,**Eino ADK(Agent Development Kit)应运而生,为 Go 开发者提供了一套完整、灵活且强大的智能体开发框架**,直接解决传统开发中的核心难题。
## 🙋 什么是 Agent?
@@ -54,9 +54,9 @@ Agent 代表一个独立的、可执行的智能任务单元,能够自主学
3. ChatModelAgent 执行工具(Act)
4. 将工具结果返回给 LLM(Observation),结合之前的上下文继续生成,直到模型判断不需要调用 Tool 后结束。
-
+
-ReAct 模式的核心是**思考 → 行动 → 观察 → 再思考**的闭环,解决传统 Agent “盲目行动”或“推理与行动脱节”的痛点,以下是几种可能的实践场景:
+ReAct 模式的核心是“**思考 → 行动 → 观察 → 再思考**”的闭环,解决传统 Agent “盲目行动”或“推理与行动脱节”的痛点,以下是几种可能的实践场景:
- **行业赛道分析**:使用 ReAct 模式避免了一次性搜集全部信息导致的信息过载,通过逐步推理聚焦核心问题;同时使用数据验证思考,而非凭空靠直觉决策,过程可解释,提升了生成报告的准确性。
- **Think-1**:判断赛道潜力,需要 “政策支持力度、行业增速、龙头公司盈利能力、产业链瓶颈”4 类信息。
@@ -102,7 +102,7 @@ Eino ADK 提供了专用于协调子 Agent 执行流程的 WorkflowAgents 模式
- **线性执行**:严格按照 SubAgents 数组的顺序执行。
- **运行结果传递**:配置中的每个 Agent 都能够获取 Sequential Agent 的完整输入以及前序 Agent 的输出。
- **支持提前退出**:如果任何一个子 Agent 产生退出 / 中断动作,整个 Sequential 流程会立即终止。
-- **可能的实践场景有**:
+- 可能的实践场景有:
- **数据 ETL**:`ExtractAgent`(从 MySQL 抽取订单数据)→ `TransformAgent`(清洗空值、格式化日期)→ `LoadAgent`(加载到数据仓库)
- **CI / CD 流水线**:`CodeCloneAgent`(从代码仓库拉取代码)→`UnitTestAgent`(运行单元测试,用例失败时返回错误与分析报告)→`CompileAgent`(编译代码)→`DeployAgent`(部署到目标环境)
@@ -120,7 +120,7 @@ sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{
})
```
-
+
- **Parallel Agent**: 将配置中注册的 Agents 并发执行,所有 Agent 执行完毕后结束,运行遵循以下原则:
- **并发执行**:所有子 Agent 同时启动,在独立的 goroutine 中并行执行。
@@ -144,7 +144,7 @@ parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{
})
```
-
+
- **Loop Agent**:将配置中注册的 Agents 按顺序依次执行并循环多次,运行遵循以下原则:
- **循环执行**:重复执行 SubAgents 序列,每次循环都是一个完整的 Sequential 执行过程。
@@ -169,7 +169,7 @@ loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{
})
```
-
+
## 🛠️ 预构建的 Multi-Agent 范式
@@ -182,7 +182,7 @@ Supervisor Agent 是 ADK 提供的一种中心化 Multi-Agent 协作模式,旨
- Supervisor Agent 负责任务的分配、子 Agent 完成后的结果汇总与下一步决策。
- 子 Agents 专注于执行具体任务,并在完成后自动将任务控制权交回 Supervisor。
-
+
Supervisor 模式有如下特点:
@@ -218,7 +218,7 @@ Plan-Execute Agent 是 ADK 提供的基于「规划-执行-反思」范式的 Mu
- **Executor**:执行当前计划中的首个步骤
- **Replanner**:评估执行进度,决定是修正计划继续交由 Executor 运行,或是结束任务
-
+
Plan-Execute 模式有如下特点:
@@ -258,6 +258,45 @@ researchAssistant := planexecute.New(ctx, &planexecute.Config{
})
```
+#### 🎯 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 抽象
@@ -304,7 +343,7 @@ func AddSessionValues(ctx context.Context, kvs map[string]any)
- **移交运行(Transfer)**:携带本 Agent 输出结果上下文,将任务移交至子 Agent 继续处理。适用于智能体功能可以清晰的划分边界与层级的场景,常结合 ChatModelAgent 使用,通过 LLM 的生成结果进行动态路由。结构上,以此方式进行协作的两个 Agent 称为父子 Agent:
-
+
```go
// 设置父子 Agent 关系
@@ -316,7 +355,7 @@ func NewTransferToAgentAction(destAgentName string) *AgentAction
- **显式调用(ToolCall)**:将 Agent 视为工具进行调用。适用于 Agent 运行仅需要明确清晰的参数而非完整运行上下文的场景,常结合 ChatModelAgent,作为工具运行后将结果返回给 ChatModel 继续处理。除此之外,ToolCall 同样支持调用符合工具接口构造的、不含 Agent 的普通工具。
-
+
```go
// 将 Agent 转换为 Tool
@@ -391,7 +430,7 @@ go get github.com/cloudwego/eino@latest
- **对已有项目的完善**:项目经理从评论 Agent 获得项目仍旧需要完善的功能点,交由编码 Agent 进行实现,再交由评论 Agent 对修改后的代码进行评审。
- **开展技术调研**:项目经理要求调研 Agent 生成技术调研报告,然后由评论 Agent 给出评审意见。调用方结合返回的技术调研报告和评审意见,决定后续动作。
-
+
该示例的设计涵盖了文中介绍的大部分概念,您可以基于示例回顾之前的提到的种种设计理念。另外,请试想普通开发模式下如何完成该示例的编写,ADK 的优势便立刻凸显了出来:
@@ -529,4 +568,4 @@ Eino ADK 不仅仅是一个开发框架,更是一个完整的智能体开发
>
> Eino ADK,让智能体开发变得简单而强大!
-
+
diff --git a/content/zh/docs/eino/overview/eino_adk_excel_agent.md b/content/zh/docs/eino/overview/eino_adk_excel_agent.md
index fd673d513c8..4bdf3805c2f 100644
--- a/content/zh/docs/eino/overview/eino_adk_excel_agent.md
+++ b/content/zh/docs/eino/overview/eino_adk_excel_agent.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-11-14"
+date: "2025-12-02"
lastmod: ""
tags: []
title: 用 Eino ADK 构建你的第一个 AI 智能体:从 Excel Agent 实战开始
@@ -9,7 +9,7 @@ weight: 4
## 从 Excel Agent 详解 Eino ADK
-本文将会向您介绍如何利用 **Eino ADK(Agent Development Kit)** 构建一个强大的多智能体系统,往期 Eino ADK 介绍链接:[Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统](https://mp.weixin.qq.com/s/ffGjlDEzEzroo8w6knlLqw)
+本文将会向您介绍如何利用 **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 数据处理任务。
@@ -23,7 +23,7 @@ Excel Agent 是一个“看得懂 Excel 的智能助手”,它先把问题拆
Excel Agent 整体是基于 Eino ADK 实现的 Multi-Agent 系统,完整架构如下图所示:
-
+
Excel Agent 内部包含的几个 Agent 功能分别为:
@@ -45,7 +45,7 @@ Excel Agent 内部包含的几个 Agent 功能分别为:
Excel Agent 的完整运行动线为:
-
+
> 💡
> **核心收益**:
@@ -62,7 +62,7 @@ Excel Agent 既可以单独使用,也可以作为子 Agent,集成在一个
`ChatModelAgent` 是 Eino ADK 中的一个核心预构建的 Agent,内部使用了 [ReAct](https://react-lm.github.io/) 模式(一种让模型‘思考-行动-观察’的链式推理模式):
-
+
`ChatModelAgent` 旨在让 ChatModel 进行显式的、一步一步的“思考”,结合思考过程驱动行动,观测历史思考过程与行动结果继续进行下一步的思考与行动,最终解决复杂问题:
@@ -71,7 +71,7 @@ Excel Agent 既可以单独使用,也可以作为子 Agent,集成在一个
- ChatModelAgent 执行工具(Act)
- 将工具结果返回给 LLM(Observation),结合之前的上下文继续生成,直到模型判断不需要调用工具后结束
-
+
在 Excel Agent 中,每个 Agent 的核心都是这样一个 `ChatModelAgent`,以 Executor 运行【读取用户输入表格的头信息】这个步骤为例 ,我们可以通过观察完整的运行过程来理解 ReAct 模式在 `ChatModelAgent` 中的表现:
@@ -86,7 +86,7 @@ Excel Agent 既可以单独使用,也可以作为子 Agent,集成在一个
### Plan-Execute Agent:基于「规划-执行-反思」的多智能体协作框架
-Plan-Execute Agent 是 Eino ADK 中一种基于「规划-执行-反思」范式的多智能体协作框架,旨在解决复杂任务的分步拆解、执行与动态调整问题。它通过 **Planner(规划器)**、**Executor(执行器)** 和 **Replanner(重规划器)** 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态 replanning,最终达成用户目标:
+Plan-Execute Agent 是 Eino ADK 中一种基于「规划-执行-反思」范式的多智能体协作框架,旨在解决复杂任务的分步拆解、执行与动态调整问题。它通过 **Planner(规划器)**、**Executor(执行器)**和 **Replanner(重规划器)** 三个核心智能体的协同工作,实现任务的结构化规划、工具调用执行、进度评估与动态 replanning,最终达成用户目标:
```go
// 完整代码: https://github.com/cloudwego/eino/blob/main/adk/prebuilt/planexecute/plan_execute.go
@@ -104,11 +104,11 @@ func NewReplanner(_ context.Context, cfg *ReplannerConfig) (adk.Agent, error)
func New(ctx context.Context, cfg *Config) (adk.Agent, error)
```
-
+
而 Excel Agent 的核心能力恰好为【解决用户在 excel 领域的问题】,与该智能体协作框架定位一致:
-- **规划者(Planner)**:明确目标,自动拆解可执行步骤
+- **规划者**(**Planner**):明确目标,自动拆解可执行步骤
- **执行者(Executor)**:调用工具(Excel 读取、系统命令、Python 代码)完成规划中的每一个详细步骤
- **反思者(Replanner)**:根据执行进度决定继续、调整规划或结束
@@ -165,7 +165,7 @@ Excel Agent 中,存在一些需要按照特定顺序运行 agent 的情况:
})
```
-
+
- **LoopAgent**:重复执行配置的子 Agent 序列,直到达到最大迭代次数或某个子 Agent 产生 ExitAction,每次迭代的结果都会累积,后续迭代的输入可以访问所有历史信息。LoopAgent 基于 SequentialAgent 实现。
@@ -184,7 +184,7 @@ Excel Agent 中,存在一些需要按照特定顺序运行 agent 的情况:
})
```
-
+
- **ParallelAgent**:允许多个子 Agent 基于相同的输入上下文并发执行。所有子 Agent 接收相同的初始输入,各自在独立的 goroutine(Go 语言中一种轻量级的并发执行单元) 运行,最终收集所有子 Agent 的执行结果并按顺序输出到 `AsyncIterator` 中。
@@ -202,7 +202,7 @@ Excel Agent 中,存在一些需要按照特定顺序运行 agent 的情况:
})
```
-
+
### Agent 抽象:灵活定义 Agent 的基础
@@ -221,8 +221,8 @@ Eino ADK 的核心是一个简洁而强大的 Agent 接口,每个 Agent 都有
```go
type AgentInput struct {
- Messages []_Message_
- _ _EnableStreaming bool
+ Messages []Message
+ EnableStreaming bool
}
type Message = *schema.Message // *schema.Message 是模型输入输出的结构定义
@@ -296,7 +296,7 @@ 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 覆盖当前的计划。
@@ -317,14 +317,14 @@ Eino ADK 包含两种基础的数据传递机制:
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 关系
@@ -336,7 +336,7 @@ func NewTransferToAgentAction(destAgentName string) *AgentAction
- **显式调用(ToolCall)**:将 Agent 视为工具进行调用,适用于 Agent 运行仅需要明确清晰的参数而非完整运行上下文的场景。常结合 ChatModelAgent,将 Agent 作为工具运行后将结果返回给 ChatModel 继续处理。除此之外,ToolCall 同样支持调用符合工具接口构造的、不含 Agent 的普通工具。
-
+
```go
// 将 Agent 转换为 Tool
@@ -379,7 +379,7 @@ Excel Agent 单次运行会在输出路径下创建一个新的工作目录,
以 `请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中` 这个任务为例,运行完成后在工作目录下的文件包含:
-
+
1. 原始输入:从输入路径获取到的 `question.csv`
2. Planner / Replanner 给出的运行计划:`plan.md`
@@ -538,4 +538,4 @@ Excel Agent 所呈现的并非“单一智能体”的技巧,而是一套以
>
> Eino ADK,让智能体开发变得简单而强大!
-
+
diff --git a/content/zh/docs/eino/overview/eino_open_source.md b/content/zh/docs/eino/overview/eino_open_source.md
index aea1bcb7162..c9e17146988 100644
--- a/content/zh/docs/eino/overview/eino_open_source.md
+++ b/content/zh/docs/eino/overview/eino_open_source.md
@@ -1,13 +1,13 @@
---
Description: ""
-date: "2025-03-04"
+date: "2025-12-01"
lastmod: ""
tags: []
title: 大语言模型应用开发框架 —— Eino 正式开源!
weight: 1
---
-今天,经过字节跳动内部半年多的使用和迭代,基于 Go 的大模型应用综合开发框架 —— Eino,已在 CloudWeGo 正式开源啦!
+今天,经过字节跳动内部半年多的使用和迭代,基于 Golang 的大模型应用综合开发框架 —— Eino,已在 CloudWeGo 正式开源啦!
Eino 基于明确的“组件”定义,提供强大的流程“编排”,覆盖开发全流程,旨在帮助开发者以最快的速度实现最有深度的大模型应用。
@@ -15,7 +15,7 @@ Eino 基于明确的“组件”定义,提供强大的流程“编排”,覆
- 内核稳定,API 简单易懂,有明确的上手路径,平滑的学习曲线。
- 极致的扩展性,研发工作高度活跃,长期可持续。
-- 基于强类型语言 Go,代码能看懂,易维护,高可靠。
+- 基于强类型语言 Golang,代码能看懂,易维护,高可靠。
- 背靠字节跳动核心业务线的充分实践经验。
- 提供开箱即用的配套工具。
@@ -116,7 +116,7 @@ agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))
基于大语言模型的软件应用正处于快速发展阶段,新技术、新思路、新实践不断涌现,我们作为应用开发者,一方面需要高效、可靠的把业界共识的最佳实践应用起来,另一方面需要不断学习和提升认知,从而能够整体理解这个新领域的可能性。因此,一个优秀的大模型应用开发框架,既需要**封装领域内“不变”的通用核心要素**,又需要基于最新进展**敏捷的横向和纵向扩展**。
-另一方面,目前较为主流的框架如 LangChain,LlamaIndex 等,都基于 Python,虽然能借助 Python 较为丰富的生态快速实现多样的功能,但是同时也继承了 Python 作为动态语言所带来的“弱类型检验”和“长期维护成本高”等问题。在大模型应用快速进入大规模线上运行阶段的当下,基于 Go 这一强类型语言而实现的**高可靠性**和**高可维护性**,逐渐具有更大的价值。
+另一方面,目前较为主流的框架如 LangChain,LlamaIndex 等,都基于 Python,虽然能借助 Python 较为丰富的生态快速实现多样的功能,但是同时也继承了 Python 作为动态语言所带来的“弱类型检验”和“长期维护成本高”等问题。在大模型应用快速进入大规模线上运行阶段的当下,基于 Golang 这一强类型语言而实现的**高可靠性**和**高可维护性**,逐渐具有更大的价值。
基于大模型的应用开发是相对较新的领域,有时需要摸着石头过河,靠实践来检验认知。依托字节跳动高频应用豆包、抖音等的多样场景、快速迭代和海量反馈,Eino 在**实践驱动设计**方面有独特的优势。
@@ -150,7 +150,7 @@ agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))
### 高可靠易维护
-基于 Go 写 Eino 代码时,开发者可以充分利用 Go 的强类型特性,为所有的组件、Lambda、编排产物等声明具体类型。这像是为代码绘制了一幅精确的地图,开发者可以沿着清晰的路径进行维护和扩展,即使在项目规模不断扩大、功能持续迭代的情况下,依然能够保有较高的可维护性。
+基于 Golang 写 Eino 代码时,开发者可以充分利用 Golang 的强类型特性,为所有的组件、Lambda、编排产物等声明具体类型。这像是为代码绘制了一幅精确的地图,开发者可以沿着清晰的路径进行维护和扩展,即使在项目规模不断扩大、功能持续迭代的情况下,依然能够保有较高的可维护性。
同时,Eino 编排能力也充分利用了强类型系统的编译时校验能力,尽可能将类型匹配问题暴露的时机提前到 graph 的编译时,而不是 graph 的运行时。尽早并明确的暴露类型匹配问题,有助于开发者迅速定位和修复,减少因类型错误在运行时引发的难以排查的故障和性能问题。
diff --git a/content/zh/docs/eino/overview/graph_or_agent.md b/content/zh/docs/eino/overview/graph_or_agent.md
index a752c87ddbe..f6b7e0ccdca 100644
--- a/content/zh/docs/eino/overview/graph_or_agent.md
+++ b/content/zh/docs/eino/overview/graph_or_agent.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-11-25"
+date: "2025-12-09"
lastmod: ""
tags: []
title: Agent 还是 Graph?AI 应用路线辨析
@@ -18,7 +18,36 @@ weight: 5
- 以“聊天框”为代表性标志的“Agent(智能体)”。**Agent 以 LLM(大语言模型)为决策中心,自主规划并能进行多轮交互**,天然适合处理开放式、持续性的任务,表现为一种“对话”形态。
- 以“按钮”或者“API”为代表性标志的“Graph(流程图)”。比如上面的“录音纪要”这个“按钮”,其背后的 Graph 大概是“录音”-》“LLM 理解并总结” -》“保存录音”这种固定流程。**Graph 的核心在于其流程的确定性与任务的封闭性**,通过预定义的节点和边来完成特定目标,表现为一种“功能”形态。
-
+```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) 开发者给出建议的使用姿势。
@@ -44,13 +73,49 @@ weight: 5
总结:Agent 可认为是自主的,整体由 LLM 驱动,以 Tool Call 的形式使用外部能力。Graph 是确定性的,以明确拓扑结构串联外部能力,同时在局部利用 LLM 做决策/生成等。
-
+```mermaid
+flowchart TD
+ subgraph AIApp["AI 应用"]
+ Agent["Agent (自主性)"]
+ Graph["Graph (确定性)"]
+ end
+
+ subgraph CoreDrive["智能来源"]
+ LLM["LLM (决策/生成)"]
+ end
+
+ subgraph ExternalCap["外部能力"]
+ Tool["External Capacity
(函数/API)"]
+ end
+
+ Agent -- "驱动力来源" --> LLM
+ Graph -- "包含节点" --> LLM
+ Agent -- "工具调用" --> Tool
+ Graph -- "包含节点" --> Tool
+
+ classDef agent fill:#EAE2FE,stroke:#000000
+ classDef graphClass fill:#F0F4FC,stroke:#000000
+ classDef llm fill:#FEF1CE,stroke:#000000
+ classDef tool fill:#DFF5E5,stroke:#000000
+
+ class Agent agent
+ class Graph graphClass
+ class LLM llm
+ class Tool tool
+```
## 历史视角:从确定性走向自主性
当 Langchain 框架在 2022 年首次发布时,LLM 世界的 API 范式还是 OpenAI 的 [Completions API](https://platform.openai.com/docs/guides/completions),一个简单的“文本进,文本出”的 API。发布之初,Langchain 的口号是“[connect LLMs to external sources of computation and data](https://blog.langchain.com/langchain-second-birthday/)”。典型的“Chain”可能是这样的:
-
+```mermaid
+flowchart LR
+ S[retrievers, loaders,
prompt templates, etc...]
+ L[LLM]
+ P[output parsers, other handlers, etc...]
+
+ S-->L-->P
+```
随后,[ReAct](https://react-lm.github.io/)(Reasoning and Acting)范式的提出,首次系统性地展示了如何让 LLM 不仅生成文本,更能通过“思考-行动-观察”的循环来与外部交互,解决复杂问题。这一突破为 Agent 的自主规划能力奠定了理论基础。近乎同时,OpenAI 推出了 [ChatCompletions API](https://platform.openai.com/docs/api-reference/chat),推动了 LLM 交互能力从“单次的文本输入输出”向“多轮对话”转变。之后 [Function Calling](https://platform.openai.com/docs/guides/function-calling)(函数调用) 能力出现,LLM 具备了标准的与外部函数和 API 交互的能力。至此,我们已经可以搭建出“多轮对话并与可以与外界自主交互”的 LLM 应用场景,即 Agent。在这个背景下,AI 应用框架产生了两个重要发展:
@@ -61,11 +126,34 @@ weight: 5
- 交付物的不匹配:编排出的 ReAct Agent 的输出是“最终结果”,而实际应用往往关注各种中间过程。用 Callback 等方案可以解决,足够完备,但依然属于“补丁”。
-
+```mermaid
+flowchart LR
+ A[ReAct Agent]
+ P@{ shape: processes, label: "全过程数据" }
+ A--o|关注|P
+
+ G[Graph]
+ F[最终结果]
+ G-->|主流程输出,
但被旁路输出涵盖|F
+
+ G-.->|旁路抽取|P
+```
- 运行模式的不匹配:由于是同步运行,所以“为了尽快把 LLM 的回复展示给用户”,要求 ReAct Agent 编排内的各节点都尽量“快”,这主要是“在判断 LLM 的输出是否包含 ToolCall”的分支判断逻辑中,要尽可能根据第一个包或者前几个包完成判断。这个分支判断逻辑可以自定义,比如“读流式输出直到看到 Content,才判断为没有 ToolCall”,但有时并不能完全解决问题,只能通过 Callback 这样的“旁路”手动切换“同步”为“异步”。
-
+```mermaid
+flowchart LR
+ L[LLM 节点]
+ S@{ shape: processes, label: "流式内容"}
+ L-->|生成|S
+
+ B{是否包含
工具调用}
+ D@{ shape: processes, label: "流式内容"}
+
+ B-->|否,上屏展示|D
+
+ S-->|逐帧判断|B
+```
这些痛点源于两者本质的差异。一个为确定性流程(Graph)设计的框架,很难原生支持一个以动态“思考链”为核心的自主系统(Agent)。
@@ -87,15 +175,44 @@ Eino 框架的目标是同时支持 Graph 和 Agent 两种场景。我们的演
- 层级调用(Agent as Tool):这是最常见的模式(参考 Google ADK 的[定义](https://google.github.io/adk-docs/agents/multi-agents/#c-explicit-invocation-agenttool)和[举例](https://google.github.io/adk-docs/agents/multi-agents/#hierarchical-task-decomposition))。一个上层 Agent 将特定子任务委托给专门的“Tool Agent”。例如,一个主 Agent 负责与用户交互,当需要执行代码时,它会调用一个“代码执行 Agent”。在这种模式下,子 Agent 通常是无状态的,不与主 Agent 共享记忆,其交互是一个简单的 Function Call。上层 Agent 和子 Agent 只有一种关系:调用与被调用。因此,我们可以得出,Agent as Tool 的 Multi-Agent 模式,不是“Graph 编排”中的“节点流转”关系。
-
+```mermaid
+flowchart LR
+ subgraph 主 Agent
+ L[主 Agent 的 LLM]
+ T1[子 Agent 1]
+ T2[子 Agent 2]
+
+ L-->|工具调用|T1
+ L-->|工具调用|T2
+ end
+```
- 预设流程:对于一些成熟的协作模式,如“规划-执行-反思”(Plan-Execute-Replan)(参考 Langchain 的[样例](https://langchain-ai.github.io/langgraph/tutorials/plan-and-execute/plan-and-execute/)),Agent 间的交互顺序和角色是固定的。框架(如 Eino adk)可以将这些模式封装为“预制 Multi-Agent 模式”,开发者可以直接使用,无需关心内部的细节,也不需要手动设置或调整子 Agent 之间的流程关系。因此,我们可以得出,针对成熟的协作模式,“Graph 编排”是封装在预制模式内部的实现细节,开发者不感知。
-
+```mermaid
+flowchart LR
+ subgraph Plan-Execute-Replan
+ P[planner]
+ E[executor]
+ R[Replanner]
+ P-->E
+ E-->R
+ R-->E
+ end
+
+ user -->|整体使用| Plan-Execute-Replan
+```
- 动态协作:在更复杂的场景中,Agent 的协作方式是动态的(参考 Google ADK 的[定义](https://google.github.io/adk-docs/agents/multi-agents/#b-llm-driven-delegation-agent-transfer)和[举例](https://google.github.io/adk-docs/agents/multi-agents/#coordinatordispatcher-pattern)),可能涉及竞价、投票或由一个“协调者 Agent”在运行时决定。这种模式下,Agent 之间的关系是“Agent 流转”,与“Graph 编排”中的“节点流转”有相似之处,都是“控制权”由 A 到 B 的完全转交。但是,这里的“Agent 流转”可以是完全动态的,其动态特性不仅体现在“可以流转到哪些 Agent”,更体现在“如何做出流转到哪个 Agent 的决策”上,都不是由开发者预设的,而是 LLM 的实时动态行为。这与“Graph 编排”的静态确定性形成了鲜明的对比。因此,我们可以得出,动态协作的 Multi-Agent 模式,从本质上与“Graph 编排”完全不同,更适合在 Agent 框架层面给出独立的解决方案。
-
+```mermaid
+flowchart LR
+ A[Agent 1]
+ B[Agent 2]
+ C[Agent 3]
+
+ A-.->|动态转交|B-.->|动态转交|C
+```
综上所述,Multi-Agent 的协作问题,或可通过“Agent as Tool”模式降维解决,或可由框架提供固化模式,或是本质上完全动态的协作,其对“编排”的需求与 Graph 的静态的、确定性的流程编排有着本质区别。
@@ -108,7 +225,17 @@ Eino 框架的目标是同时支持 Graph 和 Agent 两种场景。我们的演
- Agent 的输入来源更为多样,除了能接收来自上游节点的结构化数据外,还严重依赖于自身的会话历史(Memory)。这与 Graph 节点严格依赖其上游输出作为唯一输入的特性形成了鲜明对比。
- Agent 的输出是异步的全过程数据。这意味着其他节点很难使用“Agent 节点”的输出。
-
+```mermaid
+flowchart LR
+ U[前置节点]
+ A[Agent 节点]
+ D[后置节点]
+ M[Memory]
+
+ U-->|不是全部输入
|A
+ M-.->|外部状态注入|A
+ A-->|全过程数据
面向用户或 LLM
|D
+```
因此,向 Graph 中加入 Agent 节点,意味着将一个需要多轮交互、长时记忆和异步输出的 Agent 强行嵌入到一个确定性的、同步执行的 Graph 节点中,这通常是不优雅的。Agent 的启动可以被 Graph 编排,但其内部的复杂交互不应阻塞主流程。
@@ -126,7 +253,7 @@ Eino 框架的目标是同时支持 Graph 和 Agent 两种场景。我们的演
| 特征维度 | Graph | Tool |
| 输入 | 结构化的数据 | 结构化的数据 |
| 交付物 | 聚焦最终结果 | 聚焦最终结果 |
-| 状态管理 | 单次执行、stateless | 单次执行、stateless。 |
+| 状态管理 | 单次执行、stateless | 单次执行、stateless |
| 运行模式 | 整体是同步 | LLM 的视角 Tool 是同步的 |
@@ -134,7 +261,35 @@ Eino 框架的目标是同时支持 Graph 和 Agent 两种场景。我们的演
“Agent”与“Graph”的“路线之争”,实现了对立统一。
-
+```mermaid
+flowchart TD
+ subgraph Agent ["Agent"]
+ A["LLM 决策"] --> B{"调用工具?"}
+ B -- "是" --> C["Tool: my_graph_tool"]
+ end
+
+ subgraph Tool ["Tool"]
+ C -- "封装" --> D["Graph: my_graph"]
+ end
+
+ subgraph Graph ["Graph"]
+ D -- "执行" --> E["节点1"]
+ E --> F["节点2"]
+ F --> G["返回结果"]
+ end
+
+ G -- "输出" --> C
+ C -- "结果" --> A
+
+ classDef agent fill:#EAE2FE,stroke:#000000
+ classDef tool fill:#DFF5E5,stroke:#000000
+ classDef graphGroup fill:#F0F4FC,stroke:#000000
+ class A,B agent
+ class C tool
+ class D,E,F,G graphGroup
+```
+
+Graph-Tool-Agent 关系图
## 结论
@@ -145,7 +300,7 @@ Agent 与 Graph 并非路线之争,而是能力互补的两种 AI 应用范式
两者的最佳结合点,在于将 Graph 封装为 Agent 的 Tool。
-通过这种方式,我们可以充分利用 Graph 在流程编排和生态集成上的强大能力,来扩展 Agent 的 Tool 列表一个复杂的 Graph 应用(如一套完整的 RAG 流程、一个数据分析管道)可以被简化成 Agent 的一个原子能力,被其在合适的时机动态调用。
+通过这种方式,我们可以充分利用 Graph 在流程编排和生态集成上的强大能力,来扩展 Agent 的 Tool 列表。一个复杂的 Graph 应用(如一套完整的 RAG 流程、一个数据分析管道)可以被简化成 Agent 的一个原子能力,被其在合适的时机动态调用。
对于 Eino 的开发者而言,这意味着:
diff --git a/content/zh/docs/eino/quick_start/_index.md b/content/zh/docs/eino/quick_start/_index.md
index 0585935f654..0094c9c11e8 100644
--- a/content/zh/docs/eino/quick_start/_index.md
+++ b/content/zh/docs/eino/quick_start/_index.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-02-08"
+date: "2025-11-20"
lastmod: ""
tags: []
title: 'Eino: 快速开始'
@@ -23,7 +23,7 @@ AI 的应用中,最基础的场景就是 prompt + chat model 的场景,这
### 示例:创建一个 Agent
-大模型是 AI 的大脑,其核心是理解自然语言,并做出回应,(文本)大模型本身只能接收一段文本,然后输出一段文本。而当你希望大模型能使用一些工具自行获取所需的信息、执行一些动作,就需要使用 `Tool` 来实现了,拥有了 Tool 的大模型就像是拥有了手脚,可以和当下已有的 IT 基础设施进行交互,比如 "调用 http 接口查询天气,再根据天气提醒你今天要穿什么衣服",就需要大模型调用 "search tool" 查询信息。
+大模型是 AI 的大脑,其核心是理解自然语言,并做出回应,(文本)大模型本身只能接收一段文本,然后输出一段文本。而当你希望大模型能使用一些工具自行获取所需的信息、执行一些动作,就需要使用 `Tool` 来实现了,拥有了 Tool 的大模型就像是拥有了手脚,可以和当下已有的 IT 基础设施进行交互,比如 "调用 http 接口查询天气,再根据天气提醒你今天要传什么衣服",就需要大模型调用 "search tool" 查询信息。
我们通常把能够根据大模型的输出调用相关 tool 的这套体系所构建出的整体,叫做 “智能体”,即 Agent。
@@ -37,6 +37,3 @@ AI 的应用中,最基础的场景就是 prompt + chat model 的场景,这
- 理解 Eino 的核心模块和概念: [Eino: 核心模块](/zh/docs/eino/core_modules),这是你自如玩转使用 Eino 做应用开发的关键信息。
- Eino 保持开放生态的姿态,提供了大量生态集成组件:[Eino: 生态集成](/zh/docs/eino/ecosystem_integration),你可以使用这些组件快速构建自己的业务应用。
-
-如果您觉得这个项目还不错或者给到了你帮助,请给 [Eino](https://github.com/cloudwego/eino) 点个 star 吧!这将是对我们最大的鼓励和支持!
-如果你有任何问题或者建议,欢迎在 [GitHub Issues](https://github.com/cloudwego/eino/issues) 留言。
diff --git a/content/zh/docs/eino/quick_start/agent_llm_with_tools.md b/content/zh/docs/eino/quick_start/agent_llm_with_tools.md
index 59b9217866e..18047b6e2d7 100644
--- a/content/zh/docs/eino/quick_start/agent_llm_with_tools.md
+++ b/content/zh/docs/eino/quick_start/agent_llm_with_tools.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-09"
lastmod: ""
tags: []
title: Agent-让大模型拥有双手
@@ -149,7 +149,7 @@ func (lt *ListTodoTool) InvokableRun(ctx context.Context, argumentsInJSON string
}
```
-### **方式四:使用官方封装的工具**
+### **方式四:使用****官方封装的工具**
除了自己实现工具,我们还提供了许多开箱即用的工具。这些工具经过充分测试和优化,可以直接集成到你的 Agent 中。以 duckduckgo Search 工具为例:
diff --git a/content/zh/docs/eino/quick_start/simple_llm_application.md b/content/zh/docs/eino/quick_start/simple_llm_application.md
index 1d46309e778..a14be0795be 100644
--- a/content/zh/docs/eino/quick_start/simple_llm_application.md
+++ b/content/zh/docs/eino/quick_start/simple_llm_application.md
@@ -1,6 +1,6 @@
---
Description: ""
-date: "2025-03-18"
+date: "2025-12-09"
lastmod: ""
tags: []
title: 实现一个最简 LLM 应用
@@ -142,7 +142,7 @@ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
> OpenAI 相关信息,可以参考:[ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama)
-无论使用哪种实现,ChatModel 都提供了一致的接口,这意味着你可以轻松地在不同的模型之间切换,而无需修改大量代码。
+Eino 为大模型提供了统一的 ChatModel 抽象,并提供了开箱即用的各类 LLM 实现,因此业务代码可以无需关注模型实现细节从而专注业务逻辑编写,模型实现迭代时不会影响核心业务逻辑,这意味着开发者可以轻松地在不同的模型之间切换,而无需修改大量代码。
### 运行 ChatModel
diff --git a/content/zh/docs/hertz/tutorials/third-party/middleware/swagger.md b/content/zh/docs/hertz/tutorials/third-party/middleware/swagger.md
index 3972952964d..d774ff31348 100644
--- a/content/zh/docs/hertz/tutorials/third-party/middleware/swagger.md
+++ b/content/zh/docs/hertz/tutorials/third-party/middleware/swagger.md
@@ -125,6 +125,7 @@ go run main.go
对于想要创建自己的自定义 UI 的用户,他们需要下载 Swagger UI 分发文件,并将 UI 文件作为静态资源提供。
从 [swagger-ui/dist](https://github.com/swagger-api/swagger-ui/tree/master/dist) 中复制以下文件到 `swagger-ui/`
+
- https://github.com/swagger-api/swagger-ui/blob/master/dist/favicon-16x16.png
- https://github.com/swagger-api/swagger-ui/blob/master/dist/favicon-32x32.png
- https://github.com/swagger-api/swagger-ui/blob/master/dist/swagger-ui.css
diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html
index d199f066681..ae45aea62b2 100644
--- a/layouts/partials/scripts.html
+++ b/layouts/partials/scripts.html
@@ -2,7 +2,7 @@
{{ if .Site.Params.mermaid.enable }}
-
+
{{ end }}
diff --git a/static/img/eino/BwQDwnPYoh3masbDO4pcOflDnId.png b/static/img/eino/BwQDwnPYoh3masbDO4pcOflDnId.png
deleted file mode 100644
index 3209bafd0e5..00000000000
Binary files a/static/img/eino/BwQDwnPYoh3masbDO4pcOflDnId.png and /dev/null differ
diff --git a/static/img/eino/CizUbCRP4oMhmGxHXbdcDKdPnZc.png b/static/img/eino/CizUbCRP4oMhmGxHXbdcDKdPnZc.png
deleted file mode 100644
index d4cf8a8f5fd..00000000000
Binary files a/static/img/eino/CizUbCRP4oMhmGxHXbdcDKdPnZc.png and /dev/null differ
diff --git a/static/img/eino/D3CsbyQehoGCVkxc0tKcXVqFnil.png b/static/img/eino/D3CsbyQehoGCVkxc0tKcXVqFnil.png
deleted file mode 100644
index 8e179f1eb97..00000000000
Binary files a/static/img/eino/D3CsbyQehoGCVkxc0tKcXVqFnil.png and /dev/null differ
diff --git a/static/img/eino/DIWobv6ZWokDYhxTmIbcpI7ynAc.png b/static/img/eino/DIWobv6ZWokDYhxTmIbcpI7ynAc.png
deleted file mode 100644
index 57bc0c66a75..00000000000
Binary files a/static/img/eino/DIWobv6ZWokDYhxTmIbcpI7ynAc.png and /dev/null differ
diff --git a/static/img/eino/EfnOb1BnBoitoix6KjWcLkpQnbg.png b/static/img/eino/EfnOb1BnBoitoix6KjWcLkpQnbg.png
deleted file mode 100644
index 828881e3a95..00000000000
Binary files a/static/img/eino/EfnOb1BnBoitoix6KjWcLkpQnbg.png and /dev/null differ
diff --git a/static/img/eino/Fhm4bPmobohcQAxYPZtcFnJsn7f.png b/static/img/eino/Fhm4bPmobohcQAxYPZtcFnJsn7f.png
deleted file mode 100644
index ac83b08c97a..00000000000
Binary files a/static/img/eino/Fhm4bPmobohcQAxYPZtcFnJsn7f.png and /dev/null differ
diff --git a/static/img/eino/Gyj9bBP4koIXEQxll35c0EpRnFc.png b/static/img/eino/Gyj9bBP4koIXEQxll35c0EpRnFc.png
deleted file mode 100644
index ac83b08c97a..00000000000
Binary files a/static/img/eino/Gyj9bBP4koIXEQxll35c0EpRnFc.png and /dev/null differ
diff --git a/static/img/eino/H7vbbB8Igoi380x8TCXcDCWznFg.png b/static/img/eino/H7vbbB8Igoi380x8TCXcDCWznFg.png
deleted file mode 100644
index b1ee7ef4699..00000000000
Binary files a/static/img/eino/H7vbbB8Igoi380x8TCXcDCWznFg.png and /dev/null differ
diff --git a/static/img/eino/HHsNb3tdnorMeVxXEK3c686tnpz.png b/static/img/eino/HHsNb3tdnorMeVxXEK3c686tnpz.png
deleted file mode 100644
index 42edd8bd6a7..00000000000
Binary files a/static/img/eino/HHsNb3tdnorMeVxXEK3c686tnpz.png and /dev/null differ
diff --git a/static/img/eino/IwlPwET7lhEIznb88uGc3NxunAb.png b/static/img/eino/IwlPwET7lhEIznb88uGc3NxunAb.png
deleted file mode 100644
index 59867569452..00000000000
Binary files a/static/img/eino/IwlPwET7lhEIznb88uGc3NxunAb.png and /dev/null differ
diff --git a/static/img/eino/JMqswdPSah2dFcbX3qzcBxgLnQc.png b/static/img/eino/JMqswdPSah2dFcbX3qzcBxgLnQc.png
deleted file mode 100644
index 8eb3256e4b5..00000000000
Binary files a/static/img/eino/JMqswdPSah2dFcbX3qzcBxgLnQc.png and /dev/null differ
diff --git a/static/img/eino/JN89wZZo8h2LYybXZMTcXObgnUc.png b/static/img/eino/JN89wZZo8h2LYybXZMTcXObgnUc.png
deleted file mode 100644
index ee80daafd40..00000000000
Binary files a/static/img/eino/JN89wZZo8h2LYybXZMTcXObgnUc.png and /dev/null differ
diff --git a/static/img/eino/Jfz7bnL3SoPKPZxkO1Rci0hQnis.png b/static/img/eino/Jfz7bnL3SoPKPZxkO1Rci0hQnis.png
deleted file mode 100644
index e2cc5826d47..00000000000
Binary files a/static/img/eino/Jfz7bnL3SoPKPZxkO1Rci0hQnis.png and /dev/null differ
diff --git a/static/img/eino/LyR1wSzuBhi4rXbbnPJc0GYIngf.png b/static/img/eino/LyR1wSzuBhi4rXbbnPJc0GYIngf.png
deleted file mode 100644
index ff9458c4f31..00000000000
Binary files a/static/img/eino/LyR1wSzuBhi4rXbbnPJc0GYIngf.png and /dev/null differ
diff --git a/static/img/eino/NAdGw4BSUh2DOrbyq66cE1Zdnyg.png b/static/img/eino/NAdGw4BSUh2DOrbyq66cE1Zdnyg.png
deleted file mode 100644
index 2e4be7e1bb7..00000000000
Binary files a/static/img/eino/NAdGw4BSUh2DOrbyq66cE1Zdnyg.png and /dev/null differ
diff --git a/static/img/eino/NqU6wgF0ihxz3XbVCQdcz6m6nmh.png b/static/img/eino/NqU6wgF0ihxz3XbVCQdcz6m6nmh.png
deleted file mode 100644
index 6f194f45ff7..00000000000
Binary files a/static/img/eino/NqU6wgF0ihxz3XbVCQdcz6m6nmh.png and /dev/null differ
diff --git a/static/img/eino/QvP1wWE9RhdZLDbPYSlcBY9InWf.png b/static/img/eino/QvP1wWE9RhdZLDbPYSlcBY9InWf.png
deleted file mode 100644
index be500ef8451..00000000000
Binary files a/static/img/eino/QvP1wWE9RhdZLDbPYSlcBY9InWf.png and /dev/null differ
diff --git a/static/img/eino/SFfAbLJPiosHNMxx7zfcFD2mn1g.png b/static/img/eino/SFfAbLJPiosHNMxx7zfcFD2mn1g.png
deleted file mode 100644
index 46ba6cca870..00000000000
Binary files a/static/img/eino/SFfAbLJPiosHNMxx7zfcFD2mn1g.png and /dev/null differ
diff --git a/static/img/eino/UaTLwzyfRhWMLjbXhxCc1oTbnKb.png b/static/img/eino/UaTLwzyfRhWMLjbXhxCc1oTbnKb.png
deleted file mode 100644
index dda2011b9b7..00000000000
Binary files a/static/img/eino/UaTLwzyfRhWMLjbXhxCc1oTbnKb.png and /dev/null differ
diff --git a/static/img/eino/V3NwbrJrvoS4qdxyosscHDnon8b.png b/static/img/eino/V3NwbrJrvoS4qdxyosscHDnon8b.png
deleted file mode 100644
index 871d46a1268..00000000000
Binary files a/static/img/eino/V3NwbrJrvoS4qdxyosscHDnon8b.png and /dev/null differ
diff --git a/static/img/eino/WJ0eweFuvhq07nblYnvckVx0nzc.png b/static/img/eino/WJ0eweFuvhq07nblYnvckVx0nzc.png
deleted file mode 100644
index abd89973657..00000000000
Binary files a/static/img/eino/WJ0eweFuvhq07nblYnvckVx0nzc.png and /dev/null differ
diff --git a/static/img/eino/XigSb0WfsoYUN5xS4gNcjRranYc.png b/static/img/eino/XigSb0WfsoYUN5xS4gNcjRranYc.png
deleted file mode 100644
index ac83b08c97a..00000000000
Binary files a/static/img/eino/XigSb0WfsoYUN5xS4gNcjRranYc.png and /dev/null differ
diff --git a/static/img/eino/Y3fHwjKOyhYpd5boU95cEXMrnlb.png b/static/img/eino/Y3fHwjKOyhYpd5boU95cEXMrnlb.png
deleted file mode 100644
index c4f1b9c4be1..00000000000
Binary files a/static/img/eino/Y3fHwjKOyhYpd5boU95cEXMrnlb.png and /dev/null differ
diff --git a/static/img/eino/ZFATwEepAhUSXmbdxdYc2EWxnwh.png b/static/img/eino/ZFATwEepAhUSXmbdxdYc2EWxnwh.png
deleted file mode 100644
index 7d49f012391..00000000000
Binary files a/static/img/eino/ZFATwEepAhUSXmbdxdYc2EWxnwh.png and /dev/null differ
diff --git a/static/img/eino/ZRadbTP8jojvgxxjpJ4czaVnnfh.png b/static/img/eino/ZRadbTP8jojvgxxjpJ4czaVnnfh.png
deleted file mode 100644
index 98423c64251..00000000000
Binary files a/static/img/eino/ZRadbTP8jojvgxxjpJ4czaVnnfh.png and /dev/null differ
diff --git a/static/img/eino/ZiP5b3fivouCBbxCggYcWHqanRv.png b/static/img/eino/ZiP5b3fivouCBbxCggYcWHqanRv.png
deleted file mode 100644
index f4649d95ed5..00000000000
Binary files a/static/img/eino/ZiP5b3fivouCBbxCggYcWHqanRv.png and /dev/null differ
diff --git a/static/img/eino/agent_as_tool_outline.png b/static/img/eino/agent_as_tool_outline.png
deleted file mode 100644
index fd9a642c65a..00000000000
Binary files a/static/img/eino/agent_as_tool_outline.png and /dev/null differ
diff --git a/static/img/eino/big_challenge_graph.png b/static/img/eino/big_challenge_graph.png
deleted file mode 100644
index 1712011d255..00000000000
Binary files a/static/img/eino/big_challenge_graph.png and /dev/null differ
diff --git a/static/img/eino/chain_append_branch.png b/static/img/eino/chain_append_branch.png
deleted file mode 100644
index 890af83b7bb..00000000000
Binary files a/static/img/eino/chain_append_branch.png and /dev/null differ
diff --git a/static/img/eino/chain_append_parallel.png b/static/img/eino/chain_append_parallel.png
deleted file mode 100644
index 8e8ac4224e7..00000000000
Binary files a/static/img/eino/chain_append_parallel.png and /dev/null differ
diff --git a/static/img/eino/difference_when_enable_stream.png b/static/img/eino/difference_when_enable_stream.png
deleted file mode 100644
index 7025e8b4a65..00000000000
Binary files a/static/img/eino/difference_when_enable_stream.png and /dev/null differ
diff --git a/static/img/eino/edge_of_parallel.png b/static/img/eino/edge_of_parallel.png
deleted file mode 100644
index 199b484ad36..00000000000
Binary files a/static/img/eino/edge_of_parallel.png and /dev/null differ
diff --git a/static/img/eino/IHwSwc4uHhqHZDbUuzvc679OnRd.png b/static/img/eino/eino_adk_agent_as_tool.png
similarity index 100%
rename from static/img/eino/IHwSwc4uHhqHZDbUuzvc679OnRd.png
rename to static/img/eino/eino_adk_agent_as_tool.png
diff --git a/static/img/eino/SnmTwoOFGh2ZMHbDVoNcH2eInsQ.png b/static/img/eino/eino_adk_agent_as_tool_case.png
similarity index 100%
rename from static/img/eino/SnmTwoOFGh2ZMHbDVoNcH2eInsQ.png
rename to static/img/eino/eino_adk_agent_as_tool_case.png
diff --git a/static/img/eino/TAsuwnewYheUVqbnWSKcmR6fnNd.png b/static/img/eino/eino_adk_agent_as_tool_sequence_diagram_1.png
similarity index 100%
rename from static/img/eino/TAsuwnewYheUVqbnWSKcmR6fnNd.png
rename to static/img/eino/eino_adk_agent_as_tool_sequence_diagram_1.png
diff --git a/static/img/eino/eino_adk_architecture.png b/static/img/eino/eino_adk_architecture.png
deleted file mode 100644
index ff2844027f2..00000000000
Binary files a/static/img/eino/eino_adk_architecture.png and /dev/null differ
diff --git a/static/img/eino/NSX1w1ZJghC4f8bmyfeczj0lnGb.png b/static/img/eino/eino_adk_chat_model_agent_view.png
similarity index 100%
rename from static/img/eino/NSX1w1ZJghC4f8bmyfeczj0lnGb.png
rename to static/img/eino/eino_adk_chat_model_agent_view.png
diff --git a/static/img/eino/RZONwQH7shiID5bGLolcQFBlncd.png b/static/img/eino/eino_adk_chatmodel_agent.png
similarity index 100%
rename from static/img/eino/RZONwQH7shiID5bGLolcQFBlncd.png
rename to static/img/eino/eino_adk_chatmodel_agent.png
diff --git a/static/img/eino/FB2FwX1S5hFciJbIGDFcAWIdn1p.png b/static/img/eino/eino_adk_collaboration_example.png
similarity index 100%
rename from static/img/eino/FB2FwX1S5hFciJbIGDFcAWIdn1p.png
rename to static/img/eino/eino_adk_collaboration_example.png
diff --git a/static/img/eino/Syzww9Z2khV7uvbFzBnc2zchnGd.png b/static/img/eino/eino_adk_collaboration_run_path_sequential.png
similarity index 100%
rename from static/img/eino/Syzww9Z2khV7uvbFzBnc2zchnGd.png
rename to static/img/eino/eino_adk_collaboration_run_path_sequential.png
diff --git a/static/img/eino/JcXAbPMIjodRxPxfBEtcEztQnrb.png b/static/img/eino/eino_adk_deep_agent_definition.png
similarity index 100%
rename from static/img/eino/JcXAbPMIjodRxPxfBEtcEztQnrb.png
rename to static/img/eino/eino_adk_deep_agent_definition.png
diff --git a/static/img/eino/eino_adk_deep_agents_overview.png b/static/img/eino/eino_adk_deep_agents_overview.png
new file mode 100644
index 00000000000..a77a837b8ee
Binary files /dev/null and b/static/img/eino/eino_adk_deep_agents_overview.png differ
diff --git a/static/img/eino/JFl7wI6gAhAS1ibi0IucZIKXnzh.png b/static/img/eino/eino_adk_deterministic_transfer.png
similarity index 100%
rename from static/img/eino/JFl7wI6gAhAS1ibi0IucZIKXnzh.png
rename to static/img/eino/eino_adk_deterministic_transfer.png
diff --git a/static/img/eino/DIJjweyWRh25ynbJWE3crGJdnne.png b/static/img/eino/eino_adk_directory_structure.png
similarity index 100%
rename from static/img/eino/DIJjweyWRh25ynbJWE3crGJdnne.png
rename to static/img/eino/eino_adk_directory_structure.png
diff --git a/static/img/eino/JTjGwKheGhTZrhbCIgZciClgnab.png b/static/img/eino/eino_adk_excel_agent_architecture.png
similarity index 100%
rename from static/img/eino/JTjGwKheGhTZrhbCIgZciClgnab.png
rename to static/img/eino/eino_adk_excel_agent_architecture.png
diff --git a/static/img/eino/C7J2wMDQNhDCHfbPYsIcgw8Yn9g.png b/static/img/eino/eino_adk_excel_agent_complete.png
similarity index 100%
rename from static/img/eino/C7J2wMDQNhDCHfbPYsIcgw8Yn9g.png
rename to static/img/eino/eino_adk_excel_agent_complete.png
diff --git a/static/img/eino/LuyNwCiyHhoL4Wb48XHcsHS6nrf.png b/static/img/eino/eino_adk_excel_agent_sequential.png
similarity index 100%
rename from static/img/eino/LuyNwCiyHhoL4Wb48XHcsHS6nrf.png
rename to static/img/eino/eino_adk_excel_agent_sequential.png
diff --git a/static/img/eino/RVsgbX5Y0oQtUDxhDGzc0hAbnFc.png b/static/img/eino/eino_adk_excel_agent_user_group.png
similarity index 100%
rename from static/img/eino/RVsgbX5Y0oQtUDxhDGzc0hAbnFc.png
rename to static/img/eino/eino_adk_excel_agent_user_group.png
diff --git a/static/img/eino/eino_adk_excel_chat_model_agent_view.png b/static/img/eino/eino_adk_excel_chat_model_agent_view.png
new file mode 100644
index 00000000000..4480f271c5c
Binary files /dev/null and b/static/img/eino/eino_adk_excel_chat_model_agent_view.png differ
diff --git a/static/img/eino/ActebkYfUo7bCwxPk8ocOLEOnsf.png b/static/img/eino/eino_adk_excel_directory.png
similarity index 100%
rename from static/img/eino/ActebkYfUo7bCwxPk8ocOLEOnsf.png
rename to static/img/eino/eino_adk_excel_directory.png
diff --git a/static/img/eino/Kgoew8X0RhWDvtbBZqnc1WWlnie.png b/static/img/eino/eino_adk_excel_transfer.png
similarity index 100%
rename from static/img/eino/Kgoew8X0RhWDvtbBZqnc1WWlnie.png
rename to static/img/eino/eino_adk_excel_transfer.png
diff --git a/static/img/eino/NkWDbNKykog8nQxmGu3crnzsnkB.png b/static/img/eino/eino_adk_excel_using_deep.png
similarity index 100%
rename from static/img/eino/NkWDbNKykog8nQxmGu3crnzsnkB.png
rename to static/img/eino/eino_adk_excel_using_deep.png
diff --git a/static/img/eino/HyrlwkG2Bh2tAfbDPRUcJjoXnje.png b/static/img/eino/eino_adk_history.png
similarity index 100%
rename from static/img/eino/HyrlwkG2Bh2tAfbDPRUcJjoXnje.png
rename to static/img/eino/eino_adk_history.png
diff --git a/static/img/eino/L1jTwKR8WhZyEUbqQpKcgaJBnbh.png b/static/img/eino/eino_adk_implementation_nested_loop_sequential.png
similarity index 100%
rename from static/img/eino/L1jTwKR8WhZyEUbqQpKcgaJBnbh.png
rename to static/img/eino/eino_adk_implementation_nested_loop_sequential.png
diff --git a/static/img/eino/BeADw7qRvhynofbHnBvcJJwWnrc.png b/static/img/eino/eino_adk_loop_agent.png
similarity index 100%
rename from static/img/eino/BeADw7qRvhynofbHnBvcJJwWnrc.png
rename to static/img/eino/eino_adk_loop_agent.png
diff --git a/static/img/eino/EetbwO6wIh1YCnbylPOcQXPmnaf.png b/static/img/eino/eino_adk_loop_agent_max_iterations_example.png
similarity index 100%
rename from static/img/eino/EetbwO6wIh1YCnbylPOcQXPmnaf.png
rename to static/img/eino/eino_adk_loop_agent_max_iterations_example.png
diff --git a/static/img/eino/KxH0wlxpfhRWGfbhUMecv0O1n9f.png b/static/img/eino/eino_adk_loop_controller.png
similarity index 100%
rename from static/img/eino/KxH0wlxpfhRWGfbhUMecv0O1n9f.png
rename to static/img/eino/eino_adk_loop_controller.png
diff --git a/static/img/eino/FUn8wE2HVhsVkWbb1szc8JFDntd.png b/static/img/eino/eino_adk_loop_definition.png
similarity index 100%
rename from static/img/eino/FUn8wE2HVhsVkWbb1szc8JFDntd.png
rename to static/img/eino/eino_adk_loop_definition.png
diff --git a/static/img/eino/JCOUw5vAJhghRPbakhec9wolnfc.png b/static/img/eino/eino_adk_loop_exit.png
similarity index 100%
rename from static/img/eino/JCOUw5vAJhghRPbakhec9wolnfc.png
rename to static/img/eino/eino_adk_loop_exit.png
diff --git a/static/img/eino/XwnMwCmNph3U7ib9OFQcqYgdnjg.png b/static/img/eino/eino_adk_message_event.png
similarity index 100%
rename from static/img/eino/XwnMwCmNph3U7ib9OFQcqYgdnjg.png
rename to static/img/eino/eino_adk_message_event.png
diff --git a/static/img/eino/P98FwB163hUCwebgu6gcyZLvnSe.png b/static/img/eino/eino_adk_module_architecture.png
similarity index 100%
rename from static/img/eino/P98FwB163hUCwebgu6gcyZLvnSe.png
rename to static/img/eino/eino_adk_module_architecture.png
diff --git a/static/img/eino/MnhMwjIsnhb9WsbSvFHcz3VqnWd.png b/static/img/eino/eino_adk_overview_sequential.png
similarity index 100%
rename from static/img/eino/MnhMwjIsnhb9WsbSvFHcz3VqnWd.png
rename to static/img/eino/eino_adk_overview_sequential.png
diff --git a/static/img/eino/FpLow6LeihMbFGbOVrlcWyAqnif.png b/static/img/eino/eino_adk_parallel.png
similarity index 100%
rename from static/img/eino/FpLow6LeihMbFGbOVrlcWyAqnif.png
rename to static/img/eino/eino_adk_parallel.png
diff --git a/static/img/eino/BmQywuBIshwKKGbb8Lzc313RnUg.png b/static/img/eino/eino_adk_parallel_agent.png
similarity index 100%
rename from static/img/eino/BmQywuBIshwKKGbb8Lzc313RnUg.png
rename to static/img/eino/eino_adk_parallel_agent.png
diff --git a/static/img/eino/S1yawPJPuhMO6Ib95ircVg88nHg.png b/static/img/eino/eino_adk_parallel_controller_overview.png
similarity index 100%
rename from static/img/eino/S1yawPJPuhMO6Ib95ircVg88nHg.png
rename to static/img/eino/eino_adk_parallel_controller_overview.png
diff --git a/static/img/eino/IyblwV7Y8hilJKbHYgHcdfxlnre.png b/static/img/eino/eino_adk_parallel_definition.png
similarity index 100%
rename from static/img/eino/IyblwV7Y8hilJKbHYgHcdfxlnre.png
rename to static/img/eino/eino_adk_parallel_definition.png
diff --git a/static/img/eino/O6ezw1UfVh4jUFbTAPTcvaCzn0g.png b/static/img/eino/eino_adk_parallel_use_case.png
similarity index 100%
rename from static/img/eino/O6ezw1UfVh4jUFbTAPTcvaCzn0g.png
rename to static/img/eino/eino_adk_parallel_use_case.png
diff --git a/static/img/eino/UL4Zw6Rj7hXg2rbNHLAcPhZOnrn.png b/static/img/eino/eino_adk_parallel_yet_another_2.png
similarity index 100%
rename from static/img/eino/UL4Zw6Rj7hXg2rbNHLAcPhZOnrn.png
rename to static/img/eino/eino_adk_parallel_yet_another_2.png
diff --git a/static/img/eino/Bd5xwDLkrhR7vRbwVgpctWnHnkJ.png b/static/img/eino/eino_adk_plan_execute_replan.png
similarity index 100%
rename from static/img/eino/Bd5xwDLkrhR7vRbwVgpctWnHnkJ.png
rename to static/img/eino/eino_adk_plan_execute_replan.png
diff --git a/static/img/eino/eino_adk_plan_execute_replan_architecture_overview.png b/static/img/eino/eino_adk_plan_execute_replan_architecture_overview.png
new file mode 100644
index 00000000000..e3ec4fb4034
Binary files /dev/null and b/static/img/eino/eino_adk_plan_execute_replan_architecture_overview.png differ
diff --git a/static/img/eino/OF0JwDhHwhUQc9bpa9jc4ZK0nGg.png b/static/img/eino/eino_adk_plan_execute_replan_detail.png
similarity index 100%
rename from static/img/eino/OF0JwDhHwhUQc9bpa9jc4ZK0nGg.png
rename to static/img/eino/eino_adk_plan_execute_replan_detail.png
diff --git a/static/img/eino/SSmjw7ooshk8rybm87XchHPtn7K.png b/static/img/eino/eino_adk_plan_execute_replan_session.png
similarity index 100%
rename from static/img/eino/SSmjw7ooshk8rybm87XchHPtn7K.png
rename to static/img/eino/eino_adk_plan_execute_replan_session.png
diff --git a/static/img/eino/SjhmwZUNzhch0rb8SdUcwotpnaf.png b/static/img/eino/eino_adk_plan_execute_steps.png
similarity index 100%
rename from static/img/eino/SjhmwZUNzhch0rb8SdUcwotpnaf.png
rename to static/img/eino/eino_adk_plan_execute_steps.png
diff --git a/static/img/eino/PSFuwhsHJhYkGDb8S45cDdcxnxf.png b/static/img/eino/eino_adk_preview_tree.png
similarity index 100%
rename from static/img/eino/PSFuwhsHJhYkGDb8S45cDdcxnxf.png
rename to static/img/eino/eino_adk_preview_tree.png
diff --git a/static/img/eino/IhrFw8gxShD1gabCYIScuypjnVc.png b/static/img/eino/eino_adk_project_manager.png
similarity index 100%
rename from static/img/eino/IhrFw8gxShD1gabCYIScuypjnVc.png
rename to static/img/eino/eino_adk_project_manager.png
diff --git a/static/img/eino/KWOJwXt40hnDvEbjGFzcgA8BnIe.png b/static/img/eino/eino_adk_quick_start_agent_types.png
similarity index 100%
rename from static/img/eino/KWOJwXt40hnDvEbjGFzcgA8BnIe.png
rename to static/img/eino/eino_adk_quick_start_agent_types.png
diff --git a/static/img/eino/SviabfhOUoGC1TxrucAckWkmnqg.png b/static/img/eino/eino_adk_react_pattern.png
similarity index 100%
rename from static/img/eino/SviabfhOUoGC1TxrucAckWkmnqg.png
rename to static/img/eino/eino_adk_react_pattern.png
diff --git a/static/img/eino/IHBYwzKJahvPOdbRdX8cpTBSnde.png b/static/img/eino/eino_adk_run_path.png
similarity index 100%
rename from static/img/eino/IHBYwzKJahvPOdbRdX8cpTBSnde.png
rename to static/img/eino/eino_adk_run_path.png
diff --git a/static/img/eino/Kox4wVhSjhkBXEbDIqSciZRHnvb.png b/static/img/eino/eino_adk_run_path_deterministic.png
similarity index 100%
rename from static/img/eino/Kox4wVhSjhkBXEbDIqSciZRHnvb.png
rename to static/img/eino/eino_adk_run_path_deterministic.png
diff --git a/static/img/eino/NmeTwgv9Ph15mhbxi5KcGSxKnvL.png b/static/img/eino/eino_adk_run_path_sub_agent.png
similarity index 100%
rename from static/img/eino/NmeTwgv9Ph15mhbxi5KcGSxKnvL.png
rename to static/img/eino/eino_adk_run_path_sub_agent.png
diff --git a/static/img/eino/YT3hwDq4ahfn01bxjm8cbsCdnpq.png b/static/img/eino/eino_adk_self_driving.png
similarity index 100%
rename from static/img/eino/YT3hwDq4ahfn01bxjm8cbsCdnpq.png
rename to static/img/eino/eino_adk_self_driving.png
diff --git a/static/img/eino/PprGwUBK7hoPR4bZIDDcF8vwnQg.png b/static/img/eino/eino_adk_sequence_diagram.png
similarity index 100%
rename from static/img/eino/PprGwUBK7hoPR4bZIDDcF8vwnQg.png
rename to static/img/eino/eino_adk_sequence_diagram.png
diff --git a/static/img/eino/eino_adk_sequential.png b/static/img/eino/eino_adk_sequential.png
new file mode 100644
index 00000000000..ec96a47852c
Binary files /dev/null and b/static/img/eino/eino_adk_sequential.png differ
diff --git a/static/img/eino/BONAwG4YGhsXp2b3BXWcLXnGnPh.png b/static/img/eino/eino_adk_sequential_agent.png
similarity index 100%
rename from static/img/eino/BONAwG4YGhsXp2b3BXWcLXnGnPh.png
rename to static/img/eino/eino_adk_sequential_agent.png
diff --git a/static/img/eino/JYoHwKhfQhRmYZb6jEDcy1ofnVe.png b/static/img/eino/eino_adk_sequential_controller.png
similarity index 100%
rename from static/img/eino/JYoHwKhfQhRmYZb6jEDcy1ofnVe.png
rename to static/img/eino/eino_adk_sequential_controller.png
diff --git a/static/img/eino/FrwxwAnJGhUVnvb1n05cA7N8n2e.png b/static/img/eino/eino_adk_sequential_definition.png
similarity index 100%
rename from static/img/eino/FrwxwAnJGhUVnvb1n05cA7N8n2e.png
rename to static/img/eino/eino_adk_sequential_definition.png
diff --git a/static/img/eino/H0hbwjsHmhkQKobDBwLck70Gnte.png b/static/img/eino/eino_adk_sequential_quickstart.png
similarity index 100%
rename from static/img/eino/H0hbwjsHmhkQKobDBwLck70Gnte.png
rename to static/img/eino/eino_adk_sequential_quickstart.png
diff --git a/static/img/eino/eino_adk_sequential_with_loop.png b/static/img/eino/eino_adk_sequential_with_loop.png
new file mode 100644
index 00000000000..71feae7155d
Binary files /dev/null and b/static/img/eino/eino_adk_sequential_with_loop.png differ
diff --git a/static/img/eino/DRSQw67dlhjtW9bOEUqcHirtn6e.png b/static/img/eino/eino_adk_streaming.png
similarity index 100%
rename from static/img/eino/DRSQw67dlhjtW9bOEUqcHirtn6e.png
rename to static/img/eino/eino_adk_streaming.png
diff --git a/static/img/eino/AgV7wB9hohnlNwbRTMMcSSxsnnf.png b/static/img/eino/eino_adk_supervisor.png
similarity index 100%
rename from static/img/eino/AgV7wB9hohnlNwbRTMMcSSxsnnf.png
rename to static/img/eino/eino_adk_supervisor.png
diff --git a/static/img/eino/FNtXwQ05ahvjP4bfyK4cK61ynpe.png b/static/img/eino/eino_adk_supervisor_definition.png
similarity index 100%
rename from static/img/eino/FNtXwQ05ahvjP4bfyK4cK61ynpe.png
rename to static/img/eino/eino_adk_supervisor_definition.png
diff --git a/static/img/eino/CwpiwGzBSh7HV1bQtJBcQ8brnHf.png b/static/img/eino/eino_adk_supervisor_example.png
similarity index 100%
rename from static/img/eino/CwpiwGzBSh7HV1bQtJBcQ8brnHf.png
rename to static/img/eino/eino_adk_supervisor_example.png
diff --git a/static/img/eino/JVo8wOsuZhcf38b5ljHcJmGYnDc.png b/static/img/eino/eino_adk_supervisor_flow.png
similarity index 100%
rename from static/img/eino/JVo8wOsuZhcf38b5ljHcJmGYnDc.png
rename to static/img/eino/eino_adk_supervisor_flow.png
diff --git a/static/img/eino/R2CBwqMnbhjTQJbOAJQcyojLnIe.png b/static/img/eino/eino_adk_transfer.png
similarity index 100%
rename from static/img/eino/R2CBwqMnbhjTQJbOAJQcyojLnIe.png
rename to static/img/eino/eino_adk_transfer.png
diff --git a/static/img/eino/Wt3Nbd2t4oMlUtxKw8hct36EnKb.png b/static/img/eino/eino_adk_user_group.png
similarity index 100%
rename from static/img/eino/Wt3Nbd2t4oMlUtxKw8hct36EnKb.png
rename to static/img/eino/eino_adk_user_group.png
diff --git a/static/img/eino/T0StwIywMhjI4HbwCOcc847jn3e.png b/static/img/eino/eino_adk_why_excel_plan_executor.png
similarity index 100%
rename from static/img/eino/T0StwIywMhjI4HbwCOcc847jn3e.png
rename to static/img/eino/eino_adk_why_excel_plan_executor.png
diff --git a/static/img/eino/H04SbFvATod8oRx3VVXcxhhRnKf.png b/static/img/eino/eino_adk_write_todos.png
similarity index 100%
rename from static/img/eino/H04SbFvATod8oRx3VVXcxhhRnKf.png
rename to static/img/eino/eino_adk_write_todos.png
diff --git a/static/img/eino/ZyUJwOrovhipoKbeBIQcrviJn9c.png b/static/img/eino/eino_adk_yet_another_loop.png
similarity index 100%
rename from static/img/eino/ZyUJwOrovhipoKbeBIQcrviJn9c.png
rename to static/img/eino/eino_adk_yet_another_loop.png
diff --git a/static/img/eino/ZAlewk2iWhP5yxbieEkchYTVnWd.png b/static/img/eino/eino_adk_yet_another_parallel.png
similarity index 100%
rename from static/img/eino/ZAlewk2iWhP5yxbieEkchYTVnWd.png
rename to static/img/eino/eino_adk_yet_another_parallel.png
diff --git a/static/img/eino/eino_ai_app_agent_as_tool.png b/static/img/eino/eino_ai_app_agent_as_tool.png
deleted file mode 100644
index 5ae6c5aaaf6..00000000000
Binary files a/static/img/eino/eino_ai_app_agent_as_tool.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_agent_node.png b/static/img/eino/eino_ai_app_agent_node.png
deleted file mode 100644
index d0eb3522d88..00000000000
Binary files a/static/img/eino/eino_ai_app_agent_node.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_chain.png b/static/img/eino/eino_ai_app_chain.png
deleted file mode 100644
index fbffa128383..00000000000
Binary files a/static/img/eino/eino_ai_app_chain.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_concept.png b/static/img/eino/eino_ai_app_concept.png
deleted file mode 100644
index a84e8271183..00000000000
Binary files a/static/img/eino/eino_ai_app_concept.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_deliverable.png b/static/img/eino/eino_ai_app_deliverable.png
deleted file mode 100644
index a388c0d4423..00000000000
Binary files a/static/img/eino/eino_ai_app_deliverable.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_form_mermaid.png b/static/img/eino/eino_ai_app_form_mermaid.png
deleted file mode 100644
index d9efb22d578..00000000000
Binary files a/static/img/eino/eino_ai_app_form_mermaid.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_graph_tool.png b/static/img/eino/eino_ai_app_graph_tool.png
deleted file mode 100644
index 64145e4b5d4..00000000000
Binary files a/static/img/eino/eino_ai_app_graph_tool.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_prebuilt.png b/static/img/eino/eino_ai_app_prebuilt.png
deleted file mode 100644
index fc62c012bf7..00000000000
Binary files a/static/img/eino/eino_ai_app_prebuilt.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_run_form.png b/static/img/eino/eino_ai_app_run_form.png
deleted file mode 100644
index d65f94558c4..00000000000
Binary files a/static/img/eino/eino_ai_app_run_form.png and /dev/null differ
diff --git a/static/img/eino/eino_ai_app_transfer.png b/static/img/eino/eino_ai_app_transfer.png
deleted file mode 100644
index c36b52cfcb5..00000000000
Binary files a/static/img/eino/eino_ai_app_transfer.png and /dev/null differ
diff --git a/static/img/eino/IXAZwjLtWhrtoBbHf7HcoveonIf.png b/static/img/eino/eino_architecture_overview.png
similarity index 100%
rename from static/img/eino/IXAZwjLtWhrtoBbHf7HcoveonIf.png
rename to static/img/eino/eino_architecture_overview.png
diff --git a/static/img/eino/eino_callback_apmplus_session1.png b/static/img/eino/eino_callback_apmplus_session1.png
deleted file mode 100644
index 52e42958782..00000000000
Binary files a/static/img/eino/eino_callback_apmplus_session1.png and /dev/null differ
diff --git a/static/img/eino/eino_callback_apmplus_session2.png b/static/img/eino/eino_callback_apmplus_session2.png
deleted file mode 100644
index 9b0df942fc0..00000000000
Binary files a/static/img/eino/eino_callback_apmplus_session2.png and /dev/null differ
diff --git a/static/img/eino/X8eJw5cbLhpAmDbOuSiccRApnwg.png b/static/img/eino/eino_collaboration_agent_as_tool_thumbnail.png
similarity index 100%
rename from static/img/eino/X8eJw5cbLhpAmDbOuSiccRApnwg.png
rename to static/img/eino/eino_collaboration_agent_as_tool_thumbnail.png
diff --git a/static/img/eino/eino_component_runnable.png b/static/img/eino/eino_component_runnable.png
index 2750acec4ca..192c92227d1 100644
Binary files a/static/img/eino/eino_component_runnable.png and b/static/img/eino/eino_component_runnable.png differ
diff --git a/static/img/eino/eino_debug_enter_config_page.png b/static/img/eino/eino_debug_enter_config_page.png
index 37d3b316a2b..cd6f7053faf 100644
Binary files a/static/img/eino/eino_debug_enter_config_page.png and b/static/img/eino/eino_debug_enter_config_page.png differ
diff --git a/static/img/eino/eino_debug_ide_manage_plguin_button.png b/static/img/eino/eino_debug_ide_manage_plguin_button.png
deleted file mode 100644
index bb2f44d1035..00000000000
Binary files a/static/img/eino/eino_debug_ide_manage_plguin_button.png and /dev/null differ
diff --git a/static/img/eino/eino_debug_ide_marketplace_button.png b/static/img/eino/eino_debug_ide_marketplace_button.png
deleted file mode 100644
index e2cc5826d47..00000000000
Binary files a/static/img/eino/eino_debug_ide_marketplace_button.png and /dev/null differ
diff --git a/static/img/eino/eino_debug_ide_settings_button.png b/static/img/eino/eino_debug_ide_settings_button.png
deleted file mode 100644
index 6484dc2c957..00000000000
Binary files a/static/img/eino/eino_debug_ide_settings_button.png and /dev/null differ
diff --git a/static/img/eino/eino_dev_ability_introduction_page.png b/static/img/eino/eino_dev_ability_introduction_page.png
index 0d3f005e0c5..2b2be6fa54d 100644
Binary files a/static/img/eino/eino_dev_ability_introduction_page.png and b/static/img/eino/eino_dev_ability_introduction_page.png differ
diff --git a/static/img/eino/eino_dev_enter_page.png b/static/img/eino/eino_dev_enter_page.png
deleted file mode 100644
index 98423c64251..00000000000
Binary files a/static/img/eino/eino_dev_enter_page.png and /dev/null differ
diff --git a/static/img/eino/OiF9b24QNocuMLxSrZ2c9PkAnzf.png b/static/img/eino/eino_dev_ide_plugin_guide_1.png
similarity index 100%
rename from static/img/eino/OiF9b24QNocuMLxSrZ2c9PkAnzf.png
rename to static/img/eino/eino_dev_ide_plugin_guide_1.png
diff --git a/static/img/eino/WuuYbKYoHo6slixKBLbcPSf6n8f.png b/static/img/eino/eino_dev_ide_plugin_guide_2.png
similarity index 100%
rename from static/img/eino/WuuYbKYoHo6slixKBLbcPSf6n8f.png
rename to static/img/eino/eino_dev_ide_plugin_guide_2.png
diff --git a/static/img/eino/JKDUbMAnDoZa4TxUXafcCXO4nqg.png b/static/img/eino/eino_dev_plugin_vscode.png
similarity index 100%
rename from static/img/eino/JKDUbMAnDoZa4TxUXafcCXO4nqg.png
rename to static/img/eino/eino_dev_plugin_vscode.png
diff --git a/static/img/eino/eino_install_page.png b/static/img/eino/eino_install_page.png
index dc7fe0cb602..97ceda154f5 100644
Binary files a/static/img/eino/eino_install_page.png and b/static/img/eino/eino_install_page.png differ
diff --git a/static/img/eino/N1UgbTQFioeqPaxeL9XcWNqGn5g.png b/static/img/eino/eino_mcp_prompt.png
similarity index 100%
rename from static/img/eino/N1UgbTQFioeqPaxeL9XcWNqGn5g.png
rename to static/img/eino/eino_mcp_prompt.png
diff --git a/static/img/eino/HotzbOFL6oZFQWxWP10caT9Wnuc.png b/static/img/eino/eino_mcp_tool_architecture.png
similarity index 100%
rename from static/img/eino/HotzbOFL6oZFQWxWP10caT9Wnuc.png
rename to static/img/eino/eino_mcp_tool_architecture.png
diff --git a/static/img/eino/eino_orchestration_gencode_config_page.png b/static/img/eino/eino_orchestration_gencode_config_page.png
deleted file mode 100644
index ee28677fb28..00000000000
Binary files a/static/img/eino/eino_orchestration_gencode_config_page.png and /dev/null differ
diff --git a/static/img/eino/eino_practice_agent_graph.png b/static/img/eino/eino_practice_agent_graph.png
index d92104f2988..2a8cd827d55 100644
Binary files a/static/img/eino/eino_practice_agent_graph.png and b/static/img/eino/eino_practice_agent_graph.png differ
diff --git a/static/img/eino/eino_process.png b/static/img/eino/eino_process.png
deleted file mode 100644
index 32183097dcd..00000000000
Binary files a/static/img/eino/eino_process.png and /dev/null differ
diff --git a/static/img/eino/eino_projects_and_structure.png b/static/img/eino/eino_projects_and_structure.png
deleted file mode 100644
index 7948bd3242d..00000000000
Binary files a/static/img/eino/eino_projects_and_structure.png and /dev/null differ
diff --git a/static/img/eino/eino_structure.png b/static/img/eino/eino_structure.png
index c48776d6ffd..7948bd3242d 100644
Binary files a/static/img/eino/eino_structure.png and b/static/img/eino/eino_structure.png differ
diff --git a/static/img/eino/en_chain_abstract_perspective.png b/static/img/eino/en_chain_abstract_perspective.png
deleted file mode 100644
index 7e7ae0140ea..00000000000
Binary files a/static/img/eino/en_chain_abstract_perspective.png and /dev/null differ
diff --git a/static/img/eino/en_chain_inner_nodes.png b/static/img/eino/en_chain_inner_nodes.png
deleted file mode 100644
index d8d7d335ca5..00000000000
Binary files a/static/img/eino/en_chain_inner_nodes.png and /dev/null differ
diff --git a/static/img/eino/en_eino_agent_graph.png b/static/img/eino/en_eino_agent_graph.png
deleted file mode 100644
index abd89973657..00000000000
Binary files a/static/img/eino/en_eino_agent_graph.png and /dev/null differ
diff --git a/static/img/eino/en_eino_branch_one_way.png b/static/img/eino/en_eino_branch_one_way.png
deleted file mode 100644
index f7313b52e6c..00000000000
Binary files a/static/img/eino/en_eino_branch_one_way.png and /dev/null differ
diff --git a/static/img/eino/en_eino_branch_select.png b/static/img/eino/en_eino_branch_select.png
deleted file mode 100644
index bf4dac72b2e..00000000000
Binary files a/static/img/eino/en_eino_branch_select.png and /dev/null differ
diff --git a/static/img/eino/en_eino_callbacks.png b/static/img/eino/en_eino_callbacks.png
deleted file mode 100644
index 48af30cde34..00000000000
Binary files a/static/img/eino/en_eino_callbacks.png and /dev/null differ
diff --git a/static/img/eino/en_eino_chain_nodes.png b/static/img/eino/en_eino_chain_nodes.png
deleted file mode 100644
index 1042dc0bcf3..00000000000
Binary files a/static/img/eino/en_eino_chain_nodes.png and /dev/null differ
diff --git a/static/img/eino/en_eino_chat.png b/static/img/eino/en_eino_chat.png
deleted file mode 100644
index 5cd73ca1c17..00000000000
Binary files a/static/img/eino/en_eino_chat.png and /dev/null differ
diff --git a/static/img/eino/en_eino_debug_plugin.png b/static/img/eino/en_eino_debug_plugin.png
deleted file mode 100644
index c16b9927702..00000000000
Binary files a/static/img/eino/en_eino_debug_plugin.png and /dev/null differ
diff --git a/static/img/eino/en_eino_graph_2.png b/static/img/eino/en_eino_graph_2.png
deleted file mode 100644
index 8bcd82eefb0..00000000000
Binary files a/static/img/eino/en_eino_graph_2.png and /dev/null differ
diff --git a/static/img/eino/en_eino_graph_compile.gif b/static/img/eino/en_eino_graph_compile.gif
deleted file mode 100644
index 6c5d8d1ed1c..00000000000
Binary files a/static/img/eino/en_eino_graph_compile.gif and /dev/null differ
diff --git a/static/img/eino/en_eino_graph_node_type.png b/static/img/eino/en_eino_graph_node_type.png
deleted file mode 100644
index 2b53ceaa0ca..00000000000
Binary files a/static/img/eino/en_eino_graph_node_type.png and /dev/null differ
diff --git a/static/img/eino/en_eino_graph_parallel_node_type.png b/static/img/eino/en_eino_graph_parallel_node_type.png
deleted file mode 100644
index 5a392c4998e..00000000000
Binary files a/static/img/eino/en_eino_graph_parallel_node_type.png and /dev/null differ
diff --git a/static/img/eino/en_eino_internal_invoke_outter_transform.png b/static/img/eino/en_eino_internal_invoke_outter_transform.png
deleted file mode 100644
index 87bb0677f5c..00000000000
Binary files a/static/img/eino/en_eino_internal_invoke_outter_transform.png and /dev/null differ
diff --git a/static/img/eino/en_eino_internal_stream_outter_invoke.png b/static/img/eino/en_eino_internal_stream_outter_invoke.png
deleted file mode 100644
index 4de5fbc9f9f..00000000000
Binary files a/static/img/eino/en_eino_internal_stream_outter_invoke.png and /dev/null differ
diff --git a/static/img/eino/en_eino_internal_transform_outter_invoke.png b/static/img/eino/en_eino_internal_transform_outter_invoke.png
deleted file mode 100644
index c6d101ffffe..00000000000
Binary files a/static/img/eino/en_eino_internal_transform_outter_invoke.png and /dev/null differ
diff --git a/static/img/eino/en_eino_listen_apply_act.png b/static/img/eino/en_eino_listen_apply_act.png
deleted file mode 100644
index 08e392ce473..00000000000
Binary files a/static/img/eino/en_eino_listen_apply_act.png and /dev/null differ
diff --git a/static/img/eino/en_eino_not_recommend_of_biz.png b/static/img/eino/en_eino_not_recommend_of_biz.png
deleted file mode 100644
index 1dea0a2b1c3..00000000000
Binary files a/static/img/eino/en_eino_not_recommend_of_biz.png and /dev/null differ
diff --git a/static/img/eino/en_eino_parallel.png b/static/img/eino/en_eino_parallel.png
deleted file mode 100644
index 1857fa7b0ad..00000000000
Binary files a/static/img/eino/en_eino_parallel.png and /dev/null differ
diff --git a/static/img/eino/en_eino_parallel_edge.png b/static/img/eino/en_eino_parallel_edge.png
deleted file mode 100644
index 654d63e5596..00000000000
Binary files a/static/img/eino/en_eino_parallel_edge.png and /dev/null differ
diff --git a/static/img/eino/en_eino_parallel_node.png b/static/img/eino/en_eino_parallel_node.png
deleted file mode 100644
index c3b566c27a8..00000000000
Binary files a/static/img/eino/en_eino_parallel_node.png and /dev/null differ
diff --git a/static/img/eino/en_eino_parallel_type.png b/static/img/eino/en_eino_parallel_type.png
deleted file mode 100644
index e18d5d4b048..00000000000
Binary files a/static/img/eino/en_eino_parallel_type.png and /dev/null differ
diff --git a/static/img/eino/en_eino_practice_agent_graph.png b/static/img/eino/en_eino_practice_agent_graph.png
deleted file mode 100644
index 0dd414ef42b..00000000000
Binary files a/static/img/eino/en_eino_practice_agent_graph.png and /dev/null differ
diff --git a/static/img/eino/en_eino_practice_graph.png b/static/img/eino/en_eino_practice_graph.png
deleted file mode 100644
index 435ad0ad252..00000000000
Binary files a/static/img/eino/en_eino_practice_graph.png and /dev/null differ
diff --git a/static/img/eino/en_eino_react_agent_graph.png b/static/img/eino/en_eino_react_agent_graph.png
deleted file mode 100644
index 1b209c28533..00000000000
Binary files a/static/img/eino/en_eino_react_agent_graph.png and /dev/null differ
diff --git a/static/img/eino/en_eino_react_model_tool.png b/static/img/eino/en_eino_react_model_tool.png
deleted file mode 100644
index 8529f14e375..00000000000
Binary files a/static/img/eino/en_eino_react_model_tool.png and /dev/null differ
diff --git a/static/img/eino/en_eino_recommend_using_graph.png b/static/img/eino/en_eino_recommend_using_graph.png
deleted file mode 100644
index b35f88197f6..00000000000
Binary files a/static/img/eino/en_eino_recommend_using_graph.png and /dev/null differ
diff --git a/static/img/eino/en_eino_repo_structure.png b/static/img/eino/en_eino_repo_structure.png
deleted file mode 100644
index 36c142e00bf..00000000000
Binary files a/static/img/eino/en_eino_repo_structure.png and /dev/null differ
diff --git a/static/img/eino/en_eino_run_steps.png b/static/img/eino/en_eino_run_steps.png
deleted file mode 100644
index 1c071949430..00000000000
Binary files a/static/img/eino/en_eino_run_steps.png and /dev/null differ
diff --git a/static/img/eino/en_eino_stream.png b/static/img/eino/en_eino_stream.png
deleted file mode 100644
index c4b82b7e9dc..00000000000
Binary files a/static/img/eino/en_eino_stream.png and /dev/null differ
diff --git a/static/img/eino/en_eino_stream_copy_for_callback.png b/static/img/eino/en_eino_stream_copy_for_callback.png
deleted file mode 100644
index 15d97987bad..00000000000
Binary files a/static/img/eino/en_eino_stream_copy_for_callback.png and /dev/null differ
diff --git a/static/img/eino/en_eino_structure.png b/static/img/eino/en_eino_structure.png
deleted file mode 100644
index b99695380f5..00000000000
Binary files a/static/img/eino/en_eino_structure.png and /dev/null differ
diff --git a/static/img/eino/en_eino_type_in_parallel.png b/static/img/eino/en_eino_type_in_parallel.png
deleted file mode 100644
index 2a01614ea7a..00000000000
Binary files a/static/img/eino/en_eino_type_in_parallel.png and /dev/null differ
diff --git a/static/img/eino/en_internal_collect_outter_invoke.png b/static/img/eino/en_internal_collect_outter_invoke.png
deleted file mode 100644
index ce4ec1ce9a6..00000000000
Binary files a/static/img/eino/en_internal_collect_outter_invoke.png and /dev/null differ
diff --git a/static/img/eino/en_internal_collect_outter_transform.png b/static/img/eino/en_internal_collect_outter_transform.png
deleted file mode 100644
index f9f695209df..00000000000
Binary files a/static/img/eino/en_internal_collect_outter_transform.png and /dev/null differ
diff --git a/static/img/eino/en_internal_stream_outter_transform.png b/static/img/eino/en_internal_stream_outter_transform.png
deleted file mode 100644
index 8ba012a07aa..00000000000
Binary files a/static/img/eino/en_internal_stream_outter_transform.png and /dev/null differ
diff --git a/static/img/eino/graph_node_type1.png b/static/img/eino/graph_node_type1.png
index 4faab4df6f3..e74afd244dc 100644
Binary files a/static/img/eino/graph_node_type1.png and b/static/img/eino/graph_node_type1.png differ
diff --git a/static/img/eino/graph_nodes.png b/static/img/eino/graph_nodes.png
deleted file mode 100644
index 0e75dbe51b8..00000000000
Binary files a/static/img/eino/graph_nodes.png and /dev/null differ
diff --git a/static/img/eino/graph_runnable_after_compile.png b/static/img/eino/graph_runnable_after_compile.png
index c328ac4d63e..466f38933ed 100644
Binary files a/static/img/eino/graph_runnable_after_compile.png and b/static/img/eino/graph_runnable_after_compile.png differ
diff --git a/static/img/eino/graph_stream_chunk_copy.png b/static/img/eino/graph_stream_chunk_copy.png
index e4fcd5f3de5..57a5cc67044 100644
Binary files a/static/img/eino/graph_stream_chunk_copy.png and b/static/img/eino/graph_stream_chunk_copy.png differ
diff --git a/static/img/eino/hitl_address_adk.png b/static/img/eino/hitl_address_adk.png
deleted file mode 100644
index 70184df4231..00000000000
Binary files a/static/img/eino/hitl_address_adk.png and /dev/null differ
diff --git a/static/img/eino/hitl_address_compose.png b/static/img/eino/hitl_address_compose.png
deleted file mode 100644
index 0fd42fb7b45..00000000000
Binary files a/static/img/eino/hitl_address_compose.png and /dev/null differ
diff --git a/static/img/eino/hitl_architecture.png b/static/img/eino/hitl_architecture.png
deleted file mode 100644
index 1f50dd4d9e0..00000000000
Binary files a/static/img/eino/hitl_architecture.png and /dev/null differ
diff --git a/static/img/eino/hitl_requirements.png b/static/img/eino/hitl_requirements.png
deleted file mode 100644
index b6c7f078b8d..00000000000
Binary files a/static/img/eino/hitl_requirements.png and /dev/null differ
diff --git a/static/img/eino/hitl_sequence.png b/static/img/eino/hitl_sequence.png
deleted file mode 100644
index a4bd5260740..00000000000
Binary files a/static/img/eino/hitl_sequence.png and /dev/null differ
diff --git a/static/img/eino/input_keys_output_keys_in_parallel.png b/static/img/eino/input_keys_output_keys_in_parallel.png
deleted file mode 100644
index 0350305b359..00000000000
Binary files a/static/img/eino/input_keys_output_keys_in_parallel.png and /dev/null differ
diff --git a/static/img/eino/invoke_outside_collect_inside.png b/static/img/eino/invoke_outside_collect_inside.png
index 26388ebca04..b3b863f55c0 100644
Binary files a/static/img/eino/invoke_outside_collect_inside.png and b/static/img/eino/invoke_outside_collect_inside.png differ
diff --git a/static/img/eino/invoke_outside_stream_inside.png b/static/img/eino/invoke_outside_stream_inside.png
index 6eca46359e8..5b55e4f3f0f 100644
Binary files a/static/img/eino/invoke_outside_stream_inside.png and b/static/img/eino/invoke_outside_stream_inside.png differ
diff --git a/static/img/eino/invoke_outside_transform_inside.png b/static/img/eino/invoke_outside_transform_inside.png
index aab0e2e7951..e0ec73ddfc3 100644
Binary files a/static/img/eino/invoke_outside_transform_inside.png and b/static/img/eino/invoke_outside_transform_inside.png differ
diff --git a/static/img/eino/invoke_stream_transform_collect.png b/static/img/eino/invoke_stream_transform_collect.png
deleted file mode 100644
index e65216ff843..00000000000
Binary files a/static/img/eino/invoke_stream_transform_collect.png and /dev/null differ
diff --git a/static/img/eino/loop_agents.png b/static/img/eino/loop_agents.png
deleted file mode 100644
index 9e6f4637cd2..00000000000
Binary files a/static/img/eino/loop_agents.png and /dev/null differ
diff --git a/static/img/eino/loop_workflow_outline.png b/static/img/eino/loop_workflow_outline.png
deleted file mode 100644
index cd5cf3c9c38..00000000000
Binary files a/static/img/eino/loop_workflow_outline.png and /dev/null differ
diff --git a/static/img/eino/parallel_agents.png b/static/img/eino/parallel_agents.png
deleted file mode 100644
index ea70d72ff2c..00000000000
Binary files a/static/img/eino/parallel_agents.png and /dev/null differ
diff --git a/static/img/eino/parallel_workflow_outline.png b/static/img/eino/parallel_workflow_outline.png
deleted file mode 100644
index 6fc06e8e576..00000000000
Binary files a/static/img/eino/parallel_workflow_outline.png and /dev/null differ
diff --git a/static/img/eino/react_agent_graph.png b/static/img/eino/react_agent_graph.png
index 372fb589b8f..32980720d34 100644
Binary files a/static/img/eino/react_agent_graph.png and b/static/img/eino/react_agent_graph.png differ
diff --git a/static/img/eino/react_agent_pattern.png b/static/img/eino/react_agent_pattern.png
deleted file mode 100644
index d7987b71cd4..00000000000
Binary files a/static/img/eino/react_agent_pattern.png and /dev/null differ
diff --git a/static/img/eino/recommend_way_of_handler.png b/static/img/eino/recommend_way_of_handler.png
deleted file mode 100644
index 4be24079790..00000000000
Binary files a/static/img/eino/recommend_way_of_handler.png and /dev/null differ
diff --git a/static/img/eino/run_way_branch_in_graph.png b/static/img/eino/run_way_branch_in_graph.png
deleted file mode 100644
index 0ca0a9f9fbb..00000000000
Binary files a/static/img/eino/run_way_branch_in_graph.png and /dev/null differ
diff --git a/static/img/eino/sequential_agents.png b/static/img/eino/sequential_agents.png
deleted file mode 100644
index 487aac172e7..00000000000
Binary files a/static/img/eino/sequential_agents.png and /dev/null differ
diff --git a/static/img/eino/sequential_workflow.png b/static/img/eino/sequential_workflow.png
deleted file mode 100644
index f19ecd76562..00000000000
Binary files a/static/img/eino/sequential_workflow.png and /dev/null differ
diff --git a/static/img/eino/sub_agents_outline.png b/static/img/eino/sub_agents_outline.png
deleted file mode 100644
index 3b3526cd4e1..00000000000
Binary files a/static/img/eino/sub_agents_outline.png and /dev/null differ
diff --git a/static/img/eino/tool_model_react.png b/static/img/eino/tool_model_react.png
index 4a4a3450d74..64230cf0796 100644
Binary files a/static/img/eino/tool_model_react.png and b/static/img/eino/tool_model_react.png differ
diff --git a/static/img/eino/transfer_agent_input.png b/static/img/eino/transfer_agent_input.png
deleted file mode 100644
index 8f44393af08..00000000000
Binary files a/static/img/eino/transfer_agent_input.png and /dev/null differ
diff --git a/static/img/eino/transfer_sub_agents.png b/static/img/eino/transfer_sub_agents.png
deleted file mode 100644
index 30ed2299594..00000000000
Binary files a/static/img/eino/transfer_sub_agents.png and /dev/null differ
diff --git a/static/img/eino/transform_inside_stream_inside.png b/static/img/eino/transform_inside_stream_inside.png
index e1eb20ef2fd..81dd4cff480 100644
Binary files a/static/img/eino/transform_inside_stream_inside.png and b/static/img/eino/transform_inside_stream_inside.png differ
diff --git a/static/img/eino/transform_outside_invoke_inside.png b/static/img/eino/transform_outside_invoke_inside.png
index ec0b471fcea..b2a1d97c7f3 100644
Binary files a/static/img/eino/transform_outside_invoke_inside.png and b/static/img/eino/transform_outside_invoke_inside.png differ
diff --git a/static/img/eino/transform_outside_stream_inside.png b/static/img/eino/transform_outside_stream_inside.png
index 54a9831677c..ab7adcb1f3a 100644
Binary files a/static/img/eino/transform_outside_stream_inside.png and b/static/img/eino/transform_outside_stream_inside.png differ
diff --git a/static/img/eino/workflow_api_layer.png b/static/img/eino/workflow_api_layer.png
deleted file mode 100644
index dda2011b9b7..00000000000
Binary files a/static/img/eino/workflow_api_layer.png and /dev/null differ
diff --git a/static/img/eino/workflow_api_layer_en.png b/static/img/eino/workflow_api_layer_en.png
deleted file mode 100644
index 4f937a83cd8..00000000000
Binary files a/static/img/eino/workflow_api_layer_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_auction_en.png b/static/img/eino/workflow_auction_en.png
deleted file mode 100644
index b0c2951e226..00000000000
Binary files a/static/img/eino/workflow_auction_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_auction_static_values.png b/static/img/eino/workflow_auction_static_values.png
deleted file mode 100644
index 3a68146b946..00000000000
Binary files a/static/img/eino/workflow_auction_static_values.png and /dev/null differ
diff --git a/static/img/eino/QyggwF16hhFesobdbUAckHD0nae.png b/static/img/eino/workflow_auction_static_values_en.png
similarity index 100%
rename from static/img/eino/QyggwF16hhFesobdbUAckHD0nae.png
rename to static/img/eino/workflow_auction_static_values_en.png
diff --git a/static/img/eino/workflow_calculator_en.png b/static/img/eino/workflow_calculator_en.png
deleted file mode 100644
index 59867569452..00000000000
Binary files a/static/img/eino/workflow_calculator_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_char_counter_en.png b/static/img/eino/workflow_char_counter_en.png
deleted file mode 100644
index 7d49f012391..00000000000
Binary files a/static/img/eino/workflow_char_counter_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_cross_node_data_passing_en.png b/static/img/eino/workflow_cross_node_data_passing_en.png
deleted file mode 100644
index ee80daafd40..00000000000
Binary files a/static/img/eino/workflow_cross_node_data_passing_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_data_control_separate_en.png b/static/img/eino/workflow_data_control_separate_en.png
deleted file mode 100644
index be500ef8451..00000000000
Binary files a/static/img/eino/workflow_data_control_separate_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_existing_biz_func_en.png b/static/img/eino/workflow_existing_biz_func_en.png
deleted file mode 100644
index 6f194f45ff7..00000000000
Binary files a/static/img/eino/workflow_existing_biz_func_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_simple_en.png b/static/img/eino/workflow_simple_en.png
deleted file mode 100644
index 3209bafd0e5..00000000000
Binary files a/static/img/eino/workflow_simple_en.png and /dev/null differ
diff --git a/static/img/eino/workflow_stream_en.png b/static/img/eino/workflow_stream_en.png
deleted file mode 100644
index 8eb3256e4b5..00000000000
Binary files a/static/img/eino/workflow_stream_en.png and /dev/null differ