diff --git a/_typos.toml b/_typos.toml index 612929ed56e..de3e49d8d7e 100644 --- a/_typos.toml +++ b/_typos.toml @@ -15,3 +15,6 @@ exten = "exten" invokable = "invokable" typ = "typ" Rabit = "Rabit" +byted = "byted" +Byted = "Byted" +bytedgpt = "bytedgpt" diff --git a/content/en/docs/eino/Eino: Cookbook.md b/content/en/docs/eino/Eino: Cookbook.md index 71671a74898..a475db0cd73 100644 --- a/content/en/docs/eino/Eino: Cookbook.md +++ b/content/en/docs/eino/Eino: Cookbook.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Cookbook' @@ -26,13 +26,13 @@ This document serves as an example index for the eino-examples project, helping - + - +
DirectoryNameDescription
adk/intro/chatmodelChatModel AgentDemonstrates how to use ChatModelAgent with Interrupt mechanism
adk/intro/chatmodelChatModel AgentDemonstrates how to use ChatModelAgent with the Interrupt mechanism
adk/intro/customCustom AgentDemonstrates how to implement a custom Agent conforming to ADK definition
adk/intro/workflow/loopLoop AgentDemonstrates how to use LoopAgent to implement loop reflection pattern
adk/intro/workflow/parallelParallel AgentDemonstrates how to use ParallelAgent for parallel execution
adk/intro/workflow/sequentialSequential AgentDemonstrates how to use SequentialAgent for sequential execution
adk/intro/sessionSession ManagementDemonstrates how to pass data and state between multiple Agents through Session
adk/intro/transferAgent TransferDemonstrates ChatModelAgent's Transfer capability for task transfer between Agents
adk/intro/transferAgent TransferDemonstrates the Transfer capability of ChatModelAgent for task transfer between Agents
adk/intro/agent_with_summarizationAgent with SummarizationDemonstrates how to add conversation summarization functionality to an Agent
adk/intro/http-sse-serviceHTTP SSE ServiceDemonstrates how to expose ADK Runner as an HTTP service supporting Server-Sent Events
@@ -42,11 +42,11 @@ This document serves as an example index for the eino-examples project, helping - + - - + +
DirectoryNameDescription
adk/human-in-the-loop/1_approvalApproval ModeDemonstrates human approval mechanism before sensitive operations, requiring user confirmation before Agent execution
adk/human-in-the-loop/2_review-and-editReview and Edit ModeDemonstrates human review and editing of tool call parameters, supporting modification, approval, or rejection
adk/human-in-the-loop/2_review-and-editReview and Edit ModeDemonstrates human review and editing of tool call parameters, supporting modification, approval or rejection
adk/human-in-the-loop/3_feedback-loopFeedback Loop ModeMulti-Agent collaboration where Writer generates content, Reviewer collects human feedback, supporting iterative optimization
adk/human-in-the-loop/4_follow-upFollow-up ModeIntelligently identifies missing information, collects user requirements through multiple rounds of follow-up questions, completes complex task planning
adk/human-in-the-loop/5_supervisorSupervisor + ApprovalSupervisor multi-Agent mode combined with approval mechanism, sensitive operations require human confirmation
adk/human-in-the-loop/6_plan-execute-replanPlan-Execute-Replan + Review EditPlan-Execute-Replan mode combined with parameter review and editing, supporting reservation parameter modification
adk/human-in-the-loop/5_supervisorSupervisor + ApprovalSupervisor Multi-Agent mode combined with approval mechanism, requiring human confirmation for sensitive operations
adk/human-in-the-loop/6_plan-execute-replanPlan-Execute-Replan + Review EditPlan-Execute-Replan mode combined with parameter review editing, supporting reservation parameter modification
adk/human-in-the-loop/7_deep-agentsDeep Agents + Follow-upDeep Agents mode combined with follow-up mechanism, proactively collecting user preferences before analysis
adk/human-in-the-loop/8_supervisor-plan-executeNested Multi-Agent + ApprovalSupervisor nested with Plan-Execute-Replan sub-Agent, supporting deep nested interrupts
@@ -55,9 +55,9 @@ This document serves as an example index for the eino-examples project, helping - - - + + + @@ -68,9 +68,9 @@ This document serves as an example index for the eino-examples project, helping
DirectoryNameDescription
adk/multiagent/supervisorSupervisor AgentBasic Supervisor multi-Agent mode, coordinating multiple sub-Agents to complete tasks
adk/multiagent/layered-supervisorLayered SupervisorMulti-layer Supervisor nesting, one Supervisor as sub-Agent of another
adk/multiagent/plan-execute-replanPlan-Execute-ReplanPlan-Execute-Replan mode, supporting dynamic adjustment of execution plans
adk/multiagent/supervisorSupervisor AgentBasic Supervisor Multi-Agent mode, coordinating multiple sub-Agents to complete tasks
adk/multiagent/layered-supervisorLayered SupervisorMulti-layer Supervisor nesting, one Supervisor as a sub-Agent of another
adk/multiagent/plan-execute-replanPlan-Execute-ReplanPlan-Execute-Replan mode, supporting dynamic execution plan adjustment
adk/multiagent/integration-project-managerProject ManagerProject management example using Supervisor mode, including Coder, Researcher, Reviewer
adk/multiagent/deepDeep Agents (Excel Agent)Intelligent Excel assistant, step-by-step understanding and processing Excel files, supporting Python code execution
adk/multiagent/integration-excel-agentExcel Agent (ADK Integration)ADK integrated Excel Agent, including Planner, Executor, Replanner, Reporter
- - - + + +
DirectoryNameDescription
adk/common/tool/graphtoolGraphTool PackageToolkit for wrapping Graph/Chain/Workflow as Agent tools
adk/common/tool/graphtool/examples/1_chain_summarizeChain Document SummaryDocument summarization tool using compose.Chain
adk/common/tool/graphtool/examples/2_graph_researchGraph Multi-source ResearchParallel multi-source search and streaming output using compose.Graph
adk/common/tool/graphtool/examples/3_workflow_orderWorkflow Order ProcessingOrder processing using compose.Workflow, combined with approval mechanism
adk/common/tool/graphtool/examples/1_chain_summarizeChain Document SummarizationImplementing document summarization tool using compose.Chain
adk/common/tool/graphtool/examples/2_graph_researchGraph Multi-source ResearchImplementing parallel multi-source search and streaming output using compose.Graph
adk/common/tool/graphtool/examples/3_workflow_orderWorkflow Order ProcessingImplementing order processing using compose.Workflow, combined with approval mechanism
adk/common/tool/graphtool/examples/4_nested_interruptNested InterruptDemonstrates dual-layer interrupt mechanism with outer approval and inner risk control
@@ -78,57 +78,57 @@ This document serves as an example index for the eino-examples project, helping ## 🔗 Compose (Orchestration) -### Chain +### Chain (Chain Orchestration)
DirectoryNameDescription
compose/chainChain Basic ExampleDemonstrates how to use compose.Chain for sequential orchestration, including Prompt + ChatModel
-### Graph +### Graph (Graph Orchestration) - +
DirectoryNameDescription
compose/graph/simpleSimple GraphBasic Graph usage example
compose/graph/stateState GraphGraph example with state
compose/graph/tool_call_agentTool Call AgentBuilding a tool-calling Agent using Graph
compose/graph/tool_call_agentTool Call AgentBuilding tool calling Agent using Graph
compose/graph/tool_call_onceSingle Tool CallDemonstrates single tool call Graph implementation
compose/graph/two_model_chatTwo Model ChatGraph example of two models chatting with each other
compose/graph/async_nodeAsync NodeDemonstrates async Lambda nodes, including report generation and real-time transcription scenarios
compose/graph/react_with_interruptReAct + InterruptTicket booking scenario, demonstrating Interrupt and Checkpoint practices
-### Workflow +### Workflow (Workflow Orchestration) - + - +
DirectoryNameDescription
compose/workflow/1_simpleSimple WorkflowSimplest Workflow example, equivalent to Graph
compose/workflow/1_simpleSimple WorkflowThe simplest Workflow example, equivalent to Graph
compose/workflow/2_field_mappingField MappingDemonstrates Workflow field mapping functionality
compose/workflow/3_data_onlyData Only FlowData-only Workflow example
compose/workflow/4_control_only_branchControl Flow BranchControl-only branch example
compose/workflow/4_control_only_branchControl Flow BranchControl-flow-only branch example
compose/workflow/5_static_valuesStatic ValuesDemonstrates how to use static values in Workflow
compose/workflow/6_stream_field_mapStream Field MappingField mapping in streaming scenarios
-### Batch +### Batch (Batch Processing) - +
DirectoryNameDescription
compose/batchBatchNodeBatch processing component, supporting concurrency control and interrupt recovery, suitable for document batch review scenarios
compose/batchBatchNodeBatch processing component with concurrency control and interrupt recovery, suitable for document batch review scenarios
--- -## 🌊 Flow +## 🌊 Flow (Flow Modules) ### ReAct Agent - - - - + + + +
DirectoryNameDescription
flow/agent/reactReAct AgentBasic ReAct Agent example, restaurant recommendation scenario
flow/agent/react/memory_exampleShort-term MemoryShort-term memory implementation for ReAct Agent, supporting memory and Redis storage
flow/agent/react/dynamic_option_exampleDynamic OptionsDynamically modify Model Option at runtime, controlling thinking mode and tool selection
flow/agent/react/unknown_tool_handler_exampleUnknown Tool HandlerHandling unknown tool calls generated by model hallucination, improving Agent robustness
flow/agent/reactReAct AgentReAct Agent basic example, restaurant recommendation scenario
flow/agent/react/memory_exampleShort-term MemoryReAct Agent short-term memory implementation, supporting in-memory and Redis storage
flow/agent/react/dynamic_option_exampleDynamic OptionsDynamically modify Model Options at runtime, controlling thinking mode and tool selection
flow/agent/react/unknown_tool_handler_exampleUnknown Tool HandlerHandle unknown tool calls generated by model hallucination, improving Agent robustness
### Multi-Agent @@ -143,8 +143,8 @@ This document serves as an example index for the eino-examples project, helping - - + +
DirectoryNameDescription
flow/agent/manusManus AgentManus Agent implemented based on Eino, referencing OpenManus project
flow/agent/deer-goDeer-GoGo language implementation referencing deer-flow, supporting research team collaboration state graph transitions
flow/agent/manusManus AgentManus Agent implementation based on Eino, referencing the OpenManus project
flow/agent/deer-goDeer-GoGo language implementation referencing deer-flow, supporting research team collaboration state graph flow
--- @@ -163,7 +163,7 @@ This document serves as an example index for the eino-examples project, helping - +
DirectoryNameDescription
components/retriever/multiqueryMulti-query RetrievalUsing LLM to generate multiple query variants, improving retrieval recall
components/retriever/multiqueryMulti-Query RetrievalUsing LLM to generate multiple query variants, improving retrieval recall
components/retriever/routerRouter RetrievalDynamically routing to different retrievers based on query content
@@ -171,7 +171,7 @@ This document serves as an example index for the eino-examples project, helping - + @@ -182,7 +182,7 @@ This document serves as an example index for the eino-examples project, helping
DirectoryNameDescription
components/tool/jsonschemaJSON Schema ToolDemonstrates how to define tool parameters using JSON Schema
components/tool/jsonschemaJSON Schema ToolDemonstrates how to use JSON Schema to define tool parameters
components/tool/mcptool/callresulthandlerMCP Tool Result HandlerDemonstrates custom handling of MCP tool call results
components/tool/middlewares/errorremoverError Remover MiddlewareTool call error handling middleware, converting errors to friendly prompts
components/tool/middlewares/jsonfixJSON Fix MiddlewareFixing malformed JSON parameters generated by LLM
- +
DirectoryNameDescription
components/document/parser/customparserCustom ParserDemonstrates how to implement a custom document parser
components/document/parser/extparserExtension ParserUsing extension parser to handle HTML and other formats
components/document/parser/extparserExtension ParserUsing extension parsers to handle HTML and other formats
components/document/parser/textparserText ParserBasic text document parser example
@@ -206,7 +206,7 @@ This document serves as an example index for the eino-examples project, helping - +
DirectoryNameDescription
quickstart/chatChat QuickStartMost basic LLM conversation example, including templates, generation, streaming output
quickstart/chatChat QuickStartThe most basic LLM conversation example, including template, generation, streaming output
quickstart/eino_assistantEino AssistantComplete RAG application example, including knowledge indexing, Agent service, Web interface
quickstart/todoagentTodo AgentSimple Todo management Agent example
@@ -217,7 +217,7 @@ This document serves as an example index for the eino-examples project, helping - +
DirectoryNameDescription
devops/debugDebug ToolsDemonstrates how to use Eino's debugging features, supporting Chain and Graph debugging
devops/debugDebug ToolsDemonstrates how to use Eino debugging features, supporting Chain and Graph debugging
devops/visualizeVisualization ToolsRendering Graph/Chain/Workflow as Mermaid diagrams
diff --git a/content/en/docs/eino/FAQ.md b/content/en/docs/eino/FAQ.md index 4467a8bc681..ebe0ab2b237 100644 --- a/content/en/docs/eino/FAQ.md +++ b/content/en/docs/eino/FAQ.md @@ -1,23 +1,23 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: FAQ -weight: 7 +weight: 11 --- -# 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 +# 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. +Check that the github.com/getkin/kin-openapi dependency version does not exceed v0.118.0. Versions after Eino V0.6.0 no longer depend on the kin-openapi library. -# Q: During agent streaming, it never reaches ToolsNode, or streaming is lost and appears non-streaming. +# Q: Agent streaming calls do not enter the ToolsNode node. Or streaming effect is lost, behaving as non-streaming. -- First, update Eino to the latest version. +- 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. +Different models may output tool calls differently in streaming mode: some models (like OpenAI) output tool calls directly; some models (like Claude) output text first, then output tool calls. Therefore, different methods need to be used for judgment. This field is used to specify the function that determines whether the model's streaming output contains tool calls. -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: +The Config of ReAct Agent has a StreamToolCallChecker field. If not filled, the Agent will use "non-empty packet" to determine whether it contains tool calls: ```go func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) { @@ -45,9 +45,9 @@ func firstChunkStreamToolCallChecker(_ context.Context, sr *schema.StreamReader[ } ``` -This default works when the Tool Call message contains only a tool call. +The above default implementation is suitable for: Tool Call Messages output by the model only contain Tool Calls. -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: +Cases where the default implementation is not applicable: there are non-empty content chunks before outputting Tool Calls. In this case, a custom tool call checker is needed: ```go toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) { @@ -71,108 +71,139 @@ toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Mes } ``` -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: +The above custom StreamToolCallChecker needs to check **all packets** for ToolCall when the model normally outputs an answer, which causes the "streaming judgment" effect to be lost. To preserve the "streaming judgment" effect as much as possible, the suggestion is: > 💡 -> Add prompt constraints such as: "If a tool is required, output only the tool call; do not output text." +> Try adding prompts to constrain the model not to output extra text when calling tools, for example: "If you need to call a tool, output the tool directly without outputting text." > -> Models vary in how much they adhere to such prompts. Tune and validate for your chosen model. +> Different models may be affected differently by prompts. In actual use, you need to adjust the prompts yourself and verify the effect. # 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. +Old versions of sonic are incompatible with go1.24. Update to version greater than v1.13.2. -# Q: Tool input deserialization failed: `failed to invoke tool call {tool_call_id}: unmarshal input fail` +# 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. +Currently, models generally do not produce illegal JSON output. First confirm what the deserialization failure reason is. It's most likely caused by model output being truncated due to excessive length. -# Q: How can I implement batch processing nodes in Eino (like Coze's batch nodes)? +# Q: How does Eino implement batch processing nodes? Similar to batch processing nodes in Coze -Eino currently does not support batch processing. Two options: +Eino currently does not support batch processing. There are two optional methods: -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. +1. Dynamically build the graph on demand for each request, with low additional cost. Note that Chain Parallel requires more than one parallel node. +2. Custom batch processing node, where the node handles batch processing tasks internally Code example: [https://github.com/cloudwego/eino-examples/tree/main/compose/batch](https://github.com/cloudwego/eino-examples/tree/main/compose/batch) -# Q: Does Eino support structured model outputs? +# Q: Does Eino support structured model output? -Yes, in two steps: +Two steps. First, require the model to output structured data, with three methods: -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. +1. Some models support direct configuration (like OpenAI's response format). Check if there's such configuration in the model settings. +2. Obtain through tool call functionality +3. Write prompts requiring the model to output structured data -# Q: How to access Reasoning Content / "thinking" output from a chat model? +After getting structured output from the model, you can use schema.NewMessageJSONParser to convert the message to the struct you need. -If the model implementation supports Reasoning Content, it is stored in the `ReasoningContent` field of the output `Message`. +# Q: How to get the Reasoning Content/reasoning/deep thinking content output by the model (chat model): -# Q: Errors include `context deadline exceeded`, `timeout`, or `context canceled` +If the model wrapper supports outputting Reasoning Content/reasoning/deep thinking content, this content will be stored in the ReasoningContent field of the Message output by the model. -Cases: +# Q: Error contains "context deadline exceeded" "timeout" "context canceled" -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. +Discussion by case: -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. +1. context.canceled: When executing a graph or agent, the user passed in a cancelable context and initiated a cancellation. Check the context cancel operation in the application layer code. This error is unrelated to the Eino framework. +2. Context deadline exceeded: Could be two situations: + 1. When executing a graph or agent, the user passed in a context with timeout, triggering a timeout. + 2. Timeout or httpclient with timeout was configured for ChatModel or other external resources, triggering a timeout. -If you suspect 2-a, trace upstream to find where a timeout was set on the context (common sources include FaaS platforms, gateways, etc.). +Check `node path: [node name x]` in the thrown error. If the node name is not a node with external calls like ChatModel, it's most likely situation 2-a; otherwise, it's most likely situation 2-b. -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. +If you suspect it's situation 2-a, check which link in the upstream chain set the timeout on context. Common possibilities include FaaS platforms, etc. -# Q: How to access parent graph `State` within a subgraph? +If you suspect it's situation 2-b, check whether the node has its own timeout configuration, such as Ark ChatModel configured with Timeout, or OpenAI ChatModel configured with HttpClient (with internal Timeout configuration). If neither is configured but still timing out, it may be the model SDK's default timeout. Known default timeouts: Ark SDK 10 minutes, Deepseek SDK 5 minutes. -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 to get the parent graph's State in a subgraph -# Q: How does `eino-ext` adapt multimodal input/output for supported models? +If the subgraph and parent graph have different State types, you can use `ProcessState[parent graph state type]()` to process the parent graph's State. If the subgraph and parent graph have the same State type, make the State types different, for example, using a type alias: `type NewParentStateType StateType`. -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: How to adapt multimodal input/output for Models supported by eino-ext? -# Q: Using `UserInputMultiContent` for multimodal input, but the model side seems to miss the data or cannot read `multicontent` +For multimodal input/output scenarios supported by eino-ext, refer to the Examples in [https://www.cloudwego.io/docs/eino/ecosystem_integration/chat_model](https://www.cloudwego.io/docs/eino/ecosystem_integration/chat_model) for the corresponding model. -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: Using the latest multimodal support field UserInputMultiContent to input multimodal data, but the model side doesn't seem to have my multimodal data, or can't read multicontent content during multimodal input -# Q: After upgrading to `0.6.x`, there are breaking changes +The latest version of Eino introduces UserInputMultiContent and AssistantGenMultiContent to represent user-side input multimodal data and model-side returned multimodal data respectively. The chatmodel implementations in eino-ext have all been adapted. If you find that the model side has not received multimodal information, try upgrading the model package you're using. Use go get to update to the latest version and try running again to see if the problem is resolved. -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. +# Q: After upgrading to version 0.6.x, there are incompatibility issues -If `eino-ext` modules error with `undefined: schema.NewParamsOneOfByOpenAPIV3`, upgrade those modules to the latest versions. +According to the previous community announcement plan [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 has been released. Important update content includes removing the getkin/kin-openapi dependency and all OpenAPI 3.0 related code. -If schema migration is complex, use the helper tooling in the [JSONSchema conversion guide](https://bytedance.larkoffice.com/wiki/ZMaawoQC4iIjNykzahwc6YOknXf). +For errors like undefined: schema.NewParamsOneOfByOpenAPIV3 in some eino-ext modules, upgrade the error-reporting eino-ext module to the latest version. -# Q: Which ChatModels in `eino-ext` support the Responses API form? +If schema transformation is complex, you can use the tool methods in the [JSONSchema Conversion Methods](https://bytedance.larkoffice.com/wiki/ZMaawoQC4iIjNykzahwc6YOknXf) document to assist with conversion. -- In `eino-ext`, currently only the ARK Chat Model can create a ResponsesAPI ChatModel via **NewResponsesAPIChatModel**. Other models do not support creating or using ResponsesAPI. -- In `eino-byted-ext`, only bytedgpt supports creating Response API via **NewResponsesAPIChatModel**. Other chatmodels have not implemented Response API Client. - - Version `components/model/gemini/v0.1.16` already supports `thought_signature` passback. Check if your gemini version meets the requirement. If using bytedgemini (code.byted.org/flow/eino-byted-ext/components/model/bytedgemini) chatmodel implementation, check if its dependency `components/model/gemini` is the latest version, or directly upgrade gemini via go get. - - Replace the currently used bytedgpt package with [code.byted.org/flow/eino-byted-ext/components/model/bytedgemini](http://code.byted.org/flow/eino-byted-ext/components/model/bytedgemini) implementation, upgrade to the latest version, and check the example code to confirm how to pass BaseURL. - - If you encounter this error, please confirm whether the base url filled in when generating chat model is the chat completion URL or the ResponseAPI URL. In most cases, it's an incorrect ResponseAPI Base URL being passed. +# Q: Which models provided by Eino-ext ChatModel support Response API format calls? -# Q: How to troubleshoot ChatModel call errors? For example: [NodeRunError] failed to create chat completion: error, status code: 400, status: 400 Bad Request. +- Currently in Eino-Ext, only ARK's Chat Model can create ResponsesAPI ChatModel through **NewResponsesAPIChatModel**. Other models currently do not support ResponsesAPI creation and usage. +- In Eino-byted-ext, only bytedgpt supports creating Response API through **NewResponsesAPIChatModel**. Other chatmodels have not implemented Response API Client. + - Version components/model/gemini/v0.1.16 already supports thought_signature passback. Check if the gemini version meets requirements. If using bytedgemini (code.byted.org/flow/eino-byted-ext/components/model/bytedgemini) chatmodel implementation, check if its dependent components/model/gemini is the latest version, or directly use go get to upgrade gemini. + - Replace the currently used bytedgpt package with the implementation from [code.byted.org/flow/eino-byted-ext/components/model/bytedgemini](http://code.byted.org/flow/eino-byted-ext/components/model/bytedgemini) and upgrade to the latest version. Refer to example code to confirm how to pass BaseURL. + - If you encounter this error, confirm whether the base url filled in when generating chat model is the chat completion URL or the ResponseAPI URL. In most cases, the Response API Base URL was incorrectly passed. -This type of error comes from the model API (such as GPT, Ark, Gemini, etc.). The general approach is to check whether the actual HTTP Request to the model API has missing fields, incorrect field values, wrong BaseURL, etc. It's recommended to print the actual HTTP Request via logs and verify/modify the HTTP Request through direct HTTP requests (such as sending Curl from command line or using Postman). After identifying the problem, modify the corresponding Eino code accordingly. +# Q: How to troubleshoot ChatModel call errors? For example, [NodeRunError] failed to create chat completion: error, status code: 400, status: 400 Bad Request. -For how to print the actual HTTP Request to the model API via logs, refer to this code example: [https://github.com/cloudwego/eino-examples/tree/main/components/model/httptransport](https://github.com/cloudwego/eino-examples/tree/main/components/model/httptransport) +This type of error is an error from the model API (such as GPT, Ark, Gemini, etc.). The general approach is to check whether the actual HTTP Request calling the model API has missing fields, incorrect field values, wrong BaseURL, etc. It's recommended to print out the actual HTTP Request through logs and verify/modify the HTTP Request through direct HTTP request methods (such as sending Curl from command line or using Postman for direct requests). After locating the problem, modify the corresponding issues in the Eino code accordingly. -# Q: The gemini chat model created under eino-ext repository doesn't support passing multimodal via Image URL? How to adapt? +For how to print out the actual HTTP Request of the model API through logs, refer to this code example: [https://github.com/cloudwego/eino-examples/tree/main/components/model/httptransport](https://github.com/cloudwego/eino-examples/tree/main/components/model/httptransport) -Currently, the gemini Chat model under the Eino-ext repository has added support for passing URL types. Use `go get github.com/cloudwego/eino-ext/components/model/gemini` to update to [components/model/gemini/v0.1.22](https://github.com/cloudwego/eino-ext/releases/tag/components%2Fmodel%2Fgemini%2Fv0.1.22), the latest version, and test whether passing Image URL meets your business requirements. +# Q: The gemini chat model created under the eino-ext repository doesn't support using Image URL to pass multimodal data? How to adapt? -# Q: Before calling tools (including MCP tools), JSON Unmarshal failure error occurs. How to solve it? +Currently, the gemini Chat model under the Eino-ext repository has already added support for passing URL types. Use go get github.com/cloudwego/eino-ext/components/model/gemini to update to [components/model/gemini/v0.1.22](https://github.com/cloudwego/eino-ext/releases/tag/components%2Fmodel%2Fgemini%2Fv0.1.22), the current latest version. Test passing Image URL to see if it meets business requirements. -The Argument field in the Tool Call generated by ChatModel is a string. When the Eino framework calls the tool based on this Argument string, it first performs JSON Unmarshal. At this point, if the Argument string is not valid JSON, JSON Unmarshal will fail with an error like: `failed to call mcp tool: failed to marshal request: json: error calling MarshalJSON for type json.RawMessage: unexpected end of JSON input` +# Q: Before calling tools (including MCP tool), getting JSON Unmarshal failure error, how to solve -The fundamental solution to this problem is to rely on the model to output valid Tool Call Arguments. From an engineering perspective, we can try to fix some common JSON format issues, such as extra prefixes/suffixes, special character escaping issues, missing braces, etc., but cannot guarantee 100% correction. A similar fix implementation can be found in the code example: [https://github.com/cloudwego/eino-examples/tree/main/components/tool/middlewares/jsonfix](https://github.com/cloudwego/eino-examples/tree/main/components/tool/middlewares/jsonfix) +The Argument field in Tool Call generated by ChatModel is a string. When the Eino framework calls tools based on this Argument string, it first does JSON Unmarshal. At this point, if the Argument string is not valid JSON, JSON Unmarshal will fail, throwing an error like: `failed to call mcp tool: failed to marshal request: json: error calling MarshalJSON for type json.RawMessage: unexpected end of JSON input` + +The fundamental solution to this problem is to rely on the model to output valid Tool Call Arguments. Engineering-wise, we can try to fix some common JSON format issues, such as extra prefixes/suffixes, special character escaping issues, missing braces, etc., but cannot guarantee 100% correction. A similar fix implementation can be referenced in this code example: [https://github.com/cloudwego/eino-examples/tree/main/components/tool/middlewares/jsonfix](https://github.com/cloudwego/eino-examples/tree/main/components/tool/middlewares/jsonfix) # Q: How to visualize the topology structure of a graph/chain/workflow? -Use the `GraphCompileCallback` mechanism to export the topology structure during `graph.Compile`. A code example for exporting to mermaid diagram: [https://github.com/cloudwego/eino-examples/tree/main/devops/visualize](https://github.com/cloudwego/eino-examples/tree/main/devops/visualize) +Use the `GraphCompileCallback` mechanism to export the topology structure during `graph.Compile`. A code example for exporting as a mermaid diagram: [https://github.com/cloudwego/eino-examples/tree/main/devops/visualize](https://github.com/cloudwego/eino-examples/tree/main/devops/visualize) + +- For obtaining intermediate structures in Flow/React Agent scenarios, refer to the document [Eino: ReAct Agent Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual) + +# Q: Gemini model error missing a `thought_signature` -## Q: In Eino, when using Flow/React Agent scenarios, how to get the Tool Call Message and the Tool Result of the tool invocation? +Gemini model protocol is not openai-compatible. Use the gemini wrapper [https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini). If using ModelHub platform models, use the internal gemini wrapper [https://code.byted.org/flow/eino-byted-ext/tree/master/components/model/bytedgemini](https://code.byted.org/flow/eino-byted-ext/tree/master/components/model/bytedgemini). Initialization reference code: -- For getting intermediate results in Flow/React Agent scenarios, refer to the document [Eino: ReAct Agent User Manual](/docs/eino/core_modules/flow_integration_components/react_agent_manual) - - Additionally, you can replace Flow/React Agent with ADK's ChatModel Agent. For details, refer to [Eino ADK: Overview](/docs/eino/core_modules/eino_adk/agent_preview) +``` +cm, err := bytedgemini.NewChatModel(ctx, &bytedgemini.Config{ + ClientConfig: genai.ClientConfig{ + APIKey: apiKey, + Backend: genai.BackendGeminiAPI, + // uncomment if you want to print the actual request in CURL format + // HTTPClient: &http.Client{Transport: NewCurlLogger(http.DefaultTransport, log.Printf)}, + HTTPOptions: genai.HTTPOptions{ + // this is base URL for cn, other regions: + // - sg: gpt-i18n.byteintl.net + // - sg from office network: genai-sg-og.tiktok-row.org + // - va: search-va.byteintl.net + // - va from office network: genai-va-og.tiktok-row.org + // - Non-TT: gpt-i18n.bd.byteintl.net + // - US-TTP: gpt.tiktokd.net + // - EU-TTP, GCP: gpt.tiktoke.org + // - JP: gpt-jp.byteintl.net + // see also: https://bytedance.larkoffice.com/wiki/wikcnUPXCY2idGyg2AXKPvay4pd + BaseURL: "https://search.bytedance.net/gpt/openapi/online/multimodal/crawl/google/", + APIVersion: "v1", + }, + }, + Model: modelName, + ThinkingConfig: &genai.ThinkingConfig{ + IncludeThoughts: true, + ThinkingBudget: nil, + }, +}) +``` 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 3b82e295512..7ee9204b9ae 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,29 +1,29 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino: Callback Manual' +title: 'Eino: Callback User Manual' weight: 5 --- -## Problem Statement +## Problem Solved -Components (including Lambdas) and Graph orchestration together solve the problem of "defining business logic". Cross-cutting concerns like logging, tracing, metrics, and UI surfacing need a mechanism to inject functionality into Components (including Lambdas) and Graphs. +Components (including Lambda) and Graph orchestration together solve the problem of "defining business logic". Cross-cutting concerns such as logging, tracing, metrics, and display on screen require mechanisms to inject functionality into Components (including Lambda) and Graphs. -On the other hand, users may want to access intermediate information during the execution of a specific Component implementation, such as the DB Name queried by VikingDBRetriever, or parameters like temperature requested by ArkChatModel. A mechanism is needed to expose intermediate state. +On the other hand, users may want to obtain intermediate information during the execution of a specific Component implementation, such as VikingDBRetriever providing additional DB Name for queries, or ArkChatModel providing additional parameters like temperature for requests. A mechanism is needed to expose intermediate states. -Callbacks support both "**cross-cutting concern injection**" and "**intermediate state exposure**". Specifically: users provide and register "functions" (Callback Handlers), and Components and Graphs call back these functions at fixed "timings" (or aspects/points), providing corresponding information. +Callbacks support "**cross-cutting functionality injection**" and "**intermediate state exposure**". Specifically: users provide and register "functions" (Callback Handlers), and Components and Graphs call back these functions at fixed "timings" (or aspects, positions), providing corresponding information. ## Core Concepts -The core concepts connected together: **Entities** in Eino such as Components and Graphs, at fixed **timings** (Callback Timing), call back user-provided **functions** (Callback Handlers), passing **who they are** (RunInfo) and **what happened at that moment** (Callback Input & Output). +The core concepts connect as follows: **Entities** in Eino such as Components and Graphs, at fixed **timings** (Callback Timing), call back **functions** (Callback Handlers) provided by users, passing out **who they are** (RunInfo) and **what happened at that time** (Callback Input & Output). ### Triggering Entities -Components (including officially defined component types and Lambdas), Graph Nodes (as well as Chain/Workflow Nodes), and Graphs themselves (as well as Chain/Workflow). All three types of entities have needs for cross-cutting concern injection and intermediate state exposure, so they all trigger callbacks. See the "[Triggering Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section below for details. +Components (including officially defined component types and Lambda), Graph Nodes (as well as Chain/Workflow Nodes), and Graphs themselves (as well as Chain/Workflow). These three types of entities all have needs for cross-cutting functionality injection and intermediate state exposure, so they all trigger callbacks. See the "[Triggering Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section below for details. -### Triggering Timings +### Triggering Timing ```go // CallbackTiming enumerates all the timing of callback aspects. @@ -38,7 +38,7 @@ const ( ) ``` -Different triggering entities, in different scenarios, whether to trigger OnStart or OnStartWithStreamInput (same for OnEnd/OnEndWithStreamOutput), the specific rules are detailed in the "[Triggering Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section below. +Different triggering entities, in different scenarios, trigger OnStart or OnStartWithStreamInput (same logic for OnEnd/OnEndWithStreamOutput). The specific rules are detailed in the "[Triggering Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section below. ### Callback Handler @@ -56,19 +56,19 @@ type Handler interface { A Handler is a struct that implements the above 5 methods (corresponding to 5 triggering timings). Each method receives three pieces of information: -- Context: Used to **receive custom information that may have been set by preceding timings of the same Handler**. +- Context: Used to **receive custom information** that may be set by preceding triggering timings of the same Handler. - RunInfo: Metadata of the entity triggering the callback. - Input/Output/InputStream/OutputStream: Business information at the time of callback triggering. -And all return a new Context: used to **pass information between different triggering timings of the same Handler**. +And all return a new Context: for **passing information between different triggering timings of the same Handler**. -If a Handler doesn't want to focus on all 5 triggering timings, but only some of them, such as only OnStart, it's recommended to use `NewHandlerBuilder().OnStartFn(...).Build()`. If you don't want to focus on all component types, but only specific components like ChatModel, it's recommended to use `NewHandlerHelper().ChatModel(...).Handler()`, which only receives ChatModel callbacks and gets typed CallbackInput/CallbackOutput. See the "[Handler Implementation Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section for details. +If a Handler doesn't want to focus on all 5 triggering timings, but only some of them, such as only OnStart, it's recommended to use `NewHandlerBuilder().OnStartFn(...).Build()`. If you don't want to focus on all component types, but only specific components, such as ChatModel, it's recommended to use `NewHandlerHelper().ChatModel(...).Handler()`, which allows you to receive only ChatModel callbacks and get a specifically typed CallbackInput/CallbackOutput. See the "[Handler Implementation Methods](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual)" section for details. There is **no** guaranteed triggering order between different Handlers. ### RunInfo -Describes the metadata of the entity triggering the Callback. +Describes the metadata of the entity itself that triggers the Callback. ```go // RunInfo contains information about the running component that triggers callbacks. @@ -79,33 +79,33 @@ type RunInfo struct { } ``` -- Name: A name with business meaning, needs to be specified by the user, empty string if not specified. For different triggering entities: - - Component: When in a Graph, uses Node Name. When used standalone outside a Graph, manually set by user. See "Injecting RunInfo" and "Using Components Standalone" - - Graph Node: Uses Node Name `func WithNodeName(n string) GraphAddNodeOpt` +- Name: A name with business meaning, specified by the user. If not specified, it's an empty string. For different triggering entities: + - Component: When in a Graph, use Node Name. When used standalone outside a Graph, manually set by the user. See "Injecting RunInfo" and "Using Components Standalone" + - Graph Node: Use Node Name `func WithNodeName(n string) GraphAddNodeOpt` - Graph itself: - Top-level graph uses Graph Name `func WithGraphName(graphName string) GraphCompileOption` - - Nested internal graphs use the Node Name added when joining the parent graph -- Type: Determined by the specific component implementation: - - Components with interfaces: If Typer interface is implemented, uses GetType() method result. Otherwise uses reflection to get Struct/Func name. - - Lambda: If Type is specified with `func WithLambdaType(t string) LambdaOpt`, uses that, otherwise empty string. - - Graph Node: Uses the value of internal Component/Lambda/Graph. + - Internal nested graphs use the Node Name added when joining the parent graph +- Type: Specified by the specific component implementation: + - Components with interfaces: If Typer interface is implemented, use the result of GetType() method. Otherwise use reflection to get Struct/Func name. + - Lambda: If Type is specified using `func WithLambdaType(t string) LambdaOpt`, use that, otherwise it's an empty string. + - Graph Node: Use the value of the internal Component/Lambda/Graph. - Graph itself: Empty string. - Component: - Components with interfaces: Whatever interface it is - Lambda: Fixed value Lambda - - Graph Node: Uses the value of internal Component/Lambda/Graph. + - Graph Node: Use the value of the internal Component/Lambda/Graph. - Graph itself: Fixed value Graph / Chain / Workflow. (Previously there were StateGraph / StateChain, now integrated into Graph / Chain) ### Callback Input & Output -Essentially any type, because different Components have completely different inputs/outputs and internal states. +Essentially any type, because different Components have completely different inputs, outputs, and internal states. ```go type CallbackInput any type CallbackOutput any ``` -For specific components, there are more specific types, such as Chat Model: +For specific components, there are more specific types, such as Chat Model ```go // CallbackInput is the input for the model callback. @@ -133,61 +133,61 @@ type CallbackOutput struct { } ``` -In specific implementations of Chat Model, such as OpenAI Chat Model, component authors are recommended to pass specific Input/Output types to Callback Handlers, rather than Any. This exposes more specific, customized intermediate state information. +In specific implementations of Chat Model, such as OpenAI Chat Model, it's recommended for component authors to pass specific Input/Output types to the Callback Handler, rather than Any. This allows exposing more specific, customized intermediate state information. -If a Graph Node triggers the Callback, since the Node cannot access the component's internal intermediate state information, it can only get the inputs and outputs defined in the component interface, so that's all it can give to the Callback Handler. For Chat Model, that's []*schema.Message and *schema.Message. +If a Graph Node triggers the Callback, since the Node cannot get the component's internal intermediate state information and can only get the input and output specified in the component interface, it can only give these to the Callback Handler. For Chat Model, it's []*schema.Message and *schema.Message. -When Graph itself triggers Callback, the input and output are the overall input and output of the Graph. +When the Graph itself triggers the Callback, the input and output are the overall input and output of the Graph. ## Injecting Handlers Handlers need to be injected into the Context to be triggered. -### Injecting Handlers Globally +### Injecting Global Handlers -Inject global Handlers through `callbacks.AppendGlobalHandlers`. After injection, all callback triggering behaviors will automatically trigger these global Handlers. Typical scenarios are globally consistent, business-scenario-independent functions like tracing and logging. +Inject global Handlers through `callbacks.AppendGlobalHandlers`. After injection, all callback triggering behaviors will automatically trigger these global Handlers. Typical scenarios are global consistent, business-scenario-agnostic features like tracing and logging. -Not concurrency-safe. It's recommended to inject once during service initialization. +Not concurrency safe. It's recommended to inject once during service initialization. ### Injecting Handlers into Graph -Inject Handlers at graph runtime through `compose.WithCallbacks`, these Handlers will take effect for the entire current run of the graph, including all Nodes within the Graph and the Graph itself (as well as all nested graphs). +Inject Handlers at graph runtime through `compose.WithCallbacks`. These Handlers will take effect on the entire run of the graph, including all Nodes within the Graph and the Graph itself (as well as all nested graphs). -Inject Handlers to a specific Node of the top-level Graph through `compose.WithCallbacks(...).DesignateNode(...)`. When this Node itself is a nested Graph, it will be injected into this nested Graph itself and its internal Nodes. +Inject Handlers to a specific Node of the top-level Graph through `compose.WithCallbacks(...).DesignateNode(...)`. When this Node itself is a nested Graph, it will be injected into the nested Graph itself and its internal Nodes. Inject Handlers to a specific Node of an internally nested Graph through `compose.WithCallbacks(...).DesignateNodeWithPath(...)`. ### Injecting Handlers Outside Graph -If you don't want to use Graph but want to use Callbacks: +If you don't want to use Graph but want to use Callbacks, then: -Obtain a new Context and inject Handlers and RunInfo through `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)`. +Get a new Context through `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` and inject Handlers and RunInfo. ### Handler Inheritance -Same as child Context inheriting all Values from parent Context, child Context also inherits all Handlers from parent Context. For example, if the Context passed in when running a Graph already has Handlers, these Handlers will be inherited and take effect for this entire Graph run. +Similar to how a child Context inherits all Values from the parent Context, a child Context also inherits all Handlers from the parent Context. For example, if the Context passed when running a Graph already has Handlers, these Handlers will be inherited and take effect throughout this run of the entire Graph. ## Injecting RunInfo -RunInfo also needs to be injected into the Context to be provided to the Handler when callbacks are triggered. +RunInfo also needs to be injected into the Context to be given to the Handler when triggering callbacks. -### Graph-Managed RunInfo +### Graph Managed RunInfo Graph automatically injects RunInfo for all internal Nodes. The mechanism is that each Node's execution is a new child Context, and Graph injects the corresponding Node's RunInfo into this new Context. ### Injecting RunInfo Outside Graph -If you don't want to use Graph but want to use Callbacks: +If you don't want to use Graph but want to use Callbacks, then: -Obtain a new Context and inject Handlers and RunInfo through `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)`. +Get a new Context through `InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler)` and inject Handlers and RunInfo. -Obtain a new Context through `ReuseHandlers(ctx context.Context, info *RunInfo)`, reusing the Handlers in the previous Context and setting new RunInfo. +Get a new Context through `ReuseHandlers(ctx context.Context, info *RunInfo)` to reuse the Handlers in the previous Context and set new RunInfo. ## Triggering Methods -### Component Implementation Internal Triggering (Component Callback) +### Triggering Inside Component Implementation (Component Callback) In the component implementation code, call `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), OnEndWithStreamOutput()` from the callbacks package. Taking Ark's ChatModel implementation as an example, in the Generate method: @@ -259,14 +259,14 @@ func (cm *ChatModel) Stream(ctx context.Context, in []*schema.Message, opts ...f } ``` -You can see that Generate call triggers OnEnd, while Stream call triggers OnEndWithStreamOutput: +As you can see, Generate calls trigger OnEnd, while Stream calls trigger OnEndWithStreamOutput: -When triggering Callbacks inside component implementations: +When triggering Callbacks inside component implementation: -- **When component input is StreamReader, trigger OnStartWithStreamInput, otherwise trigger OnStart** -- **When component output is StreamReader, trigger OnEndWithStreamOutput, otherwise trigger OnEnd** +- **When the component input is StreamReader, trigger OnStartWithStreamInput, otherwise trigger OnStart** +- **When the component output is StreamReader, trigger OnEndWithStreamOutput, otherwise trigger OnEnd** -Components that implement callback triggering internally should implement the Checker interface, with IsCallbacksEnabled returning true, to communicate "I have implemented callback triggering internally" to the outside: +Components that implement callback triggering internally should implement the Checker interface, with IsCallbacksEnabled returning true, to communicate "I have implemented callback triggering internally": ```go // Checker tells callback aspect status of component's implementation @@ -277,32 +277,32 @@ type Checker interface { } ``` -If a component implementation doesn't implement the Checker interface, or IsCallbacksEnabled returns false, it can be assumed that the component doesn't trigger callbacks internally, and Graph Node needs to be responsible for injection and triggering (when used within a Graph). +If a component implementation doesn't implement the Checker interface, or IsCallbacksEnabled returns false, it can be assumed that the component doesn't trigger callbacks internally, and the Graph Node needs to be responsible for injection and triggering (when used within a Graph). ### Graph Node Triggering (Node Callback) -When a Component is orchestrated into a Graph, it becomes a Node. At this point, if the Component itself triggers callbacks, the Node reuses the Component's callback handling. Otherwise, the Node wraps callback handler trigger points around the Component. These points correspond to the Component's streaming paradigm. For example, a ChatModelNode wraps OnStart/OnEnd/OnError around the Generate method, and OnStart/OnEndWithStreamOutput/OnError around the Stream method. +When a Component is orchestrated into a Graph, it becomes a Node. At this point, if the Component itself triggers callbacks, the Node reuses the Component's callback handling. Otherwise, the Node wraps callback handler trigger points around the Component. These points correspond to the Component's own streaming paradigms. For example, a ChatModelNode will have OnStart/OnEnd/OnError wrapped around the Generate method, and OnStart/OnEndWithStreamOutput/OnError wrapped around the Stream method. -At Graph runtime, components run in Invoke or Transform paradigm, and based on the component's specific business streaming paradigm, call the corresponding component methods. For example, when Graph runs with Invoke, Chat Model Node runs with Invoke, calling the Generate method. When Graph runs with Stream, Chat Model Node runs with Transform, but since Chat Model's business streaming paradigm doesn't have Transform, it automatically falls back to calling the Stream method. Therefore: +When the Graph runs, each component runs in either Invoke or Transform paradigm, and calls the corresponding component method based on the component's specific business streaming paradigm. For example, when Graph runs with Invoke, the Chat Model Node runs with Invoke, calling the Generate method. When Graph runs with Stream, the Chat Model Node runs with Transform, but since Chat Model's business streaming paradigm doesn't have Transform, it automatically falls back to calling the Stream method. Therefore: -**Which timing point (OnStart vs OnStartWithStreamInput) a Graph Node specifically triggers depends on two factors: the component implementation's business streaming paradigm and the Graph's execution mode.** +**Which specific point the Graph Node triggers (OnStart or OnStartWithStreamInput) depends on both the component implementation's business streaming paradigm and the Graph's running method.** -For detailed introduction to Eino streaming programming, see [Eino Streaming Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) +For a detailed introduction to Eino streaming programming, see [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) ### Graph Self-Triggering (Graph Callback) -Graph triggers Callback Handlers at its own start, end, and error timings. If 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 **Graph internally always executes as Invoke or Transform**. See [Eino Streaming Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) +Graph triggers Callback Handlers at its own start, end, and error timings. If Graph is called with Invoke, it triggers OnStart/OnEnd/OnError. If called with Stream/Collect/Transform, it triggers OnStartWithStreamInput/OnEndWithStreamOutput/OnError. This is because **Graph internally always executes with Invoke or Transform**. See [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) -It's worth noting: graph is also a type of component, so graph callback is also a special form of component callback. According to the Node Callback definition, when the component inside a Node implements awareness and handling of triggering timings, the Node directly reuses the Component's implementation and won't implement Node Callback. This means when a graph is added to another Graph as a Node via AddGraphNode, this Node reuses the internal graph's graph callback. +It's worth noting that: graph is also a type of component, so graph callback is also a special form of component callback. According to the Node Callback definition, when the component inside a Node has implemented awareness and handling of triggering timing, the Node will directly reuse the Component's implementation and won't implement Node Callback again. This means when a graph is added to another Graph as a Node through AddGraphNode, this Node will reuse the internal graph's graph callback. ## Parsing Callback Input & Output -From the above, we know that Callback Input & Output's underlying type is Any, but different component types may pass their own specific types when actually triggering callbacks. And in the Callback Handler interface definition, the parameters of each method are also Any-typed Callback Input & Output. +As mentioned above, the underlying type of Callback Input & Output is Any, but different component types may pass their own specific types when actually triggering callbacks. And in the Callback Handler interface definition, the parameters of each method are also Any type Callback Input & Output. Therefore, specific Handler implementations need to do two things: -1. Determine which component type is currently triggering the callback based on RunInfo, such as RunInfo.Component == "ChatModel", or RunInfo.Type == "xxx Chat Model". -2. Convert the any-typed Callback Input & Output to the corresponding specific type, taking RunInfo.Component == "ChatModel" as an example: +1. Determine which component type currently triggered the callback based on RunInfo, such as RunInfo.Component == "ChatModel", or RunInfo.Type == "xxx Chat Model". +2. Convert the any type Callback Input & Output to the corresponding specific type. Taking RunInfo.Component == "ChatModel" as an example: ```go // ConvCallbackInput converts the callback input to the model callback input. @@ -334,7 +334,7 @@ func ConvCallbackOutput(src callbacks.CallbackOutput) *CallbackOutput { } ``` -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 a specific type, it's indeed somewhat complex. To reduce the repetitive work of writing glue code, we provide two convenient tool functions for implementing Handlers. +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 a specific type, it's indeed complex. To reduce the repetitive work of writing glue code, we provide two convenient utility functions for implementing Handlers. ## Handler Implementation Methods @@ -342,7 +342,7 @@ Besides directly implementing the Handler interface, Eino provides two convenien ### HandlerHelper -If the user's Handler only focuses on specific component types, such as in ReActAgent scenarios only focusing on ChatModel and Tool, it's recommended to use HandlerHelper to quickly create typed Callback Handlers: +If the user's Handler only focuses on specific component types, such as the ReactAgent scenario, only focusing on ChatModel and Tool, it's recommended to use HandlerHelper to quickly create specifically typed Callback Handlers: ```go import ucb "github.com/cloudwego/eino/utils/callbacks" @@ -350,7 +350,7 @@ import ucb "github.com/cloudwego/eino/utils/callbacks" handler := ucb.NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler() ``` -Where modelHandler is Chat Model component's further encapsulation of callback handler: +Where modelHandler is a further encapsulation of the callback handler for the Chat Model component: ```go // from package utils/callbacks @@ -364,15 +364,15 @@ type ModelCallbackHandler struct { } ``` -The above ModelCallbackHandler encapsulates three operations: +The ModelCallbackHandler above encapsulates three operations: -1. No longer need to determine RunInfo.Component to select callbacks triggered by ChatModel, as automatic filtering is already done. -2. Only requires implementing the triggering timings supported by Chat Model component, here removing the unsupported OnStartWithStreamInput. Also, if the user only cares about some of the four timings supported by Chat Model, such as only OnStart, they can implement only OnStart. -3. Input / Output are no longer Any types, but already converted model.CallbackInput, model.CallbackOutput. +1. No longer need to check RunInfo.Component to select callbacks triggered by ChatModel, as automatic filtering is already done. +2. Only requires implementing triggering timings supported by the Chat Model component. Here the unsupported OnStartWithStreamInput is removed. Also, if the user only cares about some of the four timings supported by Chat Model, such as only OnStart, they can implement only OnStart. +3. Input/Output are no longer Any type, but already converted model.CallbackInput, model.CallbackOutput. -HandlerHelper supports all official components, the current list is: ChatModel, ChatTemplate, Retriever, Indexer, Embedding, Document.Loader, Document.Transformer, Tool, ToolsNode. +HandlerHelper supports all official components. The current list is: ChatModel, ChatTemplate, Retriever, Indexer, Embedding, Document.Loader, Document.Transformer, Tool, ToolsNode. -For Lambda, Graph, Chain and other "components" with uncertain input/output types, HandlerHelper can also be used, but can only achieve point 1 above, i.e., automatic filtering by component type, points 2 and 3 still need to be implemented by the user: +For "components" like Lambda, Graph, Chain whose input and output types are uncertain, HandlerHelper can also be used, but only achieves point 1 above, i.e., automatic filtering by component type. Points 2 and 3 still need to be implemented by the user: ```go import ucb "github.com/cloudwego/eino/utils/callbacks" @@ -380,11 +380,11 @@ import ucb "github.com/cloudwego/eino/utils/callbacks" handler := ucb.NewHandlerHelper().Lambda(callbacks.Handler).Graph(callbacks.Handler)...Handler() ``` -At this point, NewHandlerHelper().Lambda() needs to pass in callbacks.Handler which can be implemented using the HandlerBuilder below. +In this case, the callbacks.Handler passed to NewHandlerHelper().Lambda() can be implemented using HandlerBuilder below. ### HandlerBuilder -If the user's Handler needs to focus on multiple component types, but only needs to focus on some triggering timings, HandlerBuilder can be used: +If the user's Handler needs to focus on multiple component types but only needs to focus on some triggering timings, HandlerBuilder can be used: ```go import "github.com/cloudwego/eino/callbacks" @@ -437,7 +437,7 @@ func main() { } ``` -- Inject Handlers at runtime through WithHandlers option, and specify effective Nodes / nested internal Graphs / internal Graph Nodes through DesignateNode or DesignateNodeByPath. +- Inject Handlers at runtime through WithHandlers option, specify effective Nodes / nested internal Graphs / internal Graph's Nodes through DesignateNode or DesignateNodeByPath. ```go package main @@ -482,9 +482,9 @@ func main() { ### Using Outside Graph -This scenario is: not using Graph/Chain/Workflow orchestration capabilities, but directly calling ChatModel/Tool/Lambda and other components with code, and hoping these components can successfully trigger Callback Handlers. +This scenario is: not using orchestration capabilities like Graph/Chain/Workflow, but calling ChatModel/Tool/Lambda and other components directly with code, and hoping these components can successfully trigger Callback Handlers. -The problem users need to solve in this scenario is: manually setting correct RunInfo and Handlers, because there's no Graph to help users automatically set RunInfo and Handlers. +The problem users need to solve in this scenario is: manually setting the correct RunInfo and Handlers, because there's no Graph to automatically set RunInfo and Handlers for the user. Complete example: @@ -499,7 +499,7 @@ import ( ) func innerLambda(ctx context.Context, input string) (string, error) { - // As ComponentB's implementer: add default RunInfo when entering the component (Name cannot be given a default value) + // As the implementer of ComponentB: add default RunInfo when entering the component (Name cannot be given a default value) ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda) ctx = callbacks.OnStart(ctx, input) out := "inner:" + input @@ -508,18 +508,18 @@ func innerLambda(ctx context.Context, input string) (string, error) { } func outerLambda(ctx context.Context, input string) (string, error) { - // As ComponentA's implementer: add default RunInfo when entering the component + // As the implementer of ComponentA: add default RunInfo when entering the component ctx = callbacks.EnsureRunInfo(ctx, "Lambda", compose.ComponentOfLambda) ctx = callbacks.OnStart(ctx, input) - // Recommended: replace RunInfo before calling, ensuring inner component gets correct name/type/component + // Recommended: replace RunInfo before calling, ensure inner component gets 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" + out1, _ := innerLambda(ctxInner, input) // Inner RunInfo.Name = "ComponentB" - // Without replacement: framework clears RunInfo, can only rely on EnsureRunInfo to add default values (Name is empty) - out2, _ := innerLambda(ctx, input) // inner RunInfo.Name == "" + // Not replaced: framework clears RunInfo, can only rely on EnsureRunInfo to add default values (Name is empty) + out2, _ := innerLambda(ctx, input) // Inner RunInfo.Name == "" final := out1 + "|" + out2 ctx = callbacks.OnEnd(ctx, final) @@ -538,21 +538,21 @@ func main() { } ``` -Explanation of the above sample code: +Explanation of the sample code above: -- Initialization: When using components outside graph/chain, use InitCallbacks to set the first RunInfo and Handlers, so subsequent component execution can get the complete callback context. -- Internal calls: Before component A calls component B internally, use ReuseHandlers to replace RunInfo (keeping original handlers), ensuring B's callbacks get correct Type/Component/Name. -- Consequences of not replacing: After a complete set of Callbacks is triggered, Eino clears the RunInfo in the current ctx. At this point, because RunInfo is empty, Eino won't trigger Callbacks anymore; Component B's developer can only use EnsureRunInfo in their own implementation to add default values for Type/Component, to ensure RunInfo is non-empty and roughly correct, thus successfully triggering Callbacks. But cannot give a reasonable Name, so RunInfo.Name will be an empty string. +- Initialization: When using components outside graph/chain, use InitCallbacks to set the first RunInfo and Handlers, so subsequent component execution can get complete callback context. +- Internal calls: Before calling component B inside component A, use ReuseHandlers to replace RunInfo (retaining original handlers), ensuring B's callback gets correct Type/Component/Name. +- Consequences of not replacing: After a complete set of Callbacks is triggered, Eino clears the RunInfo in the current ctx. At this point, because RunInfo is empty, Eino will no longer trigger Callbacks; component B's developer can only use EnsureRunInfo in their own implementation to add default values for Type/Component to ensure RunInfo is non-empty and roughly correct, thus successfully triggering Callbacks. But cannot give a reasonable Name, so RunInfo.Name will be an empty string. -### Component Nesting Usage +### Nested Component Usage -Scenario: Inside one component, such as Lambda, manually call another component, such as ChatModel. +Scenario: Manually calling another component, such as ChatModel, inside a component, such as Lambda. At this point, if the outer component's ctx has callback handlers, because this ctx is also passed to the inner component, the inner component will also receive the same callback handlers. Distinguishing by "whether you want the inner component to trigger callbacks": -1. Want to trigger: Basically equivalent to the situation in the previous section, it's recommended to manually set `RunInfo` for the inner component through `ReuseHandlers`. +1. Want to trigger: Basically the same as the previous section, it's recommended to manually set `RunInfo` for the inner component through `ReuseHandlers`. ```go package main @@ -570,17 +570,17 @@ import ( // Outer Lambda, manually 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 explicitly set RunInfo for inner component + // 1) Reuse outer handlers, and explicitly set RunInfo for inner component innerCtx := callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{ - Type: "InnerCM", // Customizable - Component: components.ComponentOfChatModel, // Mark component type - Name: "inner-chat-model", // Customizable + Type: "InnerCM", // customizable + Component: components.ComponentOfChatModel, // mark component type + Name: "inner-chat-model", // customizable }) - // 2) Build input messages + // 2) Construct input messages msgs := []*schema.Message{{Role: schema.User, Content: input}} - // 3) Call ChatModel (internally triggers corresponding callbacks) + // 3) Call ChatModel (will trigger corresponding callbacks internally) out, err := cm.Generate(innerCtx, msgs) if err != nil { return "", err @@ -590,19 +590,19 @@ func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda { } ``` -The above code assumes "the inner ChatModel's Generate method internally has already called OnStart, OnEnd, OnError methods". If not, you need to call these methods "on behalf of the inner component" inside the outer component: +The code above assumes "the inner ChatModel's Generate method has already called OnStart, OnEnd, OnError internally". If not, you need to call these methods "on behalf of the inner component" inside the outer component: ```go func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda { return compose.InvokableLambda(func(ctx context.Context, input string) (string, error) { - // Reuse outer handlers and explicitly set RunInfo for inner component + // Reuse outer handlers, and explicitly set RunInfo for inner component ctx = callbacks.ReuseHandlers(ctx, &callbacks.RunInfo{ Type: "InnerCM", Component: components.ComponentOfChatModel, Name: "inner-chat-model", }) - // Build input messages + // Construct input messages msgs := []*schema.Message{{Role: schema.User, Content: input}} // Explicitly trigger OnStart @@ -624,7 +624,7 @@ func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda { } ``` -1. Don't want to trigger: This assumes the inner component implements `IsCallbacksEnabled()` and returns true, and internally calls `EnsureRunInfo`. At this point, inner callbacks will trigger by default. If you don't want them to trigger, the simplest way is to remove handlers from ctx, such as passing a new ctx to the inner component: +1. Don't want to trigger: This assumes the inner component implements `IsCallbacksEnabled()` and returns true, and calls `EnsureRunInfo` internally. At this point, inner callbacks will trigger by default. If you don't want to trigger, the simplest way is to remove the handler from ctx, such as passing a new ctx to the inner component: ```go package main @@ -652,7 +652,7 @@ func OuterLambdaCallsChatModel(cm model.BaseChatModel) *compose.Lambda { } ``` - 1. But sometimes users may want to "only not trigger a specific callback handler, but still trigger other callback handlers". The recommended approach is to add code in this callback handler to filter out inner components by RunInfo: + 1. But sometimes users may want to "not trigger a specific callback handler, but still trigger other callback handlers". The recommended approach is to add code in this callback handler to filter out the inner component based on RunInfo: ```go package main @@ -673,7 +673,7 @@ func newSelectiveHandler() callbacks.Handler { 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" { - // Filter target: inner ChatModel, return directly without processing + // Filter target: inner ChatModel, return directly, do nothing return ctx } log.Printf("[OnStart] %s/%s (%s)", info.Type, info.Name, info.Component) @@ -682,7 +682,7 @@ func newSelectiveHandler() callbacks.Handler { 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" { - // Filter target: inner ChatModel, return directly without processing + // Filter target: inner ChatModel, return directly, do nothing return ctx } log.Printf("[OnEnd] %s/%s (%s)", info.Type, info.Name, info.Component) @@ -691,12 +691,12 @@ func newSelectiveHandler() callbacks.Handler { Build() } -// Composition example: outer call wants to trigger, specific handler filters out inner ChatModel through RunInfo +// Combination example: outer call wants to trigger, specific handler filters out inner ChatModel through RunInfo func Example(cm model.BaseChatModel) (compose.Runnable[string, string], error) { handler := newSelectiveHandler() chain := compose.NewChain[string, string](). - AppendLambda(OuterLambdaCallsChatModel(cm)) // Internally will ReuseHandlers + RunInfo + AppendLambda(OuterLambdaCallsChatModel(cm)) // Will ReuseHandlers + RunInfo internally return chain.Compile( context.Background(), @@ -708,30 +708,30 @@ func Example(cm model.BaseChatModel) (compose.Runnable[string, string], error) { ### Reading and Writing Input & Output in Handler -When input & output flow through the graph, they are direct variable assignments. As shown in the figure below, NodeA.Output, NodeB.Input, NodeC.Input, and the input & output obtained in each Handler, if they are reference types like struct pointers or Maps, they are all the same piece of data. Therefore, whether in Node or Handler, it's not recommended to modify Input & Output, as it will cause concurrency issues: even in synchronous situations, Node B and Node C are concurrent, causing internal handler1 and handler2 to be concurrent. When there's asynchronous processing logic, there are more possible concurrency scenarios. +Input & output flow in the graph through direct variable assignment. As shown in the figure below, NodeA.Output, NodeB.Input, NodeC.Input, and the input & output obtained in each Handler, if they are reference types like struct pointers or Maps, are all the same piece of data. Therefore, modifying Input & Output is not recommended in either Node or Handler, as it will cause concurrency issues: even in synchronous situations, Node B and Node C may be concurrent, causing handler1 and handler2 to be concurrent. With asynchronous processing logic, there are more possible concurrency scenarios. -In stream passing scenarios, all downstream nodes and handlers' input streams are streams obtained from StreamReader.Copy(n), which can be read independently. However, each chunk in the stream is direct variable assignment, if the chunk is a reference type like struct pointer or Map, each copied stream reads the same piece of data. Therefore, in Node and Handler, it's also not recommended to modify stream chunks, there are concurrency issues. +In stream passing scenarios, input streams in all downstream nodes and handlers are streams obtained from StreamReader.Copy(n), which can be read independently. However, each chunk in the stream is direct variable assignment. If the chunk is a reference type like struct pointer or Map, the same piece of data is read by each copied stream. Therefore, modifying stream chunks in Node and Handler is also not recommended due to concurrency issues. ### Passing Information Between Handlers -Between different timings of the same Handler, information can be passed through ctx, such as returning a new context through context.WithValue in OnStart, and then retrieving this value from context in OnEnd. +Information can be passed between different timings of the same Handler through ctx, such as returning a new context through context.WithValue in OnStart, and retrieving this value from the context in OnEnd. -Between different Handlers, there's no guarantee of execution order, so it's not recommended to pass information between different Handlers through the above mechanism. Essentially, there's no guarantee that the context returned by one Handler will definitely enter the function execution of the next Handler. +Between different Handlers, there's no guaranteed execution order, so passing information between different Handlers through the above mechanism is not recommended. Essentially, there's no guarantee that the context returned by one Handler will definitely enter the function execution of the next Handler. -If you need to pass information between different Handlers, the recommended approach is to set a global, request-scoped variable in the outermost context (such as the context passed in when graph executes) as a shared space for storing and retrieving common information, and read and update this shared variable as needed in each Handler. Users need to ensure the concurrency safety of this shared variable themselves. +If information needs to be passed between different Handlers, the recommended approach is to set a global, request-level variable in the outermost context (such as the context passed when executing the graph) as a common information storage space, and read and update this common variable as needed in each Handler. Users need to ensure the concurrency safety of this common variable themselves. -### Remember to Close Streams +### Streams Must Be Closed -Taking the existence of ChatModel, a node with true streaming output, as an example, when there are Callback aspects, ChatModel's output stream: +Taking the presence of nodes like ChatModel that have true stream output as an example, when there are Callback aspects, the ChatModel's output stream: -- Needs to be consumed by downstream nodes as input, and also consumed by Callback aspects -- A frame (Chunk) in a stream can only be consumed by one consumer, i.e., streams are not broadcast models +- Needs to be consumed both by downstream nodes as input and by Callback aspects +- A frame (Chunk) in a stream can only be consumed by one consumer, i.e., streams are not broadcast model -So at this point, the stream needs to be copied, the copy relationship is as follows: +So the stream needs to be copied at this point, with the copy relationship as follows: 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 index 2a2f01ead29..0fa96f3c947 100644 --- 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 @@ -1,27 +1,29 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino: Interrupt & CheckPoint Manual' +title: 'Eino: Interrupt & CheckPoint User Manual' weight: 7 --- > 💡 -> Note: A bug in v0.3.26 broke CheckPoint serialization. For new CheckPoint usage, use v0.3.26+ (preferably latest). +> Note: In version v0.3.26, due to a code writing error, the serialization content of CheckPoint broke compatibility. For new integrations using CheckPoint after v0.3.26, it's recommended to use the latest version directly. > -> 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. +> Eino provides a compatibility branch. Businesses that use checkpoint and are on versions lower than v0.3.26 should first upgrade to the compatibility branch when upgrading eino, then upgrade to the main branch after old data expires. +> +> Since the compatibility branch introduces additional performance overhead and generally business agent checkpoints have a relatively short validity period, the branch has not been merged into the main branch. ## Introduction -`Interrupt & CheckPoint` lets you pause a Graph at specified locations and resume later. For `StateGraph`, you can modify `State` before resuming. +Using the Interrupt & CheckPoint feature, you can pause Graph execution at specified positions and resume from the breakpoint later. For StateGraph, you can also modify State before resuming from the breakpoint. > 💡 -> 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). +> Breakpoint resumption can only restore input and data generated by each node at runtime. You need to ensure the Graph orchestration is exactly the same, and pass in CallOptions completely again (should be kept consistent unless there are special circumstances, except for cases like relying on CallOption to pass data during Resume). ## Using Static Interrupt -Static Interrupt supports pausing before or after specified nodes. Set `WithInterruptAfterNodes` and `WithInterruptBeforeNodes` at compile: +Static Interrupt supports pausing the Graph before or after a specified Node executes. Pass in WithInterruptAfterNodes and WithInterruptBeforeNodes Options during Compile to set Interrupts: ```go import ( @@ -30,9 +32,9 @@ import ( func main() { g := NewGraph[string, string]() - err := g.AddLambdaNode("node1", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) {/*invokable func*/}) + 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*/}) + 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 @@ -45,27 +47,27 @@ func main() { ``` > 💡 -> Tip: Currently only compile-time static breakpoints are supported. If you need request-time configuration, please open an issue. +> Currently only static breakpoints can be set during Compile. If you need to set them during requests, feel free to raise the requirement~ -Extract interrupt info from the run error: +You can get whether the current run was Interrupted and the Interrupt information from the error returned by the run: ```go // compose/checkpoint.go -type InterruptInfo struct { +**type **InterruptInfo **struct **{ State any BeforeNodes []string AfterNodes []string RerunNodes []string - RerunNodesExtra map[string]any - SubGraphs map[string]*InterruptInfo + RerunNodesExtra **map**[string]any + SubGraphs **map**[string]*InterruptInfo InterruptContexts []*InterruptCtx } func ExtractInterruptInfo(err error) (info *InterruptInfo, existed bool) {} ``` -Example: +For example: ```go import "github.com/cloudwego/eino/compse" @@ -86,28 +88,28 @@ if err != nil { ``` > 💡 -> During interrupt, the output is empty and should be ignored. +> When Interrupted, output is an empty value and has no meaning. ## Using CheckPoint -CheckPoint records Graph runtime state to support resuming. +CheckPoint records Graph running state. Using CheckPoint allows resuming execution after Interrupt. -### Implement CheckpointStore +### Implementing 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. +CheckPointStore is a KV storage interface with key type string and value type []byte. We don't provide encapsulation or default implementation; users need to implement it themselves to store 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) + 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 +### Registering Serialization Methods -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`: +CheckPoint saving and reading involves serialization and deserialization of Graph node inputs, outputs, and State. When only using simple types or eino built-in types (such as Message or Document), users don't need additional operations; when introducing custom structs, you need to register types in advance. Eino provides the registration method `schema.RegisterName`: ```go package main @@ -128,12 +130,12 @@ func init() { } ``` -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. +After registration, type information will be additionally recorded during serialization, so during deserialization, even without specifying the type (such as deserializing to interface{}), Eino can deserialize to the correct type. The key in the registration method uniquely identifies this type. Once the key is determined, it must not be changed, otherwise persisted checkpoints cannot be correctly restored. > 💡 -> Struct unexported fields are inaccessible and thus not stored/restored. +> Unexported fields of structs cannot be accessed, so they won't be 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. +By default, eino's built-in serialization functionality is used. At this time, if the registered type implements json Marshaler and Unmarshaler, the type's serialization and deserialization will use custom methods. ``` // encoding/json @@ -147,7 +149,7 @@ type Unmarshaler interface { } ``` -Eino also provides an option to use `gob` serialization: +Eino also provides an option to change the serialization method to gob: ```go r, err := compose.NewChain[*AgentInput, Message](). @@ -160,11 +162,11 @@ r, err := compose.NewChain[*AgentInput, Message](). compose.WithSerializer(&gobSerializer{})) ``` -Choose based on preference; avoid switching later, as historical data will be incompatible. +Users can choose based on preference. Once chosen, it's not recommended to change easily as historical data won't be compatible. -### Enable CheckPoint +### Enabling CheckPoint -Bind `CheckpointStore` at compile and set interrupt nodes if needed: +After creating CheckPointStore, pass it as an Option when Compiling Graph to bind CheckPointer to Graph: ```go import ( @@ -181,7 +183,7 @@ func main() { } ``` -At request time, provide a checkpoint ID: +After that, you can introduce CheckPoint during requests through CallOption: ``` // compose/checkpoint.go @@ -189,13 +191,13 @@ At request time, provide a checkpoint ID: func WithCheckPointID(checkPointID string) 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. +Checkpoint id will be used as the key for CheckPointStore. When the graph runs, it checks whether CheckPointStore has this id. If it exists, it resumes from the checkpoint; when interrupted, it saves the graph state to this id. ## Dynamic Interrupt -Nodes can trigger dynamic interrupts by returning special errors. +Nodes can dynamically trigger Interrupt by returning special errors: -### Prior to Eino v0.7.0 +### Before eino v0.7.0 ``` // eino/compose/interrupt.go @@ -204,14 +206,16 @@ Nodes can trigger dynamic interrupts by returning special errors. var InterruptAndRerun = errors.New("interrupt and rerun") // emit an interrupt signal with extra info -func NewInterruptAndRerunErr(extra any) error +**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. +When Eino Graph receives this error returned by a node, an interrupt will occur. When resuming, this node will be run again. Before re-running, StateModifier will be called to modify state (if configured). + +In this case, when re-running the node, the input will be replaced with an empty value instead of the original input. If the original input is still needed when re-running, it needs to be saved to State in advance. -### From Eino v0.7.0 onward +### In eino v0.7.0 and later -Support is added for local state persistence, exposing inner interrupt signals, and parallel interrupts: +Added support for "saving local state", "exposing internal interrupt signals", and "parallel interrupts": ``` // eino/compose/interrupt.go @@ -229,40 +233,40 @@ func StatefulInterrupt(ctx context.Context, info any, state any) error 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) +For detailed design, see: [Eino human-in-the-loop Framework: Technical Architecture Guide](/docs/eino/core_modules/eino_adk/agent_hitl) -## External Active Interrupt +## External Proactive Interrupt -Sometimes, we want to actively trigger an interrupt from outside the Graph, save the state, and resume later. These scenarios may include graceful instance shutdown, etc. In such cases, you can call `WithGraphInterrupt` to get a ctx and an interrupt function. The ctx is passed to `graph.Invoke()` and other run methods, while the interrupt function is called when you want to actively interrupt: +Sometimes, we want to proactively trigger an interrupt from outside the Graph, save the scene, and resume later. These scenarios may include graceful instance shutdown, etc. In this case, you can call `WithGraphInterrupt` to get a ctx and an interrupt function. The ctx is passed to running methods like `graph.Invoke()`, and the interrupt function is called when the user wants to proactively interrupt: ```go // from compose/graph_call_options.go -// WithGraphInterrupt creates a context with graph cancellation support. -// When the returned context is used to invoke a graph or workflow, calling the interrupt function will trigger an interrupt. -// The graph will wait for current tasks to complete by default. -func WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt func(opts ...GraphInterruptOption)) {} +_// WithGraphInterrupt creates a context with graph cancellation support._ +_// When the returned context is used to invoke a graph or workflow, calling the interrupt function will trigger an interrupt._ +_// The graph will wait for current tasks to complete by default._ +**func **WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt **func**(opts ...GraphInterruptOption)) {} ``` -When actively calling the interrupt function, you can pass parameters such as timeout: +When proactively calling the interrupt function, you can pass parameters like timeout: ```go // from compose/graph_call_options.go -// WithGraphInterruptTimeout specifies the max waiting time before generating an interrupt. -// After the max waiting time, the graph will force an interrupt. Any unfinished tasks will be re-run when the graph is resumed. -func WithGraphInterruptTimeout(timeout time.Duration) GraphInterruptOption { - return func(o *graphInterruptOptions) { +_// WithGraphInterruptTimeout specifies the max waiting time before generating an interrupt._ +_// After the max waiting time, the graph will force an interrupt. Any unfinished tasks will be re-run when the graph is resumed._ +**func **WithGraphInterruptTimeout(timeout time.Duration) GraphInterruptOption { + **return func**(o *graphInterruptOptions) { o.timeout = &timeout } } ``` -When an external interrupt is triggered, the node has no opportunity to save local state (including the node's input), so Eino automatically saves the input of externally interrupted nodes and restores it on the next execution. For non-external interrupt scenarios, when a node initiates an interrupt internally, saving the input is the responsibility of each node, which can be done by saving to the graph state or using `compose.StatefulInterrupt` to save local state. +When externally triggered interrupt occurs, nodes internally don't have the opportunity to save local state (including the node's input), so eino will automatically save the input of externally interrupted nodes and automatically restore it on the next execution. For non-externally triggered interrupt scenarios, saving input is each node's responsibility when initiating interrupt internally, which can be done by saving to graph state or using `compose.StatefulInterrupt` to save local state. ## CheckPoint in Streaming -Streaming checkpoints require concatenation of chunks. Register a concat function: +Streaming needs to concatenate data streams when saving CheckPoint, so concatenation methods need to be registered: ```go // compose/stream_concat.go @@ -273,7 +277,7 @@ type TestStruct struct { Body string } -// RegisterStreamChunkConcatFunc is not thread-safe; call during initialization +// RegisterStreamChunkConcatFunc is not thread-safe, needs to be used during initialization phase RegisterStreamChunkConcatFunc(func(ss []TestStruct)(TestStruct, error){ ret := TestStruct{Body:""} for i := range ss { @@ -283,11 +287,11 @@ RegisterStreamChunkConcatFunc(func(ss []TestStruct)(TestStruct, error){ }) ``` -Eino provides defaults for `*schema.Message`, `[]*schema.Message`, and `string`. +Eino provides concat methods for *schema.Message, []*schema.Message and string by default. -## Interrupt & CheckPoint in Nested Graphs +## Interrupt&CheckPoint in Nested Graphs -When the parent sets a `CheckpointStore`, use `WithGraphCompileOptions` during `AddGraphNode` to configure child interrupts: +With the premise that the parent graph passes in CheckPointer, when using AddGraphNode, passing InterruptNodes through WithGraphCompileOptions can enable the sub-graph's Interrupt&CheckPoint. If the parent graph doesn't have CheckPointer set, an error will be reported during Compile. ```go /* graph compose code @@ -300,15 +304,15 @@ g.AddGraphNode("node1", subGraph, WithGraphCompileOptions( g.Compile(ctx, WithCheckPointStore(cp)) ``` -If a child interrupts, resuming modifies the child's state. TODO: clarify Path usage in `StateModifier`. +If interrupted in a sub-graph, the state modified when resuming should be the sub-graph state. TODO, explain Path usage in StateModifier -## Recovery +## Resume -Recovery: subsequent graph runs after an interrupt and checkpoint save. +Resume: Graph runs after Interrupt and checkpoint saving. -### Prior to Eino v0.7.0 +### Before eino v0.7.0 -Modify `State` to affect resume behavior. +Influence resume behavior by modifying State. ```go // compose/checkpoint.go @@ -317,7 +321,7 @@ 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. +StateModifier takes effect when Graph resumes running, allowing modification of State before running. Path takes effect in nested graphs; treat as empty array for non-nested. ```go /* graph compose and compile @@ -339,11 +343,11 @@ _, err = runner.Invoke(ctx, input/*unused*/, ``` > 💡 -> During resume, input is ignored; pass a zero value. +> Input is not read during Resume, so passing empty input is fine at this time. -### From Eino v0.7.0 onward +### In eino v0.7.0 and later -In addition to `StateModifier`, you can selectively resume particular interrupt points and provide resume data: +Besides StateModifier, you can also selectively resume a specific interrupt point, and directly pass "resume data" to the specified "interrupt point": ```go // specifically resume particular interrupt point(s), @@ -357,17 +361,18 @@ func ResumeWithData(ctx context.Context, interruptID string, data any) context.C func BatchResumeWithData(ctx context.Context, resumeData map[string]any) context.Context ``` -`InterruptID` is retrieved from the interrupt error: +Where `InterruptID` is obtained from the interrupt error: ```go interruptInfo, isInterrupt := ExtractInterruptInfo(err) if isInterrupt { - // possibly multiple interrupt points; take the first for illustration + // maybe multiple interrupt points exist here, + // we only take the first one for illustration purpose 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: +`resumeData` is the type defined by the interrupt point. For example, a Tool interrupted and requires user "approval" to execute this Tool, customizing an `ApprovalResult` as resumeData: ```go func (i InvokableApprovableTool) InvokableRun(ctx context.Context, argumentsInJSON string, @@ -388,7 +393,7 @@ func (i InvokableApprovableTool) InvokableRun(ctx context.Context, argumentsInJS } isResumeTarget, hasData, data := compose.GetResumeContext[*ApprovalResult](ctx) - if !isResumeTarget { // interrupted but not explicitly resumed; reinterrupt and wait for approval again + if !isResumeTarget { // was interrupted but not explicitly resumed, reinterrupt and wait for approval again return "", compose.StatefulInterrupt(ctx, &ApprovalInfo{ ToolName: toolInfo.Name, ArgumentsInJSON: storedArguments, @@ -413,24 +418,24 @@ func (i InvokableApprovableTool) InvokableRun(ctx context.Context, argumentsInJS # Examples -### Prior to Eino v0.7.0 +### Before 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) -### From Eino v0.7.0 onward +### After 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) Including: -`TestInterruptStateAndResumeForRootGraph`: simple dynamic interrupt +`TestInterruptStateAndResumeForRootGraph`: Simple dynamic interrupt -`TestInterruptStateAndResumeForSubGraph`: subgraph interrupt +`TestInterruptStateAndResumeForSubGraph`: Sub-graph interrupt -`TestInterruptStateAndResumeForToolInNestedSubGraph`: nested subgraph tool interrupt +`TestInterruptStateAndResumeForToolInNestedSubGraph`: Tool interrupt inside nested sub-graph -`TestMultipleInterruptsAndResumes`: parallel interrupts +`TestMultipleInterruptsAndResumes`: Parallel interrupts -`TestReentryForResumedTools`: tool interrupt in ReAct Agent, multiple re-entries after resume +`TestReentryForResumedTools`: Tool interrupt in ReAct Agent, multiple loop executions after resume -`TestGraphInterruptWithinLambda`: Lambda node contains a standalone Graph and interrupts internally +`TestGraphInterruptWithinLambda`: Lambda node contains independent Graph with internal 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 e7124274fd0..5975d0e8ce5 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,88 +1,88 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Orchestration Design Principles' weight: 2 --- -The mainstream language for large model application orchestration frameworks is Python, a language known for its flexibility. While flexibility facilitates SDK development, it also places a cognitive burden on SDK users. +The mainstream language for large model application orchestration frameworks is Python, known for its flexibility. While flexibility brings convenience to SDK development, it also adds cognitive burden for SDK users. -Eino, based on Golang, is `statically typed`, performing type checking at compile time, avoiding the runtime type issues of dynamic languages like Python. +Eino, based on Golang, is `statically typed`, performing type checking at Compile time, avoiding runtime type issues common in dynamic languages like Python. -## Upstream-Downstream `Type Alignment` as the Fundamental Principle +## `Type Alignment` of Upstream and Downstream as the Basic Principle -Eino's most basic orchestration method is graph, along with the simplified wrapper chain. Regardless of the orchestration method, the essence is `logic nodes` + `upstream/downstream relationships`. When the orchestration product runs, it executes from one logic node, then proceeds to run the next node connected to it. +Eino's most basic orchestration method is graph, as well as the simplified wrapper chain. Regardless of which orchestration method is used, the essence is `logic nodes` + `upstream-downstream relationships`. When the orchestration product runs, it runs from one logic node, then runs the next node connected to this node in the next step. -This implies a fundamental assumption: **The output value of the previous running node can be used as the input value of the next node.** +This implies a basic assumption: **The output value of the previous running node can serve as the input value for the next node.** In Golang, there are two basic approaches to implement this assumption: -1. Convert the inputs and outputs of different nodes to a more generalized type, such as `any`, `map[string]any`, etc. - 1. Adopting the approach of generalizing to any, but the corresponding cost is: developers need to explicitly convert to specific types when writing code. This greatly increases the cognitive burden on developers, so this approach was ultimately abandoned. - 2. LangChain's approach can be seen as passing `map[string]any` throughout, where each logic node uses the corresponding key to get the corresponding value according to its needs. In the langchaingo implementation, this is exactly how it's done, but similarly, any in Golang still requires `type assertion` to be used. This approach still has a significant cognitive burden for developers. +1. Convert the input and output of different nodes to a more generalized type, such as `any`, `map[string]any`, etc. + 1. Adopting the generalization to any approach, but the corresponding cost is: developers need to explicitly convert to specific types when writing code. This would greatly increase developers' cognitive burden, so this approach was ultimately abandoned. + 2. Langchain's approach can be seen as passing `map[string]any` throughout, with each logic node taking the corresponding value using the corresponding key according to its needs. In the langchaingo implementation, it follows this approach, but similarly, any in Golang still requires `type assertion` to be used. This approach still has significant cognitive burden for developers. 2. Keep the input and output types of each node as expected by developers, and ensure upstream and downstream types are consistent during the Compile phase. -Approach 2 is the final solution chosen by Eino. This approach is the easiest to understand when orchestrating - the whole process is like `building blocks`, where each block's protruding and recessed parts have their own specifications, and only matching specifications can form upstream/downstream relationships. +Approach 2 is the final approach chosen by Eino. This approach is the easiest to understand during orchestration. The entire process is like `building blocks`, where each block's protruding and recessed parts have their own specifications, and only when specifications match can they form upstream-downstream relationships. -As shown below: +As shown in the figure below: -For any orchestration, only when the downstream can recognize and process the upstream's output can the orchestration run normally. This fundamental assumption is clearly expressed in Eino, allowing developers to have full confidence in understanding how the orchestration logic runs and flows, rather than guessing whether the values passed from a series of any are correct. +For orchestration, only when downstream can recognize and process upstream output can the orchestration run normally. This basic assumption is clearly expressed in Eino, allowing developers to have full confidence in understanding how the orchestration logic runs and flows when using Eino for orchestration, rather than guessing whether the passed value is correct from a series of any. ### Type Alignment in Graph #### Edge -In a graph, a node's output flows to the next node along an `edge`, therefore, nodes connected by edges must be type-aligned. +In a graph, a node's output flows to the next node along `edges`, so nodes connected by edges must have type alignment. -As shown below: +As shown in the figure below: -> This simulates a scenario of ① direct conversation with a large model ② using RAG mode, with results that can be used to compare the effects of both modes +> This simulates scenarios of ① direct conversation with large model ② using RAG mode, with results available for comparing the effects of both modes -The green parts in the diagram are normal Edge connections, which require that the upstream output must be `assignable` to the downstream. The acceptable types are: +The green parts in the figure are normal Edge connections, requiring that upstream output can be `assigned` to downstream. Acceptable types include: -① Same upstream and downstream types: e.g., upstream outputs *schema.Message and downstream input is also *schema.Message +① Same upstream and downstream types: e.g., upstream outputs *schema.Message and downstream inputs also *schema.Message -② Downstream receives an interface, upstream implements that interface: e.g., upstream struct implements the Format() interface, downstream receives an interface{ Format() }. A special case is when downstream is any (empty interface), upstream always implements any, so it can always connect. +② Downstream accepts interface, upstream implements that interface: e.g., upstream struct implements Format() interface, downstream accepts interface{ Format() }. A special case is when downstream is any (empty interface), upstream always implements any, so connection is always possible. -③ Upstream is an interface, downstream is a concrete type: When the downstream concrete type implements the upstream interface type, it may or may not work - this cannot be determined at compile time, only at runtime when the upstream's concrete type is determined. For detailed description, see: [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) +③ Upstream is interface, downstream is concrete type: When the downstream concrete type implements the upstream interface type, it may or may not work, cannot be determined at compile time, only at runtime when the upstream's concrete type is determined. For detailed description, see: [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) -The yellow parts in the diagram show another type conversion mechanism provided by Eino: if the downstream receives 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 map's key is the OutputKey specified in the option. This mechanism is convenient when multiple edges converge to a single node. +The yellow parts in the figure are another type conversion mechanism provided by Eino: if downstream accepts type `map[string]any`, but upstream output type is not map[string]any, you can use `graph.AddXXXNode(node_key, xxx, compose.WithOutputKey("outkey")` to convert upstream output type to map[string]any, where the map key is the OutputKey specified in the option. This mechanism is convenient when multiple edges converge to a node. -Similarly, if the upstream is `map[string]any`, but the downstream input type is not map[string]any, you can use `graph.AddXXXNode(node_key, xxx, compose.WithInputKey("inkey")` to get one key's value from the upstream output as the downstream's input. +Similarly, if upstream is `map[string]any`, but downstream input type is not map[string]any, you can use `graph.AddXXXNode(node_key, xxx, compose.WithInputKey("inkey")` to get one key's value from upstream output as downstream input. #### Branch -If a node is followed by multiple edges, each edge's downstream node will run once. Branch is another mechanism: a branch is followed by n nodes, but only the node corresponding to the node key returned by the condition will run. Nodes after the same branch must be type-aligned. +If a node is followed by multiple edges, all downstream nodes of each edge will run once. Branch is another mechanism: a branch has n nodes following it, but only the node corresponding to the node key returned by condition will run. All nodes following the same branch must have type alignment. -As shown below: +As shown in the figure below: > This simulates the running logic of a react agent -As you can see, a branch itself has a `condition`, and this function's input must be type-aligned with the upstream. At the same time, each node connected after a branch must also, like the condition, be able to receive the upstream's output. +As you can see, a branch itself has a `condition`, whose input must align with upstream type. Also, each node following a branch must, like condition, be able to receive upstream output. ### Type Alignment in Chain #### Chain -From an abstract perspective, a chain is a `chain`, as shown below: +From an abstract perspective, chain is a `chain`, as shown below: Logic node types can be divided into 3 categories: -- Orchestrable components (e.g., chat model, chat template, retriever, lambda, graph, etc.) +- Orchestratable components (such as chat model, chat template, retriever, lambda, graph, etc.) - Branch nodes - Parallel nodes -As you can see, from the chain's perspective, whether it's a simple node (e.g., chat model) or a complex node (e.g., graph, branch, parallel), they are all the same - during execution, one step is one node's execution. +As you can see, from the chain's perspective, whether it's a simple node (e.g., chat model) or a complex node (e.g., graph, branch, parallel), they're all the same. During execution, one step is the running of one node. Therefore, between upstream and downstream nodes in a chain, types must be aligned, as follows: @@ -106,25 +106,25 @@ func TestChain() { } ``` -The above logic represented as a diagram: +The logic above represented in a figure is as follows: -If upstream and downstream types are not aligned, chain will return an error at chain.Compile(). Graph will report an error at graph.AddXXXNode(). +If upstream and downstream types don't align, chain will return an error at chain.Compile(). Graph will report an error at graph.AddXXXNode(). #### Parallel -Parallel is a special type of node in chain. From the chain's perspective, parallel is no different from other nodes. Inside parallel, its basic topology structure is as follows: +Parallel is a special type of node in chain. From the chain's perspective, parallel is no different from other nodes. Internally, parallel's basic topology is as follows: -One of the structures formed by multiple edges in a graph is this. The basic assumption here is: each edge in a parallel has exactly one node. Of course, this one node can also be a graph. Note that currently the framework does not directly provide the ability to nest branch or parallel within parallel. +One of the structures formed by multiple edges in graph is this. The basic assumption here is: each edge in a parallel has one and only one node. Of course, this one node can also be a graph. But note that currently the framework doesn't directly provide the ability to nest branch or parallel within parallel. -For each node in parallel, since their upstream node is the same, they all need to be type-aligned with the upstream node's output type. For example, in the diagram, the upstream node outputs `*schema.Message`, so each node must be able to receive this type. The receiving methods are the same as in graph, typically using `same type`, `interface definition`, `any`, or `input key option`. +In parallel, since each node has the same upstream node, they all need to align with the upstream node's output type. For example, if the upstream node in the figure outputs `*schema.Message`, each node must be able to receive this type. The receiving methods are the same as in graph, typically using `same type`, `interface definition`, `any`, or `input key option`. -The output of a parallel node is always a `map[string]any`, where the key is the output_key specified in `parallel.AddXXX(output_key, xxx, opts...)`, and the value is the actual output of the internal node. +The output of a parallel node is always a `map[string]any`, where the key is the output_key specified in `parallel.AddXXX(output_key, xxx, opts...)`, and value is the actual output of the internal node. -An example of building a parallel: +An example of building a parallel is as follows: ```go func TestParallel() { @@ -148,34 +148,34 @@ func TestParallel() { } ``` -A parallel's view in a chain is as follows: +A parallel's perspective within a chain is as follows: -> The diagram simulates the same question being answered by different large models, with results that can be used to compare effects +> The figure simulates the same question answered by different large models, with results available for comparing effects -> Note that this structure is only a logical view. Since chain itself is implemented using graph, parallel will be flattened into the underlying graph. +> Note that this structure is only a logical perspective. Since chain itself is implemented using graph, parallel will be flattened into the graph at the underlying level. #### Branch -Chain's branch is similar to graph's branch - all nodes in the branch must be type-aligned with the upstream node, so we won't elaborate here. The special thing about chain branch is that all possible branch nodes will connect to the same node in the chain, or all will connect to END. +Chain branch is similar to branch in graph. All nodes in the branch must align with the upstream node's type, so no further elaboration here. The special thing about chain branch is that all possible branch nodes will connect to the same node in the chain, or all will connect to END. ### Type Alignment in Workflow The dimension of type alignment in Workflow changes from overall Input & Output to field level. Specifically: -- The overall upstream output is type-aligned to a specific field of the downstream. -- A specific field of the upstream output is type-aligned to the overall downstream. -- A specific field of the upstream output is type-aligned to a specific field of the downstream input. +- The entire upstream output aligns to a specific field of downstream. +- A specific upstream output field aligns to the entire downstream. +- A specific upstream output field aligns to a specific downstream input field. The principles and rules are the same as overall type alignment. ### Type Alignment of StateHandler -StatePreHandler: The input type needs to align with the corresponding node's non-streaming input type. +StatePreHandler: Input type needs to align with the corresponding node's non-streaming input type. ```go -// input type is []*schema.Message, aligns with ChatModel's non-streaming input type +// input type is []*schema.Message, aligning with ChatModel's non-streaming input type preHandler := func(ctx context.Context, input []*schema.Message, state *state) ([]*schema.Message, error) { // your handler logic } @@ -183,10 +183,10 @@ preHandler := func(ctx context.Context, input []*schema.Message, state *state) ( AddChatModelNode("xxx", model, WithStatePreHandler(preHandler)) ``` -StatePostHandler: The input type needs to align with the corresponding node's non-streaming output type. +StatePostHandler: Input type needs to align with the corresponding node's non-streaming output type. ```go -// input type is *schema.Message, aligns with ChatModel's non-streaming output type +// input type is *schema.Message, aligning with ChatModel's non-streaming output type postHandler := func(ctx context.Context, input *schema.Message, state *state) (*schema.Message, error) { // your handler logic } @@ -194,10 +194,10 @@ postHandler := func(ctx context.Context, input *schema.Message, state *state) (* AddChatModelNode("xxx", model, WithStatePostHandler(postHandler)) ``` -StreamStatePreHandler: The input type needs to align with the corresponding node's streaming input type. +StreamStatePreHandler: Input type needs to align with the corresponding node's streaming input type. ```go -// input type is *schema.StreamReader[[]*schema.Message], aligns with ChatModel's streaming input type +// input type is *schema.StreamReader[[]*schema.Message], aligning with ChatModel's streaming input type preHandler := func(ctx context.Context, input *schema.StreamReader[[]*schema.Message], state *state) (*schema.StreamReader[[]*schema.Message], error) { // your handler logic } @@ -205,10 +205,10 @@ preHandler := func(ctx context.Context, input *schema.StreamReader[[]*schema.Mes AddChatModelNode("xxx", model, WithStreamStatePreHandler(preHandler)) ``` -StreamStatePostHandler: The input type needs to align with the corresponding node's streaming output type. +StreamStatePostHandler: Input type needs to align with the corresponding node's streaming output type. ```go -// input type is *schema.StreamReader[*schema.Message], aligns with ChatModel's streaming output type +// input type is *schema.StreamReader[*schema.Message], aligning with ChatModel's streaming output type postHandler := func(ctx context.Context, input *schema.StreamReader[*schema.Message], state *state) (*schema.StreamReader[*schema.Message], error) { // your handler logic } @@ -216,15 +216,15 @@ postHandler := func(ctx context.Context, input *schema.StreamReader[*schema.Mess AddChatModelNode("xxx", model, WithStreamStatePostHandler(postHandler)) ``` -### Type Alignment in Invoke and Stream Modes +### Type Alignment Methods Under Invoke and Stream -In Eino, the result of orchestration is a graph or chain. To run it, you need to use `Compile()` to generate a `Runnable` interface. +In Eino, orchestration results are graph or chain. To run, you need to use `Compile()` to generate a `Runnable` interface. -An important function of Runnable is to provide four calling methods: "Invoke", "Stream", "Collect", and "Transform". +An important function of Runnable is providing four calling methods: "Invoke", "Stream", "Collect", and "Transform". -> For an introduction to the above calling methods and detailed Runnable introduction, see: [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) +> For introduction of the above calling methods and detailed Runnable introduction, see: [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials) -Suppose we have a `Graph[[]*schema.Message, []*schema.Message]` with a ChatModel node and a Lambda node. After Compile, it becomes a `Runnable[[]*schema.Message, []*schema.Message]`. +Suppose we have a `Graph[[]*schema.Message, []*schema.Message]` with a ChatModel node and a Lambda node. After Compile, it's a `Runnable[[]*schema.Message, []*schema.Message]`. ```go package main @@ -281,22 +281,22 @@ func TestTypeMatch(t *testing.T) { } ``` -When we call the compiled Runnable above in Stream mode, the model node will output `*schema.StreamReader[*Message]`, but the lambda node is an InvokableLambda that only accepts non-streaming `*schema.Message` as input. This also conforms to the type alignment rules because the Eino framework will automatically concatenate the streamed Message into a complete Message. +When we call the compiled Runnable with Stream, the model node outputs `*schema.StreamReader[*Message]`, but the lambda node is an InvokableLambda that only accepts non-streaming `*schema.Message` as input. This also conforms to type alignment rules because the Eino framework automatically concatenates streaming Messages into complete Messages. -In stream mode, concatenating frames is a very common operation. During concatenation, all elements from `*StreamReader[T]` are first extracted and converted to `[]T`, then an attempt is made to concatenate `[]T` into a complete `T`. The framework has built-in support for concatenating the following types: +In stream mode, concatenating frames is a very common operation. When concatenating, all elements in `*StreamReader[T]` are first extracted and converted to `[]T`, then attempt to concatenate `[]T` into a complete `T`. The framework has built-in support for concatenating the following types: -- `*schema.Message`: See `schema.ConcatMessages()` -- `string`: Implementation logic is equivalent to `+=` -- `[]*schema.Message`: See `compose.concatMessageArray()` -- `Map`: Merge values with the same key, with the same merge logic as above. If there are types that cannot be merged, it fails (note: not overwrite) -- Other slices: Can only be merged when the slice has exactly one non-zero element. +- `*schema.Message`: Details in `schema.ConcatMessages()` +- `string`: Implementation logic equivalent to `+=` +- `[]*schema.Message`: Details in `compose.concatMessageArray()` +- `Map`: Merge values of the same key, merge logic same as above. If there are types that cannot be merged, it fails (ps: not overwrite) +- Other slices: Can only be merged when only one element in the slice is non-zero value. For other scenarios, or when users want to override the default behavior above with custom logic, developers can implement their own concat method and register it to the global concatenation function using `compose.RegisterStreamChunkConcatFunc()`. -Example: +Example as follows: ```go -// Assume our own struct is as follows +// Suppose our own struct is as follows type tStreamConcatItemForTest struct { s string } @@ -312,41 +312,41 @@ func concatTStreamForTest(items []*tStreamConcatItemForTest) (*tStreamConcatItem } func Init() { - // Register to the global concatenation method + // Register to global concatenation method compose.RegisterStreamChunkConcatFunc(concatTStreamForTest) } ``` ### Scenarios Where Type Alignment is Checked at Runtime -Eino's Graph type alignment check is performed at `err = graph.AddEdge("node1", "node2")` to check whether the two node types match. This allows type mismatch errors to be discovered during `the graph building process` or `the Compile process`. This applies to rules ① ② ③ listed in [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles). +Eino's Graph type alignment check occurs at `err = graph.AddEdge("node1", "node2")` when checking whether the two nodes' types match. This allows discovering type mismatch errors during `graph building` or `Compile process`, applicable to rules ① ② ③ listed in [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles). -When the upstream node's output is an `interface`, if the downstream node type implements that `interface`, the upstream may be able to convert to the downstream type (type assertion), but this can only be known during `runtime`. The type check for this scenario is moved to runtime. +When the upstream node's output is `interface`, if the downstream node type implements that `interface`, upstream may be convertible to downstream type (type assertion), but can only be determined during `runtime`. At this point, if it's determined that upstream cannot be assigned to downstream, an error will be thrown. -The structure is shown below: +The structure is shown in the figure below: -This scenario is suitable for cases where developers can handle upstream and downstream type alignment themselves, and can choose downstream execution nodes based on different types. +This scenario is suitable for cases where developers can handle upstream-downstream type alignment themselves, selecting downstream execution nodes based on different types. ## Design Choices with Clear Preferences -### External Variables Read-Only Principle +### Read-Only Principle for External Variables -When data flows between Nodes, Branches, and Handlers in Eino's Graph, it is always variable assignment, not Copy. When Input is a reference type, such as Struct pointer, map, or slice, modifications to Input inside Node, Branch, or Handler will have side effects externally and may cause concurrency issues. Therefore, Eino follows the external variables read-only principle: Node, Branch, Handler should not modify Input internally. If modification is needed, copy it first. +When data flows between Nodes, Branches, and Handlers in Eino's Graph, it's all variable assignment, not Copy. When Input is a reference type, such as Struct pointer, map, slice, modifications to Input inside Node, Branch, or Handler will have side effects externally, potentially causing concurrency issues. Therefore, Eino follows the read-only principle for external variables: Node, Branch, and Handler should not modify Input internally. If modification is needed, first Copy it yourself. This principle also applies to Chunks in StreamReader. -### Fan-in and Merge +### Fan-In and Merge -**Fan-in**: Data from multiple upstreams flows into the downstream, together serving as the downstream's input. It is necessary to clearly define how multiple upstream outputs are **merged**. +**Fan-In**: Data from multiple upstreams flows into downstream, together serving as downstream input. Need to clearly define how outputs from multiple upstreams are **merged**. -By default, first, the **actual types** of multiple upstream outputs must be the same and be a Map, and keys must not overlap. Second: +By default, first, the **actual types** of multiple upstream outputs must be the same and be Map, and keys must not overlap between them. Then: -- In non-streaming scenarios, after merging, it becomes one Map containing all key-value pairs from all upstreams. +- In non-streaming scenarios, after merging becomes one Map, containing all key-value pairs from all upstreams. - In streaming scenarios, multiple upstream StreamReaders of the same type are merged into one StreamReader. The actual Recv effect is fair reading from multiple upstream StreamReaders. -When AddNode, you can add the WithOutputKey Option to convert the node's output to a Map: +When AddNode, you can convert node output to Map by adding the WithOutputKey Option: ```go // This node's output will change from string to map[string]any, @@ -357,121 +357,121 @@ graph.AddLambdaNode("your_node_key", compose.InvokableLambda(func(ctx context.Co }), compose.WithOutputKey("your_output_key")) ``` -You can also register a Merge method to implement merge of any type: +You can also implement merge of any type by registering a Merge method: ```go // eino/compose/values_merge.go func RegisterValuesMergeFunc[T any](fn func([]T) (T, error)) ``` -Workflow can map multiple output fields from multiple upstreams to different fields of the downstream node. This is not a merge scenario, but point-to-point field mapping. In fact, eino workflow currently does not support "multiple upstream fields mapping to the same downstream field simultaneously". +Workflow can map multiple output fields from multiple upstreams to different fields of the downstream node. This doesn't belong to merge scenarios, but point-to-point field mapping. In fact, eino workflow currently doesn't support "multiple upstream fields mapping to the same downstream field at the same time". -### Streaming Handling +### Stream Processing -Eino believes that components should only need to implement the streaming paradigms that are real in business scenarios. For example, ChatModel doesn't need to implement Collect. Therefore, in orchestration scenarios, Eino automatically helps all nodes **complete missing streaming paradigms**. +Eino believes that components should only need to implement streaming paradigms that are real in business scenarios, for example, ChatModel doesn't need to implement Collect. Therefore, in orchestration scenarios, Eino automatically helps all nodes **complete missing streaming paradigms**. -Running Graph in Invoke mode, all internal nodes run in Invoke paradigm. Running Graph in Stream, Collect, or Transform mode, all internal nodes run in Transform paradigm. +Running Graph with Invoke, internal nodes all run with Invoke paradigm. Running Graph with Stream, Collect or Transform, internal nodes all run with Transform paradigm. -**Auto Concatenate**: For scenarios where Stream chunks are concatenated into complete content, user-registered custom concatenation functions are used first, followed by framework-provided default behaviors, including Message, Message array, String, Map, and Struct and Struct pointers. +**Auto Concatenate**: For scenarios where Stream chunks are concatenated into complete content, first use user-registered custom concatenation functions, then execute framework-provided default behavior, including Message, Message array, String, Map, and Struct and Struct pointers. -**Auto Box**: For scenarios where non-streaming T needs to become StreamReader[T], the framework executes automatically. +**Auto Box**: For scenarios where non-streaming T needs to become StreamReader[T], the framework automatically executes. -**Auto Merge**: See the "Fan-in and Merge" section above. +**Auto Merge**: See "Fan-In and Merge" section above. -**Auto Copy**: Automatic stream copying in scenarios that require it, including a stream fanning out to multiple downstream nodes, and a stream entering one or more callback handlers. +**Auto Copy**: Automatic stream copying in scenarios requiring stream copying, including one stream fanning out to multiple downstream nodes, one stream entering one or more callback handlers. -Finally, Eino requires all orchestration elements to be able to sense and handle streams. This includes branch, state handler, callback handler, passthrough, lambda, etc. +Finally, Eino requires all orchestration elements to be able to sense and process streams. Including branch, state handler, callback handler, passthrough, lambda, etc. -For Eino's stream handling capabilities, see [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials). +For detailed Eino stream handling capabilities, see [Eino Stream Programming Essentials](/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials). ### Global State -**State**: Pass the State creation method through `compose.WithGenLocalState` when NewGraph. This request-scoped global state can be read and written in various stages of a request. +**State**: Pass the State creation method through `compose.WithGenLocalState` when NewGraph. This request-level global state can be read and written throughout various stages of a request. -Eino recommends using `StatePreHandler` and `StatePostHandler`, with the functional positioning of: +Eino recommends using `StatePreHandler` and `StatePostHandler`, with functionality positioned as: -- StatePreHandler: Read and write State before each node execution, and replace the node's Input as needed. Input needs to align with the node's non-streaming input type. -- StatePostHandler: Read and write State after each node execution, and replace the node's Output as needed. Input needs to align with the node's non-streaming output type. +- StatePreHandler: Read and write State before each node executes, and replace node Input as needed. Input needs to align with node's non-streaming input type. +- StatePostHandler: Read and write State after each node executes, and replace node Output as needed. Input needs to align with node's non-streaming output type. -For streaming scenarios, use the corresponding `StreamStatePreHandler` and `StreamStatePostHandler`, with input needing to align with the node's streaming input and streaming output types respectively. +For streaming scenarios, use the corresponding `StreamStatePreHandler` and `StreamStatePostHandler`, with input needing to align with node's streaming input and streaming output types respectively. -These state handlers are located outside the node, affecting the node through modifications to Input or Output, thus ensuring the node's "state-independent" characteristic. +These state handlers are located outside the node, affecting the node through modifications to Input or Output, thus ensuring the node's "state-agnostic" characteristic. -If you need to read and write State inside a node, Eino provides the `ProcessState[S any](ctx context.Context, handler func(context.Context, S) error) error` function. +If State needs to be read and written inside the node, Eino provides the `ProcessState[S any](ctx context.Context, handler func(context.Context, S) error) error` function. -The Eino framework will lock at all positions where State is read or written. +Eino framework will lock at all positions where State is read or written. ### Callback Injection The Eino orchestration framework believes that components entering orchestration may or may not have Callback aspects embedded internally. This information is determined by whether the component implements the `Checker` interface and the return value of the `IsCallbacksEnabled` method in the interface. - When `IsCallbacksEnabled` returns true, the Eino orchestration framework uses the Callback aspects inside the component implementation. -- Otherwise, it automatically wraps Callback aspects outside the component implementation, (only) reporting input and output. +- Otherwise, automatically wrap Callback aspects outside the component implementation, (can only) report input and output. -In either case, RunInfo will be automatically inferred. +Either way, RunInfo will be automatically inferred. -At the same time, for the Graph as a whole, Callback aspects will always be injected, with RunInfo being the Graph itself. +Also, for the Graph as a whole, Callback aspects will definitely be injected, with RunInfo being the Graph itself. -For the complete description of Eino's Callback capabilities, see [Eino: Callback User Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual). +For complete description of Eino's Callback capability, see [Eino: Callback User Manual](/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual). ### Option Distribution Eino supports various dimensions of Call Option distribution: -- Global by default, i.e., distributed to all nodes, including nested internal graphs. -- Can add Options for a specific component type, which are then distributed to all nodes of that type by default, such as AddChatModelOption. Lambda that defines its own Option type can also specify Options to itself this way. +- Default global, i.e., distributed to all nodes, including nested internal graphs. +- Can add Options for a specific component type, which by default distributes to all nodes of that type, such as AddChatModelOption. Lambda with defined unique Option types can also specify Options to itself this way. - Can specify any specific nodes using `DesignateNode(key ...string)`. -- Can specify nested graphs at any depth, or any specific nodes within them, using `DesignateNodeWithPath(path ...*NodePath)`. +- Can specify nested graphs of any depth, or any specific nodes within them, using `DesignateNodeWithPath(path ...*NodePath)`. -For the complete description of Eino's Call Option capabilities, see [Eino: CallOption Capabilities and Specifications](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities). +For complete description of Eino's Call Option capability, see [Eino: CallOption Capabilities and Specifications](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities). ### Graph Nesting -The graph orchestration product `Runnable` has a very similar interface form to Lambda. Therefore, a compiled graph can be simply wrapped as a Lambda and nested into other graphs as a Lambda node. +Graph orchestration product `Runnable` has a very similar interface form to Lambda. Therefore, a compiled graph can be simply wrapped as Lambda and nested into other graphs as a Lambda node. -Another way is to directly nest Graph, Chain, Workflow, etc. into other graphs through AddGraph before compilation. The differences between the two approaches are: +Another way, before compilation, Graph, Chain, Workflow, etc. can all be directly nested into other graphs through AddGraph. The differences between the two methods are: -- With the Lambda approach, there will be an extra Lambda node level in the trace. Other Callback handler perspectives will also see an extra layer. -- With the Lambda approach, CallOption needs to be received through Lambda's Option, and cannot use DesignateNodeWithPath. -- With the Lambda approach, the internal graph needs to be compiled beforehand. With direct AddGraph, the internal graph is compiled together with the upper-level graph. +- Lambda method, there will be an extra Lambda node level in trace. Other Callback handler perspectives will also see an extra layer. +- Lambda method, needs to use Lambda's Option to accept CallOption, cannot use DesignateNodeWithPath. +- Lambda method, internal graph needs to be compiled first. AddGraph directly, internal graph compiles together with parent graph. ## Internal Mechanisms ### Execution Sequence -Taking an InvokableLambda (input is string, output is int) with StatePreHandler, StatePostHandler, InputKey, OutputKey, and no internal Callback aspects as an example, the complete streaming execution sequence in the graph is as follows: +Taking an InvokableLambda (input is string, output is int) with StatePreHandler, StatePostHandler, InputKey, OutputKey added, and no Callback aspects implemented internally as an example, the complete streaming execution sequence in the graph is as follows: In workflow scenarios, field mapping occurs at two positions: -- After the node execution's StatePostHandler and "stream copy" steps, the fields needed by each downstream are extracted separately. -- After the "merge" step before node execution and before StatePreHandler, the extracted upstream field values are converted to the current node's input. +- After StatePostHandler and "stream copy" step after node execution, fields needed by each downstream are extracted separately. +- After "merge" step before node execution, before StatePreHandler, extracted upstream field values are converted to current node's input. ### Execution Engine -When `NodeTriggerMode == AnyPredecessor`, the graph executes with the pregel engine, corresponding to a directed cyclic graph topology. The characteristics are: +When `NodeTriggerMode == AnyPredecessor`, graph executes with pregel engine, corresponding topological structure is directed cyclic graph. Characteristics are: -- All successor nodes of the currently executing one or more nodes form a SuperStep and execute together. At this point, these new nodes become the "current" nodes. -- Supports Branch, supports cycles in the graph, but may require manually adding passthrough nodes to ensure the nodes in the SuperStep meet expectations, as shown below: +- All successor nodes of currently executing one or more nodes, as a SuperStep, execute together. At this point, these new nodes become "current" nodes. +- Supports Branch, supports cycles in graph, but may need to manually add passthrough nodes to ensure nodes in SuperStep meet expectations, as shown: -In the above diagram, Node 4 and Node 5 are placed together for execution according to the rules, which is probably not as expected. It needs to be changed to: +In the figure above, Node 4 and Node 5 are placed together for execution by rule, which probably doesn't meet expectations. Need to change to: -When `NodeTriggerMode == AllPredecessor`, the graph executes with the dag engine, corresponding to a directed acyclic graph topology. The characteristics are: +When `NodeTriggerMode == AllPredecessor`, graph executes with dag engine, corresponding topological structure is directed acyclic graph. Characteristics are: -- Each node has definite predecessor nodes, and this node only has the condition to run after all predecessor nodes are complete. -- Does not support cycles in the graph, because it would break the assumption that "each node has definite predecessor nodes". -- Supports Branch. At runtime, nodes not selected by the Branch are marked as skipped, not affecting the AllPredecessor semantics. +- Each node has definite predecessor nodes. Only when all predecessor nodes complete does this node have running conditions. +- Doesn't support cycles in graph, because it would break the assumption that "each node has definite predecessor nodes". +- Supports Branch. At runtime, nodes not selected by Branch are marked as skipped, not affecting AllPredecessor semantics. > 💡 -> After setting NodeTriggerMode = AllPredecessor, nodes will execute after all predecessors are ready, but not immediately - they still follow SuperStep semantics, running new runnable nodes after a batch of nodes has completed execution. +> After setting NodeTriggerMode = AllPredecessor, nodes execute after all predecessors are ready, but not immediately. They still follow SuperStep—running new runnable nodes after a batch of nodes all complete execution. > > If you pass compose.WithEagerExecution() during Compile, ready nodes will run immediately. > > In Eino v0.4.0 and later versions, setting NodeTriggerMode = AllPredecessor will enable EagerExecution by default. -In summary, pregel mode is flexible and powerful but has additional cognitive burden, while dag mode is clear and simple but has limited scenarios. In the Eino framework, Chain uses pregel mode, Workflow uses dag mode, and Graph supports both, allowing users to choose between pregel and dag. +In summary, pregel mode is flexible and powerful but has additional cognitive burden, dag mode is clear and simple but limited in scenarios. In the Eino framework, Chain is pregel mode, Workflow is dag mode, Graph supports both, allowing users to choose from pregel and dag. 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 7dcbe2395c1..c90c4e2fcea 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 @@ -3,49 +3,49 @@ Description: "" date: "2026-01-30" lastmod: "" tags: [] -title: Eino Streaming Essentials +title: Eino Stream Programming Essentials weight: 4 --- > 💡 -> Recommended reading first: [Eino: Overview](/docs/eino/overview) and [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) +> Recommended reading first: [Eino: Basic Concepts Introduction](/docs/eino/overview) [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) -## Streaming in Orchestration: Overview +## Overview of Orchestration Streaming -Key factors when orchestrating streaming graphs: +When orchestrating streaming Graphs, there are several key elements to consider: -- 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`. +- What Lambda operators are included in components/Lambda: Choose from Invoke, Stream, Collect, Transform +- In the orchestration topology, whether upstream and downstream nodes' inputs and outputs are both streams or both non-streams. +- If upstream and downstream nodes' stream types cannot match, two operations are needed: + - Streaming: Convert T into a single-Chunk Stream[T] + - Concat: Merge Stream[T] into a complete T. Each "frame" in Stream[T] is part of this complete T. -## Semantics of Streaming in Eino +## The Meaning of Eino Stream Programming -- 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: +- Some components naturally support outputting in "frames", outputting part of a complete output each time, i.e., "streaming" output. After streaming output is complete, downstream needs to concatenate these "frames" into complete output. A typical example is LLM. +- Some components naturally support inputting in "frames", starting meaningful business processing or even completing business processing when receiving incomplete input. For example, in a react agent, the branch used to determine whether to call a tool or end the run can make a decision from the first frame of the LLM's streaming output by checking whether the message contains a tool call. +- Therefore, a component, from the input perspective, has "non-streaming" input and "streaming" input two types. From the output perspective, has "non-streaming" output and "streaming" output two types. +- Combined, there are four possible streaming programming paradigms - - - - - + + + + +
FunctionPatternInteractionLambda ConstructorNotes
InvokeNon-streaming in, non-streaming outPing-Pongcompose.InvokableLambda()
StreamNon-streaming in, streaming outServer-Streamingcompose.StreamableLambda()
CollectStreaming in, non-streaming outClient-Streamingcompose.CollectableLambda()
TransformStreaming in, streaming outBidirectional-Streamingcompose.TransformableLambda()
Function NameMode DescriptionInteraction Mode NameLambda ConstructorNotes
InvokeNon-streaming input, non-streaming outputPing-Pong Modecompose.InvokableLambda()
StreamNon-streaming input, streaming outputServer-Streaming Modecompose.StreamableLambda()
CollectStreaming input, non-streaming outputClient-Streamingcompose.CollectableLambda()
TransformStreaming input, streaming outputBidirectional-Streamingcompose.TransformableLambda()
-## Streaming at the Single-Component Level +## Streaming from Single Component Perspective -Eino is a "component-first" framework; components can be used independently. Do you need to consider streaming when defining component interfaces? The simple answer is no. The complex answer is "follow real business scenarios". +Eino is a "component first" framework where components can be used independently. When defining component interfaces, do you need to consider streaming programming issues? The simple answer is no. The complex answer is "based on real business scenarios". -### Business Semantics of Components +### Component's Own Business Paradigm -A typical component, such as ChatModel or Retriever, should define its interface according to actual business semantics. If it actually supports a certain streaming paradigm, implement that paradigm. If a certain streaming paradigm has no real business scenario, there's no need to implement it. For example: +A typical component, like Chat Model or Retriever, just needs to define interfaces based on actual business semantics. If it actually supports a certain streaming paradigm, implement that streaming paradigm. If a certain streaming paradigm has no real business scenario, then no need to implement it. For example: -- ChatModel: besides `Invoke` (non-streaming), it naturally supports `Stream` (streaming). It therefore implements `Generate` and `Stream`, but `Collect` and `Transform` have no corresponding real business scenarios, so they are not implemented: +- Chat Model, besides the non-streaming Invoke paradigm, also naturally supports the Stream streaming paradigm, so Chat Model's interface implements both Generate and Stream interfaces. But Collect and Transform have no corresponding real business scenarios, so corresponding interfaces are not implemented: ```go type ChatModel interface { @@ -56,7 +56,7 @@ type ChatModel interface { } ``` -- Retriever: besides `Invoke` (non-streaming), the other three streaming paradigms have no real business scenarios, so it only implements `Retrieve`: +- Retriever, besides the non-streaming Invoke paradigm, none of the other three streaming paradigms have real business scenarios, so only the Retrieve interface is implemented: ```go type Retriever interface { @@ -64,12 +64,12 @@ type Retriever interface { } ``` -### Paradigms Supported by Components +### Specific Paradigms Supported by Components - - - + + + @@ -78,36 +78,36 @@ type Retriever interface {
ComponentInvokeStreamCollectTransform
ChatModelyesyesnono
ChatTemplateyesnonono
Component NameImplements InvokeImplements StreamImplements CollectImplements Transform
Chat modelyesyesnono
Chat templateyesnonono
Retrieveryesnonono
Indexeryesnonono
Embedderyesnonono
Toolyesyesnono
-Among official Eino components, only ChatModel and Tool additionally support `Stream`; all other components only support `Invoke`. For component details, see: [[Updating] Eino: Components Abstraction & Implementation](/docs/eino/core_modules/components) +Among Eino official components, besides Chat Model and Tool which additionally support stream, all other components only support invoke. For specific component introduction, see: [[Updating] Eino: Components Abstraction & Implementation](/docs/eino/core_modules/components) -`Collect` and `Transform` paradigms are currently only used in orchestration scenarios. +Collect and Transform two streaming paradigms are currently only used in orchestration scenarios. -## Streaming Across Multiple Components (Orchestration) +## Streaming from Multiple Component Orchestration Perspective -### Component Streaming Paradigms in Orchestration +### Component Streaming Paradigm in Orchestration -When a component is used standalone, its input and output streaming paradigms are fixed and cannot exceed the scope defined by the component interface. +A component, when used standalone, has fixed input and output streaming paradigms that cannot exceed the range defined by the component's interface. -- For example, ChatModel's input can only be non-streaming `[]Message`, and its output can be either non-streaming `Message` or streaming `StreamReader[Message]`, because ChatModel only implements the `Invoke` and `Stream` paradigms. +- For example, Chat Model's input can only be non-streaming []Message, output can be either non-streaming Message or streaming StreamReader[Message], because Chat Model only implements Invoke and Stream paradigms. -However, once a component is in an "orchestration" scenario where multiple components are combined, its input and output are no longer fixed, but depend on the "upstream output" and "downstream input" in the orchestration context. Consider a typical ReAct Agent orchestration diagram: +However, once a component is in an "orchestration" scenario where multiple components are used together, its input and output are no longer so fixed, but depend on the "upstream output" and "downstream input" of this component in the orchestration scenario. For example, the typical orchestration diagram of React Agent: -In the diagram above, if Tool is a StreamableTool (i.e., output is `StreamReader[Message]`), then Tool → ChatModel could have streaming output. However, ChatModel has no business scenario for accepting streaming input, nor does it have a corresponding interface. At this point, Eino framework automatically provides ChatModel with the ability to accept streaming input: +In the figure above, if Tool is a StreamableTool, i.e., output is StreamReader[Message], then Tool -> ChatModel could be streaming output. But Chat Model doesn't have a business scenario for receiving streaming input, nor a corresponding interface. At this point, the Eino framework automatically helps ChatModel complete the ability to receive streaming input: -The "Concat message stream" above is a capability automatically provided by the Eino framework. Even if it's not a message but any arbitrary `T`, as long as certain conditions are met, Eino framework will automatically perform this `StreamReader[T]` to `T` conversion. The condition is: **In orchestration, when a component's upstream output is `StreamReader[T]`, but the component only provides a business interface with `T` as input, the framework will automatically concat `StreamReader[T]` into `T` before inputting it to the component.** +The Concat message stream above is automatically provided by the Eino framework. Even if it's not message, but any T, as long as specific conditions are met, the Eino framework will automatically do this StreamReader[T] to T conversion. The condition is: **In orchestration, when a component's upstream output is StreamReader[T], but the component only provides a business interface with T as input, the framework will automatically concat StreamReader[T] into T, then input to this component.** > 💡 -> The process of the framework automatically concatenating `StreamReader[T]` into `T` may require the user to provide a concat function. See [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) for the section on "merge frames". +> The process of framework automatically concatenating StreamReader[T] into T may require users to provide a Concat function. See the chapter on "merging frames" in [Eino: Orchestration Design Principles](/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles#share-FaVnd9E2foy4fAxtbTqcsgq3n5f). -On the other hand, consider an opposite example. Still the ReAct Agent, this time with a more complete orchestration diagram: +On the other hand, consider an opposite example. Still React Agent, this time a more complete orchestration diagram: -In the diagram above, the branch receives the message output from the ChatModel and decides whether to end the agent's current run and output the message directly, or call the Tool and pass the result back to the ChatModel for cyclic processing, based on whether the message contains a tool call. Since this Branch can complete its logic judgment from the first frame of the message stream, we define this Branch with the `Collect` interface, i.e., streaming input, non-streaming output: +In the figure above, branch receives message output by chat model and, based on whether the message contains tool calls, chooses whether to directly end the agent's current run and output the message, or call Tool and give the call result to Chat Model for loop processing. Since this Branch can complete logic judgment from the first frame of the message stream, we define this Branch with a Collect interface, i.e., streaming input, non-streaming output: ```go compose.NewStreamGraphBranch(func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (endNode string, err error) { @@ -122,40 +122,40 @@ 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` streaming programming paradigms respectively. When a ReactAgent is called via `Stream`, the ChatModel's output is `StreamReader[Message]`, so the Branch's input is `StreamReader[Message]`, which matches the function signature definition of this Branch condition, and can run without any conversion. +ReactAgent has two interfaces, Generate and Stream, implementing Invoke and Stream streaming programming paradigms respectively. When a ReactAgent is called with Stream, Chat Model's output is StreamReader[Message], so Branch's input is StreamReader[Message], matching this Branch condition's function signature definition, can run without any conversion. -However, when this ReactAgent is called via `Generate`, the ChatModel's output is `Message`, so the Branch's input would also be `Message`, which does not match the `StreamReader[Message]` function signature definition of the Branch Condition. At this point, the Eino framework will automatically box `Message` into `StreamReader[Message]` and pass it to the Branch, and this StreamReader will only have one frame. +However, when this ReactAgent is called with Generate, Chat Model's output is Message, so Branch's input will also be Message, not matching Branch Condition's StreamReader[Message] function signature definition. At this point, the Eino framework will automatically box Message into StreamReader[Message], then pass to Branch, and this StreamReader will only have one frame. > 💡 -> This kind of stream with only one frame is colloquially called a "pseudo-stream", because it doesn't bring the actual benefit of streaming, which is "low first-packet latency", but is simply boxed to meet the streaming input/output interface signature requirements. +> This kind of stream with only one frame is commonly called "fake stream", because it doesn't bring the actual benefit of streaming, i.e., "low first packet latency", but is just simple boxing to meet the streaming input/output interface signature requirements. -In summary: **In orchestration, when a component's upstream output is `T`, but the component only provides a business interface with `StreamReader[T]` as input, the framework will automatically box `T` into a single-frame `StreamReader[T]` before inputting it to the component.** +In summary: **In orchestration, when a component's upstream output is T, but the component only provides a business interface with StreamReader[T] as input, the framework will automatically box T into a single-frame StreamReader[T], then input to this component.** -### Streaming Paradigms of Orchestration Aids +### Streaming Paradigms of Orchestration Helper Elements -The Branch mentioned above is not a component that can be used standalone, but an "orchestration aid" that only makes sense in orchestration scenarios. Similar "components" that are only meaningful in orchestration scenarios include: +The Branch mentioned above is not a standalone component, but an "orchestration helper element" that only makes sense in orchestration scenarios. There are some similar "components" that only make sense in orchestration scenarios, see the figure below: - - - - - - + + + + + +
ElementUse CaseInvokeStreamCollectTransform
BranchDynamically select one from a set of downstream Nodes based on upstream output
  • If decision can only be made after receiving complete input → implement Invoke
  • If decision can be made after receiving partial frames → implement Collect
  • Only one can be implemented
  • yesnoyesno
    StatePreHandlerModify State and/or Input before entering a Node in Graph. Supports streaming.yesnonoyes
    StatePostHandlerModify State and/or Output after a Node completes in Graph. Supports streaming.yesnonoyes
    PassthroughIn parallel scenarios, to balance the number of Nodes in each parallel branch, Passthrough nodes can be added to branches with fewer Nodes. Passthrough node's input and output are the same, following the upstream node's output or downstream node's input (expected to be the same).yesnonoyes
    LambdaEncapsulate business logic not defined by official components. Choose the corresponding streaming paradigm based on which paradigm the business logic is.yesyesyesyes
    Component NameUsage ScenarioImplements InvokeImplements StreamImplements CollectImplements Transform
    BranchDynamically select one from a group of downstream Nodes based on upstream output
  • For cases that can only judge after receiving complete input, implement Invoke
  • For cases that can judge after receiving partial frames, implement Collect
  • Only one can be implemented
  • yesnoyesno
    StatePreHandlerIn Graph, modify State or/and Input before entering Node. Can support streaming.yesnonoyes
    StatePostHandlerIn Graph, modify State or/and Output after Node completes. Can support streamingyesnonoyes
    PassthroughIn parallel situations, to flatten the number of Nodes in each parallel branch, can add Passthrough nodes to branches with fewer Nodes. Passthrough node's input and output are the same, following upstream node's output or following downstream node's input (expected to be the same).yesnonoyes
    LambdaEncapsulate business logic not defined by official components. Whatever paradigm the business logic is, choose the corresponding streaming paradigm to implement.yesyesyesyes
    -Additionally, there's another type of "component" that only makes sense in orchestration scenarios: treating orchestration artifacts as a whole, such as compiled Chain, Graph. These overall orchestration artifacts can be called as "components" standalone, or added as nodes to higher-level orchestration artifacts. +There's another type of "component" that only makes sense in orchestration scenarios, which is viewing orchestration products as a whole, like orchestrated Chain, Graph. These overall orchestration products can be called as "components" standalone, or can be added as nodes to higher-level orchestration products. -## Streaming at Orchestration Level (Whole Graph) +## Streaming from Overall Orchestration Perspective -### "Business" Paradigms of Orchestration Artifacts +### Orchestration Product's "Business" Paradigm -Since overall orchestration artifacts can be viewed as "components", from a component perspective we can ask: do orchestration artifact "components" have interface paradigms that match "business scenarios" like ChatModel and other components? The answer is both "yes" and "no". +Since overall orchestration products can be viewed as a "component", from the component perspective we can ask: Does the orchestration product as a "component" have interface paradigms that fit "business scenarios" like Chat Model and other components? The answer is both "yes" and "no". -- "No": Overall, Graph, Chain and other orchestration artifacts have no business attributes themselves, they only serve abstract orchestration, so there are no interface paradigms that match business scenarios. At the same time, orchestration needs to support various paradigm business scenarios. Therefore, the `Runnable[I, O]` interface representing orchestration artifacts in Eino makes no choice and cannot choose, providing methods for all streaming paradigms: +- "No": Overall, orchestration products like Graph, Chain have no business attributes themselves, only serving abstract orchestration, so there's no interface paradigm matching business scenarios. Also, orchestration needs to support various business scenario paradigms. So, Eino's Runnable[I, O] interface representing orchestration products, without making choices and unable to choose, provides all streaming paradigm methods: ```go type Runnable[I, O any] interface { @@ -166,57 +166,57 @@ type Runnable[I, O any] interface { } ``` -- "Yes": Specifically, a particular Graph or Chain must carry specific business logic, so there must be streaming paradigms suitable for that specific business scenario. For example, a Graph similar to ReactAgent matches the business scenarios of `Invoke` and `Stream`, so the logical calling methods for this Graph are `Invoke` and `Stream`. Although the orchestration artifact interface `Runnable[I, O]` itself has `Collect` and `Transform` methods, normal business scenarios don't need to use them. +- "Yes": Specifically, a specific Graph or Chain must carry specific business logic, so there must be a streaming paradigm suitable for that specific business scenario. For example, a Graph similar to React Agent, matching business scenarios are Invoke and Stream, so the logical calling methods for this Graph are Invoke and Stream. Although the orchestration product's interface Runnable[I, O] has Collect and Transform methods, normal business scenarios don't need to use them. -### Runtime Paradigms of Components Inside Orchestration Artifacts +### Paradigm of Internal Components at Runtime in Orchestration Products -From another perspective, since orchestration artifacts as a whole can be viewed as "components", "components" must have their own internal implementation. For example, the internal implementation logic of ChatModel might be to convert the input `[]Message` into API requests for various models, then call the model's API, and convert the response into the output `Message`. By analogy, what is the internal implementation of the Graph "component"? It's data flowing among the various components inside the Graph in the user-specified flow direction and streaming paradigms. "Flow direction" is not within the current discussion scope, while the streaming paradigms of each component at runtime are determined by the overall triggering method of the Graph. Specifically: +From another perspective, since orchestration products as a whole can be viewed as "components", then "components" must have their own internal implementation. For example, ChatModel's internal implementation logic might be converting input []Message to various model API requests, then calling the model API, and converting the response to output Message. So by analogy, what is Graph as a "component"'s internal implementation? It's data flowing between Graph's internal components in user-specified flow directions and streaming paradigms. Among them, "flow direction" is not within current discussion scope, while each component's streaming paradigm at runtime is determined by Graph's overall trigger method, specifically: -If the user calls the Graph via **Invoke**, all internal components are called with the `Invoke` paradigm. If a component does not implement the `Invoke` paradigm, the Eino framework automatically wraps the `Invoke` calling paradigm based on the streaming paradigms the component has implemented, with the following priority: +If user calls Graph through **Invoke**, all internal Graph components are called with Invoke paradigm. If a component hasn't implemented Invoke paradigm, Eino framework automatically wraps Invoke calling paradigm based on the streaming paradigm the component has implemented, with priority order as follows: -- If the component implements `Stream`, wrap `Stream` as `Invoke`, i.e., automatically concat the output stream. +- If component implements Stream, wrap Stream into Invoke, i.e., automatically concat output stream. -- Otherwise, if the component implements `Collect`, wrap `Collect` as `Invoke`, i.e., convert non-streaming input to single-frame stream. +- Otherwise, if component implements Collect, wrap Collect into Invoke, i.e., non-streaming input converts to single-frame stream. -- If neither is implemented, the component must implement `Transform`, wrap `Transform` as `Invoke`, i.e., convert input to single-frame stream and concat output. +- If neither is implemented, must implement Transform, wrap Transform into Invoke, i.e., input converts to single-frame stream, output concats. -If the user calls the Graph via **Stream/Collect/Transform**, all internal components are called with the `Transform` paradigm. If a component does not implement the `Transform` paradigm, the Eino framework automatically wraps the `Transform` calling paradigm based on the streaming paradigms the component has implemented, with the following priority: +If user calls Graph through **Stream/Collect/Transform**, all internal Graph components are called with Transform paradigm. If a component hasn't implemented Transform paradigm, Eino framework automatically wraps Transform calling paradigm based on the streaming paradigm the component has implemented, with priority order as follows: -- If the component implements `Stream`, wrap `Stream` as `Transform`, i.e., automatically concat the input stream. +- If component implements Stream, wrap Stream into Transform, i.e., automatically concat input stream. -- Otherwise, if the component implements `Collect`, wrap `Collect` as `Transform`, i.e., convert non-streaming output to single-frame stream. +- Otherwise, if component implements Collect, wrap Collect into Transform, i.e., non-streaming output converts to single-frame stream. -- If neither is implemented, the component must implement `Invoke`, wrap `Invoke` as `Transform`, i.e., concat input stream and convert output to single-frame stream. +- If neither is implemented, must implement Invoke, wrap Invoke into Transform, i.e., input stream concats, output converts to single-frame stream -Combining the various cases enumerated above, Eino framework's automatic conversion between `T` and `Stream[T]` can be summarized as: +Combining the various cases exhaustively listed above, Eino framework's automatic conversion of T and Stream[T] can be summarized as: -- **T -> Stream[T]: Box 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` may need to be provided.** +- **T -> Stream[T]: Box complete T into single-frame Stream[T]. Non-streaming becomes fake streaming.** +- **Stream[T] -> T: Concat Stream[T] into complete T. When Stream[T] is not single-frame stream, may need to provide Concat method for T.** -After seeing the implementation principles above, you might have questions: why does calling `Invoke` on a graph require all internal components to be called with `Invoke`? And why does calling `Stream/Collect/Transform` on a graph require all internal components to be called with `Transform`? After all, some counterexamples can be given: +After seeing the above implementation principles, there might be questions about why Invoke on graph requires all internal components to be called with Invoke? And why Stream/Collect/Transform on graph requires all internal components to be called with Transform? After all, counterexamples can be given: -- Components A and B are orchestrated into a Chain, called with `Invoke`. A's business interface implements `Stream`, B's business interface implements `Collect`. At this point, there are two choices for the calling paradigm of internal components in the graph: - - A is called with `Stream`, B is called with `Collect`, the overall Chain still has `Invoke` semantics, while preserving true streaming internal semantics. That is, A's output stream doesn't need to be concatenated and can be input to B in real-time. - - Current Eino implementation: A and B are both called with `Invoke`, requiring A's output stream to be concatenated and B's input to be made into a pseudo-stream. True streaming internal semantics are lost. -- Components A and B are orchestrated into a Chain, called with `Collect`. A implements `Transform` and `Collect`, B implements `Invoke`. Two choices: - - A is called with `Collect`, B is called with `Invoke`: the overall still has `Collect` semantics, no automatic conversion or boxing operations are needed by the framework. - - Current Eino implementation: A and B are both called with `Transform`. Since A's business interface implements `Transform`, both A's output and B's input could be true streaming, but B's business interface only implements `Invoke`. According to the analysis above, B's input will need to be concatenated from true streaming to non-streaming. At this point, the user needs to additionally provide a concat function for B's input, which could have been avoided. +- Two components A, B orchestrated into a Chain, called with Invoke. A's business interface implements Stream, B's business interface implements Collect. At this point, there are two choices for graph internal component calling paradigms: + - A is called with stream, B is called with collect, the overall Chain still has Invoke semantics, while preserving true streaming internal semantics. I.e., A's output stream doesn't need Concat, can be input to B in real-time. + - Current Eino implementation, both A and B are called with Invoke, need to Concat A's output stream, and make B's input fake streaming. Loses true streaming internal semantics. +- Two components A, B orchestrated into a Chain, called with Collect. A implements Transform and Collect, B implements Invoke. Two choices: + - A is called with Collect, B is called with Invoke: overall still has Collect semantics, no need for framework to do any automatic conversion or boxing operations. + - Current Eino implementation, both A and B are called with Transform. Since A's business interface implements Transform, both A's output and B's input may be true streaming. But B's business interface only implements Invoke, based on above analysis, B's input needs to be concatenated from true streaming to non-streaming. At this point, users need to additionally provide B's input concat function, which could have been avoided. -The two examples above can both find a clear, different from Eino's convention, but better streaming call path. However, when generalized to arbitrary orchestration scenarios, it's difficult to find a clearly defined, different from Eino's convention, yet always better universal rule. For example, A->B->C, called with `Collect` semantics, should `Collect` happen at A->B or B->C? Potential factors include the business interfaces specifically implemented by A, B, C, possibly the judgment of "use true streaming as much as possible", and maybe which parameters implement `Concat` and which don't. If it's a more complex Graph, the factors to consider will increase rapidly. In this situation, even if the framework can define a clear, better universal rule, it would be hard to explain clearly, and the understanding and usage cost would be high, likely already exceeding the actual benefit brought by this new rule. +Both examples above can find a clear, different from Eino's convention, but better streaming calling path. However, when generalized to arbitrary orchestration scenarios, it's hard to find a clearly defined, different from Eino's convention, yet always better universal rule. For example, A->B->C, called with Collect semantics, is it A->B that does Collect, or B->C? Potential factors include specific business interfaces implemented by A, B, C, possibly also "use as much true streaming as possible" judgment, maybe also which parameter implements Concat and which doesn't. For more complex Graphs, factors to consider increase rapidly. In this situation, even if the framework can define a set of clear, better universal rules, it would be hard to explain clearly, understanding and usage costs would be high, likely already exceeding the actual benefits brought by this new rule. -In summary, we can say that the runtime paradigms of components inside Eino orchestration artifacts are **By Design**, clearly as follows: +In summary, we can say the paradigm of each component at runtime within Eino orchestration products is **By Design**, specifically: -- **Called with `Invoke` overall, all internal components are called with `Invoke`, there is no streaming process.** -- **Called with `Stream/Collect/Transform` overall, all internal components are called with `Transform`. When `Stream[T] -> T` concat process occurs, a concat function for `T` may need to be additionally provided.** +- **Called overall with Invoke, internal components all called with Invoke, no streaming process exists.** +- **Called overall with Stream/Collect/Transform, internal components all called with Transform. When Stream[T] -> T concat process occurs, may need to additionally provide T's 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 ea063b20fa0..7b69f914dd1 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,15 +1,15 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Workflow Orchestration Framework' weight: 3 --- -## What Is Eino Workflow? +## What is Eino Workflow -An orchestration API at the same architectural level as Graph: +It's a set of orchestration APIs at the same architectural level as Graph API: ```mermaid flowchart LR @@ -22,64 +22,64 @@ flowchart LR G --> C ``` -Key traits: +Essential characteristics: -- 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`. +- Has the same level of capability as Graph API, both are suitable framework tools for orchestrating "information flow around large models". + - Maintains consistency in node types, stream processing, callback, option, state, interrupt & checkpoint, etc. + - Implements AnyGraph interface, can be added as a sub-graph to parent Graph/Chain/Workflow through AddGraphNode. + - Can also add other Graph/Chain/Workflow as its own sub-graphs. +- Field-level mapping capability: A node's input can be composed of any output fields from any predecessor nodes. + - Natively supports struct, map, and mutual mapping between structs and maps of any nesting level. +- Separation of control flow and data flow: Graph's Edge determines both execution order and data transfer. In Workflow, they can be transferred together or separately. +- Does not support cycles (i.e., loops like chatmodel->toolsNode->chatmodel in react agent). NodeTriggerMode is fixed to AllPredecessor. -## Why Workflow? +## Why Use Workflow -### Flexible Input/Output Types +### Flexible Input and Output Types -For example, you need to orchestrate two lambda nodes containing two "existing business functions f1, f2" with specific struct inputs/outputs suited to business scenarios, each different: +For example, when orchestrating two lambda nodes containing two "existing business functions f1, f2", whose input and output types are specific structs matching business scenarios, each different: -When orchestrating with Workflow, map f1's output field F1 directly to f2's input field F3, while preserving the original function signatures of f1 and f2. The effect achieved is: **each node's input/output is "determined by the business scenario", without needing to consider "who provides my input and who uses my output"**. +When orchestrating with Workflow, map f1's output field F1 directly to f2's input field F3, while keeping f1, f2's original function signatures. The effect achieved is: **Each node is "business scenario determines input and output", no need to consider "who gives me input, and who uses my output"**. -When orchestrating with Graph, due to the "type alignment" requirement, if f1 → f2, then f1's output type and f2's input type need to align. You must choose one of two options: +When orchestrating with Graph, due to the "type alignment" requirement, if f1 -> f2, then f1's output type and f2's input type need to be aligned, requiring one of two choices: -- Define a new common struct, and change both f1's output type and f2's input type to this common struct. This has costs and may intrude on business logic. -- Change both f1's output type and f2's input type to map. This loses the strong typing alignment characteristic. +- Define a new common struct, change both f1's output type and f2's input type to this common struct. Has cost, may intrude on business logic. +- Change both f1's output type and f2's input type to map. Loses the strong type alignment characteristic. -### Separate Control and Data Flow +### Separation of Control Flow and Data Flow -Consider the following scenario: +Look at the following scenario: -Node D references certain output fields from A, B, and C simultaneously. The dashed line from A to D is purely "data flow", not carrying "control" information - meaning whether A completes execution does not determine whether D starts execution. +Node D simultaneously references certain output fields from A, B, C. The dashed line from A to D is purely "data flow", not transmitting "control" information, meaning A's execution status doesn't determine whether D can start executing. -The bold arrow from node D to E represents that node E does not reference any output from node D - it is purely "control flow", not carrying "data". Whether D completes execution determines whether E starts execution, but D's output does not affect E's input. +The thick arrow from node D to E represents pure "control flow", not transmitting "data". Meaning D's completion status determines whether E starts executing, but D's output doesn't affect E's input. -Other lines in the diagram combine control flow and data flow. +Other lines in the figure combine control flow and data flow. -Note that data flow can only be transmitted when a control flow path exists. For example, the A→D data flow depends on the existence of A→branch→B→D or A→branch→C→D control flow. Data flow can only reference outputs from predecessor nodes. +Note that the premise for data flow transfer is that a control flow must exist. For example, the data flow from A->D depends on control flow from A->branch->B->D or A->branch->C->D existing. Data flow can only reference predecessor nodes' outputs. -For example, this "cross-node" specific data passing scenario: +For example, this scenario of "cross-node" transferring specific data: -In the diagram above, the chat template node's input can be very explicit: +In the figure above, the chat template node's input can be very explicit: `map[string]any{"prompt": "prompt from START", "context": "retrieved context"}` -In contrast, if using Graph or Chain API, you must choose one of two options: +In contrast, if using Graph or Chain API, one of two choices is needed: -- Use OutputKey to convert node output types (can't add to START node, so need an extra passthrough node), and the ChatTemplate node's input will include the full output of START and retriever (rather than just the specific fields actually needed). -- Put START node's prompt in state, and ChatTemplate reads from state. This introduces additional state. +- Use OutputKey to convert node output type (can't add to START node, so need to add extra passthrough node). ChatTemplate node's input will include full output from START and retriever (not just the actually needed fields). +- Put START node's prompt in state, ChatTemplate reads from state. Additionally introduces state. -## Using Workflow +## How to Use Workflow ### Simplest Workflow -START → node → END +START -> node -> END @@ -132,30 +132,30 @@ func main() { Core APIs: - `func NewWorkflow[I, O any](opts ...NewGraphOption) *Workflow[I, O]` - - Creates a new Workflow. - - Signature is identical to `NewGraph`. -- `func (wf *Workflow[I, O]) AddChatModelNode(key string, chatModel model.BaseChatModel, opts ...GraphAddNodeOpt) *WorkflowNode` - - Adds a new node to the Workflow. - - Supported node types are identical to Graph. - - Unlike Graph's AddXXXNode which returns an error immediately, Workflow defers error handling to the final Compile step. - - AddXXXNode returns a WorkflowNode, allowing subsequent field mapping operations via method chaining. + - Build a new Workflow. + - Signature completely identical to `NewGraph`. +- `func (wf *Workflow[I, O]) AddChatModelNode(key string, chatModel model.BaseChatModel, opts ...GraphAddNodeOpt) *WorkflowNode ` + - Add a new node to Workflow. + - Node types that can be added are completely identical to Graph. + - Difference from Graph's AddXXXNode is that Workflow doesn't return error immediately, but handles and returns errors uniformly at final Compile. + - AddXXXNode returns a WorkflowNode, subsequent operations like adding field mappings to the Node are done directly with Method Chaining - `func (n *WorkflowNode) AddInput(fromNodeKey string, inputs ...*FieldMapping) *WorkflowNode` - - Adds input field mappings to a WorkflowNode. - - Returns WorkflowNode for continued method chaining. + - Add input field mappings to a WorkflowNode + - Returns WorkflowNode, can continue Method Chaining. - `(wf *Workflow[I, O]) Compile(ctx context.Context, opts ...GraphCompileOption) (Runnable[I, O], error)` - - Compiles a Workflow. - - Signature is identical to compiling a Graph. + - Compile a Workflow. + - Signature completely identical to Compile Graph. ### Field Mapping -START (struct input) → [parallel lambda c1, c2] → END (map output). +START (input struct) -> [parallel lambda1, lambda2] -> END (output map). -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 diagram below, the workflow's overall input is a message struct, both `c1` and `c2` lambdas have counter struct inputs, both output int, and the workflow's overall output is `map[string]any`: +Let's use an example of "counting character occurrences in a string". The workflow overall inputs an eino Message and a sub string, gives Message.Content to a counter c1, gives Message.ReasoningContent to another counter c2, calculates sub string occurrence counts in parallel, then maps them to END respectively: +In the figure above, workflow's overall input is a message struct, c1 and c2 lambdas' inputs are both counter struct, outputs are both int, workflow's overall output is map[string]any. Code as follows: + ```go // demonstrates the field mapping ability of eino workflow. func main() { @@ -164,7 +164,7 @@ func main() { SubStr string // exported because we will do field mapping for this field } - // wordCounter is a lambda function that counts occurrences of SubStr within FullStr + // wordCounter is a lambda function that count occurrences of SubStr within FullStr wordCounter := func(ctx context.Context, c counter) (int, error) { return strings.Count(c.FullStr, c.SubStr), nil } @@ -188,15 +188,17 @@ 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 c2's SubStr field + // map START's SubStr field to lambda c1's SubStr field compose.MapFields("SubStr", "SubStr"), - // map START's Message's ReasoningContent field to lambda c2's FullStr field + // map START's Message's ReasoningContent field to lambda c1's FullStr field compose.MapFieldPaths([]string{"Message", "ReasoningContent"}, []string{"FullStr"})) wf.End(). // Obtain the compose.END for method chaining - // add an input from c1, mapping full output of c1 to the map key 'content_count' + // 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 @@ -224,31 +226,31 @@ func main() { } ``` -[Eino example link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/2_field_mapping/main.go) +[Eino example code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/2_field_mapping/main.go) -The `AddInput` method accepts 0–n field mappings and can be called multiple times. This means: +The main information from this example is that the `AddInput` method can pass 0-n field mapping rules, and can call `AddInput` multiple times. This means: -- 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` +- A node can reference any number of fields from one predecessor node's output. +- 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 mapping between nested fields. +- Different types above have different APIs to express the mapping: + - Top-level field to top-level field: `MapFields(string, string)` + - Full output to top-level field: `ToField(string)` + - Top-level field to full input: `FromField(string)` + - Nested field to nested field: `MapFieldPaths(FieldPath, FieldPath)`, needed when either upstream or downstream is nested + - Full output to nested field: `ToFieldPath(FieldPath)` + - Nested field to full input: `FromFieldPath(FieldPath)` + - Full output to full input: Just use `AddInput`, no need to pass `FieldMapping` ## Advanced Features -### Data-only Edges (No Control Flow) +### Data Flow Only, No Control Flow -Imagine a simple scenario: START → adder node → multiplier node → END. The "multiplier node" multiplies one field from START with the result from the adder node: +Imagine a simple scenario: START -> add node -> multiply node -> END. Where "multiply node" multiplies a field from START with the result from add node: -In the diagram above, the multiplier node executes after the adder node, meaning the "multiplier node" is controlled by the "adder node". However, the START node does not directly control the "multiplier node"; it only passes data to it. In code, use `AddInputWithOptions(fromNode, fieldMappings, WithNoDirectDependency)` to specify a pure data flow: +In the figure above, multiply node executes after add node, i.e., "multiply node" is controlled by "add node". But START node doesn't directly control "multiply node", only passes data over. In code, use `AddInputWithOptions(fromNode, fieldMappings, WithNoDirectDependency)` to specify pure data flow: ```go func main() { @@ -307,7 +309,7 @@ func main() { } ``` -[Eino examples link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/3_data_only/main.go) +[Eino examples code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/3_data_only/main.go) New API introduced in this example: @@ -317,7 +319,7 @@ func (n *WorkflowNode) AddInputWithOptions(fromNodeKey string, inputs []*FieldMa } ``` -And the new Option: +And new Option: ```go func WithNoDirectDependency() WorkflowAddInputOpt { @@ -327,19 +329,19 @@ func WithNoDirectDependency() WorkflowAddInputOpt { } ``` -Combined, these can add pure "data dependency relationships" to nodes. +Combined, can add pure "data dependency relationship" to nodes. -### Control-only Edges (No Data Flow) +### Control Flow Only, No Data Flow -Imagine a "sequential bidding with confidential prices" scenario: START → bidder1 → threshold check → bidder2 → END: +Imagine an "sequential bidding, but price confidential" scenario: START -> bidder 1 -> is qualified -> bidder 2 -> END: -In the diagram above, regular lines are "control + data", dashed lines are "data only", and bold lines are "control only". The logic is: input an initial price, bidder1 makes bid1, a branch checks if it's high enough - if so, end directly; otherwise, pass the initial price to bidder2 for bid2, and finally aggregate both bids for output. +In the figure above, normal lines are "control + data", dashed lines are "data only", bold lines are "control only". Logic is: input an initial price, bidder 1 gives bid 1, branch judges if it's high enough, if high enough then end directly, otherwise give initial price to bidder 2, give bid 2, finally summarize bids 1 and 2 for output. -After bidder1 bids, an announcement is made: "bidder completed bidding". Note that bidder1→announcer is a bold solid line, "control only", because the amount must be kept confidential when announcing! +After bidder 1 gives bid, announce "bidder completed bidding". Note bidder1->announcer is bold line, "control only", because the price must be kept confidential when announcing! -The two bold lines from the branch are both "control only" because neither bidder2 nor END depends on data from the branch. In code, use `AddDependency(fromNode)` to specify pure control flow: +Both bold lines from branch are "control only", because neither bidder2 nor END depends on data from branch. In code, use `AddDependency(fromNode)` to specify pure control flow: ```go func main() { @@ -402,7 +404,7 @@ func main() { } ``` -[Eino examples link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/4_control_only_branch/main.go) +[Eino examples code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/4_control_only_branch/main.go) New API introduced in this example: @@ -412,11 +414,11 @@ func (n *WorkflowNode) AddDependency(fromNodeKey string) *WorkflowNode { } ``` -You can use `AddDependency` to specify pure "control dependency relationships" for nodes. +Can specify pure "control dependency relationship" for nodes through AddDependency. ### Branch -In the example above, we added a branch in almost the same way as with the Graph API: +In the example above, we added a branch in almost exactly the same way as Graph API: ```go // add a branch just like adding branch in Graph. @@ -428,20 +430,20 @@ In the example above, we added a branch in almost the same way as with the Graph }, map[string]bool{compose.END: true, "b2": true})) ``` -Branch semantics are the same as Graph's AllPredecessor mode: +Branch semantics are the same as branch semantics in Graph's AllPredecessor mode: -- There is exactly one 'fromNode', meaning a branch can only have one predecessor control node. -- Can be single-select (`NewGraphBranch`) or multi-select (`NewGraphMultiBranch`). -- Selected branches can execute. Unselected branches are marked as skip. -- A node can only execute when all incoming edges are complete (success or skip), and at least one edge succeeded. (Like END in the example above) -- If all incoming edges of a node are skip, all outgoing edges of that node are automatically marked as skip. +- Has one and only one 'fromNode', i.e., a branch's preceding control node can only be one. +- Can single-select (NewGraphBranch), can multi-select (NewGraphMultiBranch). +- Branch-selected branches are executable. Unselected branches are marked as skip. +- A node can only execute when all incoming edges are complete (success or skip), and at least one edge succeeds. (Like END in the example above) +- If all incoming edges of a node are skip, all outgoing edges of this node are automatically marked as skip. -Additionally, there is one key difference between workflow branch and graph branch: +Meanwhile, workflow branch has one core difference from graph branch: -- Graph branch is always "control and data combined"; the downstream node's input of a branch is always the output of the branch's fromNode. -- Workflow branch is always "control only"; the downstream node's input is specified via `AddInputWithOptions`. +- Graph branch is always "control and data combined", branch downstream node's input is always branch fromNode's output. +- Workflow branch is always "control only", branch downstream node's input is specified through AddInputWithOptions. -Related API: +New API involved: ```go func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *WorkflowBranch { @@ -455,11 +457,11 @@ func (wf *Workflow[I, O]) AddBranch(fromNodeKey string, branch *GraphBranch) *Wo } ``` -Signature is almost identical to `Graph.AddBranch`, allowing you to add a branch to the workflow. +Signature almost completely identical to Graph.AddBranch, can add a branch to workflow. ### Static Values -Let's modify the "bidding" example above by giving bidder1 and bidder2 each a static "budget" configuration: +Let's modify the "bidding" example above, giving bidders 1 and 2 each a "budget" static configuration: @@ -522,9 +524,9 @@ func main() { } ``` -[Eino examples link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/5_static_values/main.go) +[Eino examples code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/5_static_values/main.go) -Related API: +New API involved here: ```go func (n *WorkflowNode) SetStaticValue(path FieldPath, value any) *WorkflowNode { @@ -533,22 +535,22 @@ func (n *WorkflowNode) SetStaticValue(path FieldPath, value any) *WorkflowNode { } ``` -Use this method to set static values on specified fields of Workflow nodes. +Set static values on specified fields of Workflow nodes through this method. -### Streaming Effects +### Streaming Effect -Returning to the previous "character counting" example, if our workflow's input 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": +Going back to the previous "character counting" example, if our workflow's input 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": We make some modifications to the previous example: -- Change `InvokableLambda` to `TransformableLambda`, so it can consume streams and produce streams. -- Change the `SubStr` in the input to a static value, injected into c1 and c2. -- Change the Workflow's overall input to `*schema.Message`. -- Call the workflow using Transform, passing a stream containing 2 `*schema.Message`. +- Change InvokableLambda to TransformableLambda, so it can consume streams and produce streams. +- Change SubStr in input to static value, injected into c1 and c2. +- Change Workflow's overall input to *schema.Message. +- Call workflow with Transform method, passing a stream containing 2 *schema.Messages. -The completed code: +Completed code: ```go // demonstrates the stream field mapping ability of eino workflow. @@ -560,7 +562,7 @@ func main() { } // wordCounter is a transformable lambda function that - // counts occurrences of SubStr within FullStr for each chunk. + // count occurrences of SubStr within FullStr, for each trunk. wordCounter := func(ctx context.Context, c *schema.StreamReader[counter]) ( *schema.StreamReader[int], error) { var subStr, cachedStr string @@ -657,45 +659,45 @@ func main() { } ``` -[Eino examples link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/6_stream_field_map/main.go) +[Eino examples code link](https://github.com/cloudwego/eino-examples/blob/main/compose/workflow/6_stream_field_map/main.go) Based on the example above, we summarize some characteristics of workflow streaming: -- Still 100% Eino stream: four paradigms (invoke, stream, collect, transform), automatically converted, copied, concatenated, and merged by the Eino framework. -- Field mapping configuration doesn't require special handling for streams: regardless of whether the actual input/output is a stream, the `AddInput` syntax is the same. The Eino framework handles stream-based mapping. -- Static values don't require special handling for streams: even if the actual input is a stream, you can use `SetStaticValue` the same way. The Eino framework will place static values in the input stream, but not necessarily in the first chunk read. +- Still 100% Eino stream: Four paradigms (invoke, stream, collect, transform), automatically converted, copied, concatenated, merged by Eino framework. +- Field mapping configuration doesn't need special handling for streams: Whether actual input/output is a stream or not, AddInput is written the same way, Eino framework handles stream-based mapping. +- Static values don't need special handling for streams: Even if actual input is a stream, SetStaticValue works the same way. Eino framework puts static values in input stream, but not necessarily in the first chunk read. ### Field Mapping Scenarios #### Type Alignment -Workflow follows the same type alignment rules as Graph, except the alignment granularity changes from complete input/output alignment to alignment between mapped field pairs. Specifically: +Workflow follows the same type alignment rules as Graph, just the alignment granularity changes from complete input/output alignment to type alignment between paired mapped fields. Specifically: -- Identical types: passes compile-time validation and will definitely align. -- Different types but upstream can be assigned to downstream (e.g., upstream is a concrete type, downstream is `any`): passes compile-time validation and will definitely align. -- Upstream cannot be assigned to downstream (e.g., upstream is `int`, downstream is `string`): compile error. -- Upstream may be assignable to downstream (e.g., upstream is `any`, downstream is `int`): cannot be determined at compile time, deferred to runtime. At runtime, the actual upstream type is extracted and checked. If upstream cannot be assigned to downstream, an error is thrown. +- Types completely identical, will pass Compile validation, alignment is guaranteed. +- Types different, but upstream can be Assigned to downstream (e.g., upstream is concrete type, downstream is Any), will pass Compile validation, alignment is guaranteed. +- Upstream cannot be Assigned to downstream (e.g., upstream is int, downstream is string), will report error at Compile. +- Upstream may be Assignable to downstream (e.g., upstream is Any, downstream is int), cannot be determined at Compile, will be postponed to runtime, when upstream's actual type is extracted, then judge. At this point if it's determined upstream cannot be Assigned to downstream, an error will be thrown. #### Merge Scenarios -Merge refers to situations where a node's input is mapped from multiple `FieldMapping`s: +Merge refers to cases where a node's input maps from multiple `FieldMappings`. -- Mapping to multiple different fields: supported -- Mapping to the same single field: not supported -- Mapping to the whole input while also having mappings to specific fields: conflict, not supported +- Mapping to multiple different fields: Supported +- Mapping to the same field: Not supported +- Mapping to whole, while also mapping to field: Conflict, not supported -#### Nested `map[string]any` +#### Nested map[string]any -For example, this mapping: `ToFieldPath([]string{"a","b"})`, where the target node's input type is `map[string]any`, the mapping order is: +For example this mapping: `ToFieldPath([]string{"a","b"})`, target node's input type is `map[string]any`, mapping order is: -1. First level "a": result is `map[string]any{"a": nil}` -2. Second level "b": result is `map[string]any{"a": map[string]any{"b": x}}` +1. First level "a", result at this point is `map[string]any{"a": nil}` +2. Second level "b", result at this point is `map[string]any{"a": map[string]any{"b": x}}` -As you can see, at the second level, the Eino framework automatically replaces `any` with the actual `map[string]any`. +As you can see, at the second level, Eino framework automatically replaced any with actual `map[string]any` #### CustomExtractor -In some scenarios, standard field mapping semantics cannot support the requirement. For example, if upstream is `[]int` and you want to extract the first element to map to downstream, use `WithCustomExtractor`: +Some scenarios where standard field mapping semantics cannot support, like upstream is []int, want to extract first element to map to downstream, we use `WithCustomExtractor`: ```go t.Run("custom extract from array element", func(t *testing.T) { @@ -711,20 +713,20 @@ t.Run("custom extract from array element", func(t *testing.T) { }) ``` -When using `WithCustomExtractor`, all compile-time type alignment checks cannot be performed and can only be deferred to runtime validation. +When using WithCustomExtractor, all Compile-time type alignment validation cannot be performed, can only be postponed to runtime validation. -### Constraints +### Some Constraints -- Map key restrictions: only `string`, or string alias (types that can be converted to `string`). +- Map Key restriction: Only supports string, or string alias (type that can convert to string). - Unsupported CompileOptions: - `WithNodeTriggerMode`, because it's fixed to `AllPredecessor`. - - `WithMaxRunSteps`, because there are no cycles. -- If the mapping source is a Map Key, the Map must contain this key. However, if the mapping source is a Stream, Eino cannot determine whether this key appears at least once across all frames in the stream, so validation cannot be performed for Streams. -- If the mapping source field or target field belongs to a struct, these fields must be exported, because reflection is used internally. -- Nil mapping source: generally supported, only errors when the mapping target cannot be nil, such as basic types (int, etc.). + - `WithMaxRunSteps`, because there won't be cycles. +- If mapping source is Map Key, requires the Map to have this key. But if mapping source is Stream, Eino cannot determine if all frames in the stream have this key at least once, so cannot validate for Stream. +- If mapping source field or target field belongs to struct, requires these fields to be exported, because reflection is used internally. +- Mapping source is nil: Generally supported, only reports error when mapping target cannot possibly be nil, such as when target is basic type (int, etc.). -## Real-world Usage +## Practical Applications ### Coze-Studio Workflow -[Coze-Studio](https://github.com/coze-dev/coze-studio) open source version's workflow engine is built on the Eino Workflow orchestration framework. See: [11. Adding New Workflow Node Types (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](https://github.com/coze-dev/coze-studio) open source version's workflow engine is based on Eino Workflow orchestration framework. See: [11. Adding New Workflow Node Types (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) diff --git a/content/en/docs/eino/core_modules/components/agentic_chat_model_guide.md b/content/en/docs/eino/core_modules/components/agentic_chat_model_guide.md index 3724c41bbba..eec475b6961 100644 --- a/content/en/docs/eino/core_modules/components/agentic_chat_model_guide.md +++ b/content/en/docs/eino/core_modules/components/agentic_chat_model_guide.md @@ -1,26 +1,36 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino: AgenticModel Guide [Beta]' +title: 'Eino: AgenticModel User Guide [Beta]' weight: 10 --- -## Overview +## Introduction -AgenticModel is a model capability abstraction centered on "goal-driven autonomous execution". As capabilities like caching and built-in tools gain native support in advanced provider APIs such as OpenAI Responses API and Claude API, models are evolving from "one-shot Q&A engines" to "goal-oriented autonomous agents": capable of closed-loop planning, tool invocation, and iterative execution around user goals to accomplish more complex tasks. +AgenticModel is an abstraction of model capabilities centered on "goal-driven autonomous execution". As capabilities like caching and built-in tools become natively supported in APIs from advanced providers like OpenAI Responses API and Claude API, models are evolving from "one-shot Q&A engines" to "user goal-oriented autonomous agents": capable of closed-loop planning around objectives, invoking tools, and iterative execution to accomplish more complex tasks. ### Differences from ChatModel - + - +
    AgenticModelChatModel
    PositioningModel capability abstraction centered on "goal-driven autonomous execution", an enhanced abstraction over ChatModelOne-shot Q&A engine
    PositioningModel component abstraction based on AgenticMessageModel component abstraction based on Message
    Core Entities
  • AgenticMessage
  • ContentBlock
  • Message
    Capabilities
  • Multi-turn model conversation generation
  • Session caching
  • Support for various built-in tools
  • Support for MCP tools
  • Better model adaptability
  • Single-turn model conversation generation
  • Session caching
  • Support for simple built-in tools
  • Related Components
  • AgenticModel
  • AgenticTemplate
  • AgenticToolsNode
  • ChatModel
  • ChatTemplate
  • ToolsNode
  • Related Components
  • AgenticTemplate
  • AgenticToolsNode
  • ChatTemplate
  • ToolsNode
  • +Server-side tools (like web_search) and MCP tools are natively supported by model providers, meaning a single API request/response may contain results from multiple reasoning-action cycles. Taking web search capability as an example: + +- Implemented with ChatModel: requires pre-defining a custom web search tool. One reasoning-action process is as follows: + 1. Model generates tool call parameters + 2. User-side executes the tool + 3. Returns tool result to the model +- Implemented with AgenticModel: can directly configure native web search tools provided by the model provider. One API request process is as follows: + 1. The model autonomously calls the web search tool based on the user's question and completes multiple tool calls on the model server side, generating multiple reasoning-action results until the user's task is completed. + 2. The user side only needs to receive the results. + ## Component Definition ### Interface Definition @@ -40,29 +50,29 @@ type AgenticModel interface { #### Generate Method -- Purpose: Generate a complete model response +- Function: Generate a complete model response - Parameters: - ctx: Context object for passing request-level information and Callback Manager - - input: List of input messages + - input: Input message list - opts: Optional parameters for configuring model behavior -- Returns: +- Return values: - `*schema.AgenticMessage`: The response message generated by the model - error: Error information during generation #### Stream Method -- Purpose: Generate model response in streaming mode +- Function: Generate model response in streaming mode - Parameters: Same as Generate method -- Returns: +- Return values: - `*schema.StreamReader[*schema.AgenticMessage]`: Stream reader for model response - error: Error information during generation #### WithTools Method -- Purpose: Bind available tools to the model +- Function: Bind available tools to the model - Parameters: - tools: List of tool information -- Returns: +- Return values: - Model: A new AgenticModel instance with tools bound - error: Error information during binding @@ -70,7 +80,7 @@ type AgenticModel interface { > Code location: [https://github.com/cloudwego/eino/tree/main/schema/agentic_message.go](https://github.com/cloudwego/eino/tree/main/schema/agentic_message.go) -`AgenticMessage` is the basic unit for interacting with the model. A complete model response is encapsulated as an `AgenticMessage`, which carries complex composite content through an ordered list of `ContentBlock`. The definition is as follows: +`AgenticMessage` is the basic unit for interacting with the model. A complete response from the model is encapsulated as an `AgenticMessage`, which carries complex composite content through an ordered set of `ContentBlock`s. The biggest difference between `AgenticMessage` and `Message` is the introduction of the `ContentBlock` array concept, which can carry multiple reasoning-action outputs from the `AgenticModel`. Definition: ```go type AgenticMessage struct { @@ -88,7 +98,7 @@ type AgenticMessage struct { } ``` -`ContentBlock` is the basic building unit of `AgenticMessage`, used to carry the specific content of a message. It is designed as a polymorphic structure that identifies the type of data contained in the current block through the `Type` field and holds the corresponding non-null pointer field. `ContentBlock` enables a message to contain mixed-type rich media content or structured data, such as "text + image" or "reasoning process + tool call". The definition is as follows: +`ContentBlock` is the basic building unit of `AgenticMessage`, used to carry the specific content of a message. It's designed as a polymorphic structure that identifies what type of data the current block contains through the `Type` field and holds the corresponding non-null pointer field. `ContentBlock` allows a message to contain mixed rich media content or structured data, such as "text + image" or "reasoning process + tool call". Definition: ```go type ContentBlockType string @@ -183,7 +193,7 @@ type ContentBlock struct { } ``` -`AgenticResponseMeta` is the metadata returned in the model response, where `TokenUsage` is the metadata returned by all model providers. `OpenAIExtension`, `GeminiExtension`, and `ClaudeExtension` are extension field definitions specific to OpenAI, Gemini, and Claude models respectively; extension information from other model providers is placed in `Extension`, with specific definitions provided by the corresponding component implementations in **eino-ext**. +`AgenticResponseMeta` is the metadata returned in model responses, where `TokenUsage` is common metadata returned by all model providers. `OpenAIExtension`, `GeminiExtension`, and `ClaudeExtension` are extension field definitions specific to OpenAI, Gemini, and Claude models respectively; extension information from other model providers is placed in `Extension`, with specific definitions provided by the corresponding component implementations in **eino-ext**. ```go type AgenticResponseMeta struct { @@ -206,7 +216,7 @@ type AgenticResponseMeta struct { #### Reasoning -The Reasoning type is used to represent the model's reasoning process and thinking content. Some advanced models can perform internal reasoning before generating the final answer, and this reasoning content can be passed through this type. +The Reasoning type represents the model's reasoning process and thinking content. Some advanced models can perform internal reasoning before generating the final answer, and this reasoning content can be conveyed through this type. - Definition @@ -232,7 +242,7 @@ reasoning := &schema.Reasoning{ #### UserInputText -UserInputText is the most basic content type, used to pass plain text input. It is the primary way for users to interact with the model, suitable for natural language conversations, instruction delivery, and question asking. +UserInputText is the most basic content type for conveying plain text input. It's the primary way users interact with models, suitable for natural language dialogue, instruction delivery, and question asking. - Definition @@ -247,18 +257,18 @@ type UserInputText struct { ```go textInput := &schema.UserInputText{ - Text: "Please help me analyze the performance bottlenecks in this code", + Text: "Please help me analyze the performance bottleneck of this code", } // Or use convenience functions to create messages -textInput := schema.UserAgenticMessage("Please help me analyze the performance bottlenecks in this code") +textInput := schema.UserAgenticMessage("Please help me analyze the performance bottleneck of this code") textInput := schema.SystemAgenticMessage("You are an intelligent assistant") textInput := schema.DeveloperAgenticMessage("You are an intelligent assistant") ``` #### UserInputImage -UserInputImage is used to provide image content to the model. It supports passing image data via URL reference or Base64 encoding, suitable for visual understanding, image analysis, and multimodal conversations. +UserInputImage is used to provide image content to the model. It supports passing image data via URL reference or Base64 encoding, suitable for visual understanding, image analysis, and multimodal dialogue scenarios. - Definition @@ -297,7 +307,7 @@ imageInput := &schema.UserInputImage{ #### UserInputAudio -UserInputAudio is used to provide audio content to the model. It is suitable for speech recognition, audio analysis, and multimodal understanding. +UserInputAudio is used to provide audio content to the model. It's suitable for speech recognition, audio analysis, and multimodal understanding scenarios. - Definition @@ -325,7 +335,7 @@ audioInput := &schema.UserInputAudio{ #### UserInputVideo -UserInputVideo is used to provide video content to the model. It is suitable for video understanding, scene analysis, and action recognition and other advanced visual tasks. +UserInputVideo is used to provide video content to the model. It's suitable for video understanding, scene analysis, and action recognition advanced visual tasks. - Definition @@ -353,7 +363,7 @@ videoInput := &schema.UserInputVideo{ #### UserInputFile -UserInputFile is used to provide file content to the model. It is suitable for document analysis, data extraction, and knowledge understanding. +UserInputFile is used to provide file content to the model. It's suitable for document analysis, data extraction, and knowledge understanding scenarios. - Definition @@ -385,7 +395,7 @@ fileInput := &schema.UserInputFile{ #### AssistantGenText -AssistantGenText is the text content generated by the model, which is the most common form of model output. For different model providers, the extension field definitions vary: OpenAI models use `OpenAIExtension`, Claude models use `ClaudeExtension`; extension information from other model providers is placed in `Extension`, with specific definitions provided by the corresponding component implementations in **eino-ext**. +AssistantGenText is the text content generated by the model, the most common form of model output. Extension field definitions differ for different model providers: OpenAI models use `OpenAIExtension`, Claude models use `ClaudeExtension`; extension information from other model providers is placed in `Extension`, with specific definitions provided by corresponding component implementations in **eino-ext**. - Definition @@ -430,13 +440,13 @@ type AssistantGenText struct { "github.com/cloudwego/eino-ext/components/model/agenticark" ) - // Assert to specific implementation definition + // Assert to concrete implementation definition ext := textGen.Extension.(*agenticark.AssistantGenTextExtension) ``` #### AssistantGenImage -AssistantGenImage is the image content generated by the model. Some models have image generation capabilities and can create images based on text descriptions, with the output passed through this type. +AssistantGenImage is the image content generated by the model. Some models have image generation capabilities and can create images based on text descriptions, with output results conveyed through this type. - Definition @@ -464,7 +474,7 @@ imageGen := &schema.AssistantGenImage{ #### AssistantGenAudio -AssistantGenAudio is the audio content generated by the model. Some models have audio generation capabilities, and the output audio data is passed through this type. +AssistantGenAudio is the audio content generated by the model. Some models have audio generation capabilities, with output audio data conveyed through this type. - Definition @@ -492,7 +502,7 @@ audioGen := &schema.AssistantGenAudio{ #### AssistantGenVideo -AssistantGenVideo is the video content generated by the model. Some models have video generation capabilities, and the output video data is passed through this type. +AssistantGenVideo is the video content generated by the model. Some models have video generation capabilities, with output video data conveyed through this type. - Definition @@ -520,7 +530,7 @@ audioGen := &schema.AssistantGenAudio{ #### FunctionToolCall -FunctionToolCall represents a user-defined function tool call initiated by the model. When the model needs to perform a specific function, it generates a tool call request containing the tool name and parameters, with the actual execution handled by the user side. +FunctionToolCall represents a user-defined function tool call initiated by the model. When the model needs to execute a specific function, it generates a tool call request containing the tool name and parameters, with actual execution handled by the user side. - Definition @@ -549,7 +559,7 @@ toolCall := &schema.FunctionToolCall{ #### FunctionToolResult -FunctionToolResult represents the execution result of a user-defined function tool. After the user side executes the tool call, the result is returned to the model through this type, allowing the model to continue generating responses. +FunctionToolResult represents the execution result of a user-defined function tool. After the user side executes a tool call, the result is returned to the model through this type, allowing the model to continue generating responses. - Definition @@ -575,7 +585,7 @@ toolResult := &schema.FunctionToolResult{ Result: `{"temperature": 15, "condition": "sunny"}`, } -// Or use convenience functions to create messages +// Or use convenience function to create message msg := schema.FunctionToolResultAgenticMessage( "call_abc123", "get_weather", @@ -585,7 +595,7 @@ msg := schema.FunctionToolResultAgenticMessage( #### ServerToolCall -ServerToolCall represents a call to a model server's built-in tool. Some model providers integrate specific tools on the server side (such as web search, code executor), which the model can call autonomously without user intervention. `Arguments` is the parameters for the model to call the server-side built-in tool, with specific definitions provided by the corresponding component implementations in **eino-ext**. +ServerToolCall represents the invocation of a model server-side built-in tool. Some model providers integrate specific tools (like web search, code executor) on the server side, and the model can autonomously call these tools without user intervention. `Arguments` are the parameters for the model to call the server-side built-in tool, with specific definitions provided by corresponding component implementations in **eino-ext**. - Definition @@ -617,7 +627,7 @@ type ServerToolCall struct { WebSearch: &WebSearchArguments{ ActionType: WebSearchActionSearch, Search: &WebSearchQuery{ - Query: "weather in Beijing today", + Query: "Beijing weather today", }, }, }, @@ -631,13 +641,13 @@ type ServerToolCall struct { "github.com/cloudwego/eino-ext/components/model/agenticopenai" ) - // Assert to specific implementation definition + // Assert to concrete implementation definition args := serverCall.Arguments.(*agenticopenai.ServerToolCallArguments) ``` #### ServerToolResult -ServerToolResult represents the execution result of a server-side built-in tool. After the model server executes the tool call, the result is returned through this type. `Result` is the result of the model calling the server-side built-in tool, with specific definitions provided by the corresponding component implementations in **eino-ext**. +ServerToolResult represents the execution result of a server-side built-in tool. After the model server executes a tool call, the result is returned through this type. `Result` is the result of the model calling the server-side built-in tool, with specific definitions provided by corresponding component implementations in **eino-ext**. - Definition @@ -683,7 +693,7 @@ type ServerToolResult struct { "github.com/cloudwego/eino-ext/components/model/agenticopenai" ) - // Assert to specific implementation definition + // Assert to concrete implementation definition args := serverResult.Result.(*agenticopenai.ServerToolResult) ``` @@ -725,7 +735,7 @@ mcpCall := &schema.MCPToolCall{ #### MCPToolResult -MCPToolResult represents the MCP tool execution result returned by the model. After the model autonomously completes the MCP tool call, the result or error information is returned through this type. +MCPToolResult represents the MCP tool execution result returned by the model. After the model autonomously completes an MCP tool call, the result or error information is returned through this type. - Definition @@ -759,7 +769,7 @@ type MCPToolCallError struct { - Example ```go -// MCP tool call success +// MCP tool call succeeded mcpResult := &schema.MCPToolResult{ ServerLabel: "database-server", CallID: "mcp_call_456", @@ -767,7 +777,7 @@ mcpResult := &schema.MCPToolResult{ Result: `{"rows": [...], "count": 10}`, } -// MCP tool call failure +// MCP tool call failed errorCode := int64(500) mcpError := &schema.MCPToolResult{ ServerLabel: "database-server", @@ -782,7 +792,7 @@ mcpError := &schema.MCPToolResult{ #### MCPListToolsResult -MCPListToolsResult represents the query result of available tools from an MCP server returned by the model. Models that support configuring MCP tools can autonomously send available tool list query requests to the MCP server, and the query results are returned through this type. +MCPListToolsResult represents the query result for available tools from an MCP server returned by the model. Models that support configuring MCP tools can autonomously query the MCP server for available tools, with the query result returned through this type. - Definition @@ -823,7 +833,7 @@ toolsList := &schema.MCPListToolsResult{ }, { Name: "create_table", - Description: "Create data table", + Description: "Create database table", InputSchema: &jsonschema.Schema{...}, }, }, @@ -832,7 +842,7 @@ toolsList := &schema.MCPListToolsResult{ #### MCPToolApprovalRequest -MCPToolApprovalRequest represents an MCP tool call request that requires user approval. In the model's autonomous MCP tool calling process, certain sensitive or high-risk operations (such as data deletion, external payments, etc.) require explicit user authorization before execution. Some models support configuring MCP tool call approval policies, and before each call to a high-risk MCP tool, the model returns a call authorization request through this type. +MCPToolApprovalRequest represents an MCP tool call request that requires user approval. In the model's autonomous MCP tool calling process, certain sensitive or high-risk operations (like data deletion, external payments) require explicit user authorization before execution. Some models support configuring MCP tool call approval policies, and before each high-risk MCP tool call, the model returns an authorization request through this type. - Definition @@ -865,7 +875,7 @@ approvalReq := &schema.MCPToolApprovalRequest{ #### MCPToolApprovalResponse -MCPToolApprovalResponse represents the user's approval decision for an MCP tool call. After receiving an MCPToolApprovalRequest, the user needs to review the operation details and make a decision. The user can choose to approve or reject the operation and optionally provide a reason for the decision. +MCPToolApprovalResponse represents the user's approval decision for an MCP tool call. After receiving an MCPToolApprovalRequest, the user needs to review the operation details and make a decision, choosing to approve or reject the operation, with an optional reason for the decision. - Definition @@ -895,7 +905,7 @@ approvalResp := &schema.MCPToolApprovalResponse{ #### StreamingMeta -StreamingMeta is used in streaming response scenarios to identify the position of a content block in the final response. During streaming generation, content may be returned in multiple blocks incrementally, and the index allows for correct assembly of the complete response. +StreamingMeta is used in streaming response scenarios to identify the position of a content block in the final response. During streaming generation, content may be returned in multiple chunks, and the index allows for correctly assembling the complete response. - Definition @@ -916,7 +926,7 @@ block := schema.NewContentBlockChunk(textGen, meta) ### Common Options -AgenticModel and ChatModel share a common set of Options for configuring model behavior. Additionally, AgenticModel provides some exclusive configuration options specific to itself. +AgenticModel and ChatModel share a common set of Options for configuring model behavior. Additionally, AgenticModel provides some exclusive configuration options. > Code location: [https://github.com/cloudwego/eino/tree/main/components/model/option.go](https://github.com/cloudwego/eino/tree/main/components/model/option.go) @@ -933,16 +943,16 @@ AgenticModel and ChatModel share a common set of Options for configuring model b AllowedToolsSupportedNot Supported -Accordingly, AgenticModel adds the following method for setting Options: +Correspondingly, AgenticModel has added the following method for setting Options: ```go // WithAgenticToolChoice is the option to set tool choice for the agentic model. func WithAgenticToolChoice(toolChoice schema.ToolChoice, allowedTools ...*schema.AllowedTool) Option {} ``` -#### Component Implementation Custom Options +#### Implementation-Specific Custom Options -The WrapImplSpecificOptFn method provides the ability for component implementations to inject custom Options. Developers need to define proprietary Option types in specific implementations and provide corresponding Option configuration methods. +The WrapImplSpecificOptFn method provides component implementations the ability to inject custom Options. Developers need to define implementation-specific Option types and provide corresponding Option configuration methods. ```go type openaiOptions struct { @@ -1101,9 +1111,9 @@ func main() { } ``` -## Options and Callbacks Usage +## Option and Callback Usage -### Options Usage +### Option Usage ```go import "github.com/cloudwego/eino/components/model" 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 30aae23c4e1..709e8f47505 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 @@ -3,18 +3,18 @@ Description: "" date: "2026-01-20" lastmod: "" tags: [] -title: 'Eino: ChatModel Guide' -weight: 1 +title: 'Eino: ChatModel User Guide' +weight: 8 --- -## Overview +## Introduction -The Model component is used to interact with large language models. Its main purpose 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 is a component for interacting with large language models. Its main purpose 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: -- Natural language conversations +- Natural language dialogue - Text generation and completion -- Tool call parameter generation -- Multimodal interactions (text, images, audio, etc.) +- Parameter generation for tool calls +- Multimodal interaction (text, images, audio, etc.) ## Component Definition @@ -42,8 +42,8 @@ type ToolCallingChatModel interface { - Function: Generate a complete model response - Parameters: - - ctx: Context object for passing request-level information, also used to pass the Callback Manager - - input: List of input messages + - ctx: Context object for passing request-level information and Callback Manager + - input: Input message list - opts: Optional parameters for configuring model behavior - Return values: - `*schema.Message`: The response message generated by the model @@ -52,7 +52,7 @@ type ToolCallingChatModel interface { #### Stream Method - Function: Generate model response in streaming mode -- Parameters: Same as the Generate method +- Parameters: Same as Generate method - Return values: - `*schema.StreamReader[*schema.Message]`: Stream reader for model response - error: Error information during generation @@ -72,7 +72,7 @@ type ToolCallingChatModel interface { ```go type Message struct { - // Role indicates the role of the message (system/user/assistant/tool) + // Role represents the message role (system/user/assistant/tool) Role RoleType // Content is the text content of the message Content string @@ -82,18 +82,18 @@ type Message struct { // UserInputMultiContent stores user input multimodal data, supporting text, images, audio, video, files // When using this field, the model role is restricted to User UserInputMultiContent []MessageInputPart - // AssistantGenMultiContent holds multimodal data output by the model, supporting text, images, audio, video + // AssistantGenMultiContent stores model output multimodal data, supporting text, images, audio, video // When using this field, the model role is restricted to Assistant AssistantGenMultiContent []MessageOutputPart // Name is the sender name of the message Name string // ToolCalls is the tool call information in assistant messages ToolCalls []ToolCall - // ToolCallID is the tool call ID for tool messages + // ToolCallID is the tool call ID of tool messages ToolCallID string // ResponseMeta contains response metadata ResponseMeta *ResponseMeta - // Extra is used to store additional information + // Extra stores additional information Extra map[string]any } ``` @@ -102,8 +102,8 @@ The Message struct is the basic structure for model interaction, supporting: - Multiple roles: system, user, assistant (ai), tool - Multimodal content: text, images, audio, video, files -- Tool calls: Support for model calling external tools and functions -- Metadata: Including response reason, token usage statistics, etc. +- Tool calls: supports model calling external tools and functions +- Metadata: includes response reason, token usage statistics, etc. ### Common Options @@ -121,12 +121,12 @@ type Options struct { Model *string // TopP controls the diversity of output TopP *float32 - // Stop specifies the conditions to stop generation + // Stop specifies conditions to stop generation Stop []string } ``` -Options can be set using the following methods: +Options can be set in the following ways: ```go // Set temperature @@ -183,7 +183,7 @@ import ( "github.com/cloudwego/eino/schema" ) -// Initialize model (using OpenAI as an example) +// Initialize model (using openai as example) cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ // Configuration parameters }) @@ -192,11 +192,11 @@ cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ messages := []*schema.Message{ { Role: schema.System, - Content: "你是一个有帮助的助手。", + Content: "You are a helpful assistant.", }, { Role: schema.User, - Content: "你好!", + Content: "Hello!", }, } @@ -282,7 +282,7 @@ handler := &callbacksHelper.ModelCallbackHandler{ return ctx }, OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *model.CallbackOutput) context.Context { - fmt.Printf("Generation complete, Token usage: %+v\n", output.TokenUsage) + fmt.Printf("Generation complete, token usage: %+v\n", output.TokenUsage) return ctx }, OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*model.CallbackOutput]) context.Context { @@ -336,22 +336,22 @@ result, err := runnable.Invoke(ctx, messages, compose.WithCallbacks(helper)) ## **Existing Implementations** -1. OpenAI ChatModel: Using OpenAI's GPT series models [ChatModel - OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai) -2. Ollama ChatModel: Using Ollama local models [ChatModel - Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) -3. ARK ChatModel: Using ARK platform model services [ChatModel - ARK](/docs/eino/ecosystem_integration/chat_model/chat_model_ark) +1. OpenAI ChatModel: Using OpenAI's GPT series models [ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) +2. Ollama ChatModel: Using Ollama local models [ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) +3. ARK ChatModel: Using ARK platform model services [ChatModel - ARK](https://bytedance.larkoffice.com/wiki/WUzzwaX8ricGwZk1i1mcJHHNnEl) 4. More: [Eino ChatModel](https://www.cloudwego.io/docs/eino/ecosystem_integration/chat_model/) -## Custom Implementation Reference +## Implementation Reference -When implementing a custom ChatModel component, pay attention to the following points: +When implementing a custom ChatModel component, note the following: 1. Make sure to implement common options -2. Make sure to implement the callback mechanism -3. Remember to close the writer after streaming output is complete +2. Make sure to implement callback mechanism +3. Remember to close the writer after completing output in streaming mode ### Option Mechanism -If a custom ChatModel needs Options beyond the common Options, you can use the component abstraction utility functions to implement custom Options, for example: +Custom ChatModel can use the component abstraction utility function to implement custom Options if Options beyond common Options are needed, for example: ```go import ( @@ -445,7 +445,7 @@ func NewMyChatModel(config *MyChatModelConfig) (*MyChatModel, error) { } func (m *MyChatModel) Generate(ctx context.Context, messages []*schema.Message, opts ...model.Option) (*schema.Message, error) { - // 1. Process options + // 1. Handle options options := &MyChatModelOptions{ Options: &model.Options{ Model: &m.model, @@ -456,7 +456,7 @@ func (m *MyChatModel) Generate(ctx context.Context, messages []*schema.Message, options.Options = model.GetCommonOptions(options.Options, opts...) options = model.GetImplSpecificOptions(options, opts...) - // 2. Callback before starting generation + // 2. Callback before generation starts ctx = callbacks.OnStart(ctx, &model.CallbackInput{ Messages: messages, Config: &model.Config{ @@ -467,7 +467,7 @@ func (m *MyChatModel) Generate(ctx context.Context, messages []*schema.Message, // 3. Execute generation logic response, err := m.doGenerate(ctx, messages, options) - // 4. Handle error and completion callbacks + // 4. Handle errors and completion callback if err != nil { ctx = callbacks.OnError(ctx, err) return nil, err @@ -481,7 +481,7 @@ func (m *MyChatModel) Generate(ctx context.Context, messages []*schema.Message, } func (m *MyChatModel) Stream(ctx context.Context, messages []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { - // 1. Process options + // 1. Handle options options := &MyChatModelOptions{ Options: &model.Options{ Model: &m.model, @@ -492,7 +492,7 @@ func (m *MyChatModel) Stream(ctx context.Context, messages []*schema.Message, op options.Options = model.GetCommonOptions(options.Options, opts...) options = model.GetImplSpecificOptions(options, opts...) - // 2. Callback before starting streaming generation + // 2. Callback before streaming generation starts ctx = callbacks.OnStart(ctx, &model.CallbackInput{ Messages: messages, Config: &model.Config{ @@ -501,18 +501,18 @@ func (m *MyChatModel) Stream(ctx context.Context, messages []*schema.Message, op }) // 3. Create streaming response - // Pipe produces a StreamReader and a StreamWriter; writing to StreamWriter can be read from StreamReader, both are concurrency-safe. - // The implementation asynchronously writes generated content to StreamWriter and returns StreamReader as the return value - // ***StreamReader is a data stream that can only be read once. When implementing Callback yourself, you need to pass the data stream to callback via OnEndWithCallbackOutput and also return a data stream, requiring a copy of the data stream - // Considering this scenario always requires copying the data stream, the OnEndWithStreamOutput function will copy internally and return an unread stream - // The following code demonstrates one stream processing approach; the processing method is not unique + // Pipe produces a StreamReader and StreamWriter. Writing to StreamWriter can be read from StreamReader, both are concurrency-safe. + // In implementation, write generated content to StreamWriter asynchronously and return StreamReader as return value + // ***StreamReader is a data stream that can only be read once. When implementing Callback yourself, you need to pass the data stream to callback via OnEndWithCallbackOutput and also return a data stream, requiring copying the data stream. + // Considering this scenario always requires copying the data stream, the OnEndWithCallbackOutput function copies internally and returns an unread stream. + // The following code demonstrates one stream handling approach; handling approaches are not unique. sr, sw := schema.Pipe[*model.CallbackOutput](1) - // 4. Start asynchronous generation + // 4. Start async generation go func() { defer sw.Close() - // Stream writing + // Stream write m.doStream(ctx, messages, options, sw) }() 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 5c793b1580b..1549638ea12 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 @@ -3,23 +3,23 @@ Description: "" date: "2025-11-20" lastmod: "" tags: [] -title: 'Eino: ChatTemplate Guide' -weight: 2 +title: 'Eino: ChatTemplate User Guide' +weight: 7 --- -## Introduction +## **Introduction** -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: +The Prompt component is a component for processing and formatting prompt templates. Its main purpose is to fill user-provided variable values into predefined message templates to generate standard message formats for interacting with language models. This component can be used in the following scenarios: -- Structured system prompts -- Multi-turn dialogue templates (including history) -- Reusable prompt patterns +- Building structured system prompts +- Handling multi-turn dialogue templates (including history) +- Implementing reusable prompt patterns -## Component Definition +## **Component Definition** -### Interface +### **Interface Definition** -> Code: `eino/components/prompt/interface.go` +> Code location: eino/components/prompt/interface.go ```go type ChatTemplate interface { @@ -27,52 +27,62 @@ type ChatTemplate interface { } ``` -#### Format +#### **Format Method** -- 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` +- Function: Fill variable values into message templates +- Parameters: + - ctx: Context object for passing request-level information and Callback Manager + - vs: Variable value mapping for filling template placeholders + - opts: Optional parameters for configuring formatting behavior +- Return values: + - `[]*schema.Message`: Formatted message list + - error: Error information during formatting -### Built-in Templating +### **Built-in Templating Methods** -Prompt supports three built-in templating modes: +The Prompt component has built-in support for three templating methods: -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. +1. FString format (schema.FString) + + - Uses `{variable}` syntax for variable substitution + - 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 text/template syntax + - Supports conditional logic, loops, and other complex logic - Example: `"{{if .expert}}As an expert{{end}} please {{.action}}"` -3. `Jinja2` (`schema.Jinja2`) - - Jinja2 template syntax +3. Jinja2 format (schema.Jinja2) + + - Uses Jinja2 template syntax - Example: `"{% if level == 'expert' %}From an expert perspective{% endif %} analyze {{topic}}"` -### Options +### **Common Options** -Prompt includes an `Option` mechanism; there’s no global option abstraction. Each implementation may define its own specific options and wrap them via `WrapImplSpecificOptFn`. +The Prompt component uses Option to define optional parameters. ChatTemplate has no common option abstraction. Each specific implementation can define its own specific Options, wrapped into a unified Option type through the WrapImplSpecificOptFn function. -## Usage +## **Usage** -`ChatTemplate` is typically used before `ChatModel` to prepare context. +ChatTemplate is generally used before ChatModel for context preparation. ### Creation Methods -- `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 +- `prompt.FromMessages()` + - Used to turn multiple messages into a chat 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 role "system" +- `schema.AssistantMessage()` + - This method is a shortcut for constructing a message with role "assistant" +- `schema.UserMessage()` + - This method is a shortcut for constructing a message with role "user" +- `schema.ToolMessage()` + - This method is a shortcut for constructing a message with role "tool" +- `schema.MessagesPlaceholder()` + - Can be used to insert a `[]*schema.Message` into the message list, commonly used for inserting conversation history + +### **Standalone Usage** ```go import ( @@ -82,23 +92,26 @@ import ( // Create template template := prompt.FromMessages(schema.FString, - schema.SystemMessage("你是一个{role}。"), + schema.SystemMessage("You are a {role}."), schema.MessagesPlaceholder("history_key", false), - &schema.Message{ Role: schema.User, Content: "请帮我{task}。" }, + &schema.Message{ + Role: schema.User, + Content: "Please help me {task}.", + }, ) -// Variables +// Prepare variables variables := map[string]any{ - "role": "专业的助手", - "task": "写一首诗", - "history_key": []*schema.Message{{Role: schema.User, Content: "告诉我油画是什么?"}, {Role: schema.Assistant, Content: "油画是xxx"}}, + "role": "professional assistant", + "task": "write a poem", + "history_key": []*schema.Message{{Role: schema.User, Content: "Tell me what is oil painting?"}, {Role: schema.Assistant, Content: "Oil painting is xxx"}}, } -// Format +// Format template messages, err := template.Format(context.Background(), variables) ``` -### In Orchestration +### **Usage in Orchestration** ```go import ( @@ -123,22 +136,24 @@ graph := compose.NewGraph[map[string]any, []*schema.Message]() graph.AddChatTemplateNode("template_node", template) ``` -### Pull Data from a Predecessor Node +### Getting Data from Predecessor Node Output -Use `WithOutputKey` to map a node’s output into a keyed `map[string]any`: +When using AddNode, you can add the WithOutputKey Option to convert the node's output to a Map: ```go +// This node's output will be changed from string to map[string]any, +// and the map will have only one element with key "your_output_key" and value being the actual string output from 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")) ``` -Then refer to that key within a downstream `ChatTemplate` node. +After converting the predecessor node's output to map[string]any and setting the key, use the value corresponding to that key in the subsequent ChatTemplate node. -## Options and Callbacks +## **Option and Callback Usage** -### Callback Example +### **Callback Usage Example** ```go import ( @@ -150,55 +165,70 @@ 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("Formatting template; variables: %v\n", input.Variables) + fmt.Printf("Starting template formatting, variables: %v\n", input.Variables) return ctx }, OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *prompt.CallbackOutput) context.Context { - fmt.Printf("Template formatted; messages: %d\n", len(output.Result)) + fmt.Printf("Template formatting complete, generated message count: %d\n", len(output.Result)) return ctx }, } +// Use callback handler 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)) ``` -## Implementation Notes +## **Implementation Reference** -### Option Mechanism +### Option **Mechanism** -If needed, define custom prompt options: +If needed, component implementers can implement 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 Structures +### **Callback Handling** + +Prompt implementations need to trigger callbacks at appropriate times. The following structures are defined by the component: -> Code: `eino/components/prompt/callback_extra.go` +> Code location: eino/components/prompt/callback_extra.go ```go +// Define callback input and output type CallbackInput struct { Variables map[string]any Templates []schema.MessagesTemplate @@ -212,7 +242,7 @@ type CallbackOutput struct { } ``` -### Complete Implementation Example +### **Complete Implementation Example** ```go type MyPrompt struct { @@ -232,30 +262,41 @@ 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 Options 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 your own defined 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 a997debf945..9302eef06c9 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 @@ -3,22 +3,22 @@ Description: "" date: "2025-11-20" lastmod: "" tags: [] -title: 'Eino: Document Loader Guide' -weight: 8 +title: 'Eino: Document Loader User Guide' +weight: 1 --- -## Introduction +## **Introduction** -`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: +Document Loader is a component for loading documents. Its main purpose is to load document content from different sources (such as network URLs, local files, etc.) and convert them into a standard document format. This component plays an important role in scenarios that require obtaining document content from various sources, such as: -- Loading web content from URLs -- Reading local documents like PDF or Word +- Loading web content from network URLs +- Reading local PDF, Word, and other format documents -## Component Definition +## **Component Definition** -### Interface +### **Interface Definition** -> Code: `eino/components/document/interface.go` +> Code location: eino/components/document/interface.go ```go type Loader interface { @@ -26,18 +26,18 @@ type Loader interface { } ``` -#### Load +#### **Load Method** -- 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`: loaded documents - - `error` +- Function: Load documents from specified data sources +- Parameters: + - ctx: Context object for passing request-level information and Callback Manager + - src: Document source containing URI information of the document + - opts: Loading options for configuring loading behavior +- Return values: + - `[]*schema.Document`: Loaded document list + - error: Error information during loading -### Source +### **Source Struct** ```go type Source struct { @@ -45,35 +45,43 @@ type Source struct { } ``` -Defines source information: +The Source struct defines the document source information: -- `URI`: a web URL or local file path +- URI: Uniform Resource Identifier of the document, can be a network URL or local file path -### Document +### **Document Struct** ```go type Document struct { + // ID is the unique identifier of the document ID string + // Content is the content of the document Content string + // MetaData stores metadata information of the document MetaData map[string]any } ``` -Standard document fields: +The Document struct is the standard format for documents, containing the following important fields: -- `ID`: unique identifier -- `Content`: document content -- `MetaData`: source info, embeddings, scores, sub-indexes, and custom metadata +- ID: Unique identifier of the document, used to uniquely identify a document in the system +- Content: Actual content of the document +- MetaData: Metadata of the document, can store information such as: + - Source information of the document + - Vector representation of the document (for vector retrieval) + - Document score (for ranking) + - Document sub-index (for hierarchical retrieval) + - Other custom metadata -### Options +### **Common Options** -`LoaderOption` represents loader options. There is no global common option; each implementation defines its own and wraps via `WrapLoaderImplSpecificOptFn`. +The Loader component uses `LoaderOption` to define loading options. Loader currently has no common Options. Each specific implementation can define its own specific options, wrapped into a unified `LoaderOption` type through the `WrapLoaderImplSpecificOptFn` function. -## Usage +## **Usage** -### Standalone +### **Standalone Usage** -> Code: `eino-ext/components/document/loader/file/examples/fileloader` +> Code location: eino-ext/components/document/loader/file/examples/fileloader ```go import ( @@ -81,32 +89,43 @@ import ( "github.com/cloudwego/eino-ext/components/document/loader/file" ) -loader, _ := file.NewFileLoader(ctx, &file.FileLoaderConfig{ UseNameAsID: true }) +// Initialize loader (using file loader as example) +loader, _ := file.NewFileLoader(ctx, &file.FileLoaderConfig{ + // Configuration parameters + 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) ``` -### In Orchestration +### **Usage in Orchestration** ```go -// Chain +// Use in Chain chain := compose.NewChain[string, []*schema.Document]() chain.AppendLoader(loader) + +// Compile and run runnable, _ := chain.Compile() + result, _ := runnable.Invoke(ctx, input) -// Graph +// Use in Graph graph := compose.NewGraph[string, []*schema.Document]() graph.AddLoaderNode("loader_node", loader) ``` -## Options and Callbacks +## **Option and Callback Usage** -### Callback Example +### **Callback Usage Example** -> Code: `eino-ext/components/document/loader/file/examples/fileloader` +> Code location: eino-ext/components/document/loader/file/examples/fileloader ```go import ( @@ -115,60 +134,83 @@ 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: %d\n", len(output.Docs)) + log.Printf("complete loading docs, total loaded docs: %d\n", len(output.Docs)) return ctx }, + // OnError } -helper := callbacksHelper.NewHandlerHelper().Loader(handler).Handler() +// Use callback 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: For loading documents from the local file system [Loader - local file](/docs/eino/ecosystem_integration/document/loader_local_file) +2. Web Loader: For loading documents pointed to by network URLs [Loader - web url](/docs/eino/ecosystem_integration/document/loader_web_url) +3. S3 Loader: For loading 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) +## **Implementation Reference** -## Implementation Notes +When implementing your own loader component, pay attention to the option mechanism and callback handling. -### Option Mechanism +### Option **Mechanism** + +Custom Loader needs to implement its own Option parameter mechanism: ```go +// Define option 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 Structures +### **Callback Handling** -> Code: `eino/components/document/callback_extra_loader.go` +Loader implementations need to trigger callbacks at appropriate times: + +> Code location: eino/components/document/callback_extra_loader.go ```go +// This is the callback input/output defined by the loader component. Implementations need to satisfy the meaning of the parameters. type LoaderCallbackInput struct { Source Source Extra map[string]any @@ -181,7 +223,7 @@ type LoaderCallbackOutput struct { } ``` -### Full Implementation Example +### **Complete Implementation Example** ```go import ( @@ -208,6 +250,7 @@ type Config struct { } func (l *CustomLoader) Load(ctx context.Context, src document.Source, opts ...document.LoaderOption) ([]*schema.Document, error) { + // 1. Handle options options := &customLoaderOptions{ Timeout: l.timeout, RetryCount: l.retryCount, @@ -215,17 +258,21 @@ func (l *CustomLoader) Load(ctx context.Context, src document.Source, opts ...do options = document.GetLoaderImplSpecificOptions(options, opts...) var err error + // 2. Handle errors and call error callback method defer func() { if err != nil { callbacks.OnError(ctx, err) } }() + // 3. Callback before loading starts ctx = callbacks.OnStart(ctx, &document.LoaderCallbackInput{ Source: src, }) + // 4. Execute loading logic docs, err := l.doLoad(ctx, src, options) + if err != nil { return nil, err } @@ -239,20 +286,23 @@ 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 loading logic + // 1. Load document content + // 2. Construct Document object, note that important information like document source can be saved in MetaData return []*schema.Document{{ Content: "Hello World", }}, nil } ``` -### Notes +### **Notes** -- `MetaData` is critical for storing document source and other metadata -- Return meaningful errors on load failures for easier debugging +- MetaData is an important part of the document, used to save various metadata information of the document +- Return meaningful error information when document loading fails, making error troubleshooting easier -## Other References +## Other Reference Documents -- [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 User Guide](/docs/eino/core_modules/components/document_transformer_guide) +- [[🚧]Eino: Embedding User Guide](/docs/eino/core_modules/components/embedding_guide) +- [[🚧]Eino: Indexer User Guide](/docs/eino/core_modules/components/indexer_guide) +- [[🚧]Eino: Retriever User Guide](/docs/eino/core_modules/components/retriever_guide) 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 fd9a61d7a68..080ba8e7c81 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 @@ -3,24 +3,24 @@ Description: "" date: "2025-07-21" lastmod: "" tags: [] -title: 'Eino: Document Transformer Guide' -weight: 9 +title: 'Eino: Document Transformer User Guide' +weight: 3 --- -## **Overview** +## **Introduction** -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: +Document Transformer is a component for document conversion and processing. Its main purpose is to perform various transformation operations on input documents, such as splitting, filtering, merging, etc., to obtain documents that meet specific requirements. This component can be used in the following scenarios: -- Split long documents into smaller chunks for processing -- Filter document content by rules -- Convert document structure -- Extract specific parts of a document +- Splitting long documents into small paragraphs for easier processing +- Filtering document content based on specific rules +- Performing structural transformations on document content +- Extracting specific parts of documents ## **Component Definition** -### **Interface** +### **Interface Definition** -> Code: `eino/components/document/interface.go` +> Code location: eino/components/document/interface.go ```go type Transformer interface { @@ -30,48 +30,48 @@ type Transformer interface { #### **Transform Method** -- 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 +- Function: Perform transformation processing on input documents +- Parameters: + - ctx: Context object for passing request-level information and Callback Manager + - src: List of documents to be processed + - opts: Optional parameters for configuring transformation behavior +- Return values: + - `[]*schema.Document`: Transformed document list + - error: Error information during transformation ### **Document Struct** ```go type Document struct { - // ID is the unique identifier - ID string - // Content is the document text + // ID is the unique identifier of the document + ID string + // Content is the content of the document Content string - // MetaData stores metadata + // MetaData stores metadata information of the document MetaData map[string]any } ``` -Key fields: +The Document struct is the standard format for documents, containing the following important fields: -- 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 +- ID: Unique identifier of the document, used to uniquely identify a document in the system +- Content: Actual content of the document +- MetaData: Metadata of the document, can store information such as: + - Source information of the document + - Vector representation of the document (for vector retrieval) + - Document score (for ranking) + - Document sub-index (for hierarchical retrieval) + - Other custom metadata ### **Common Options** -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`. +The Transformer component uses TransformerOption to define optional parameters. There are currently no common options. Each specific implementation can define its own specific Options, wrapped into a unified TransformerOption type through the WrapTransformerImplSpecificOptFn function. ## **Usage** -### **Standalone** +### **Standalone Usage** -> Code: `eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter` +> Code location: eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter ```go import ( @@ -79,8 +79,9 @@ import ( "github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown" ) -// init transformer (markdown example) +// Initialize transformer (using markdown as example) transformer, _ := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ + // Configuration parameters Headers: map[string]string{ "##": "", }, @@ -89,7 +90,7 @@ transformer, _ := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ markdownDoc := &schema.Document{ Content: "## Title 1\nHello Word\n## Title 2\nWord Hello", } -// transform +// Transform document transformedDocs, _ := transformer.Transform(ctx, []*schema.Document{markdownDoc}) for idx, doc := range transformedDocs { @@ -97,23 +98,23 @@ for idx, doc := range transformedDocs { } ``` -### **In Orchestration** +### **Usage in Orchestration** ```go -// in Chain +// Use in Chain chain := compose.NewChain[[]*schema.Document, []*schema.Document]() chain.AppendDocumentTransformer(transformer) -// in Graph +// Use in Graph graph := compose.NewGraph[[]*schema.Document, []*schema.Document]() graph.AddDocumentTransformerNode("transformer_node", transformer) ``` -## **Options and Callbacks** +## **Option and Callback Usage** -### **Callback Example** +### **Callback Usage Example** -> Code: `eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter` +> Code location: eino-ext/components/document/transformer/splitter/markdown/examples/headersplitter ```go import ( @@ -126,6 +127,7 @@ 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) @@ -135,8 +137,10 @@ 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() @@ -144,6 +148,7 @@ 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)) @@ -155,26 +160,30 @@ for idx, doc := range outDocs { ## **Existing Implementations** -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) +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: Filter document content based on rules [Splitter - recursive](/docs/eino/ecosystem_integration/document/splitter_recursive) -## **Implement Your Own** +## **Implementation Reference** -Consider the following when implementing a custom Transformer: +When implementing a custom Transformer component, note the following: 1. Option handling 2. Callback handling ### **Option Mechanism** +Custom Transformer needs to implement its own Option mechanism: + ```go +// Define Option struct type MyTransformerOptions struct { ChunkSize int Overlap int MinChunkLength int } +// Define Option functions func WithChunkSize(size int) document.TransformerOption { return document.WrapTransformerImplSpecificOptFn(func(o *MyTransformerOptions) { o.ChunkSize = size @@ -190,7 +199,10 @@ func WithOverlap(overlap int) document.TransformerOption { ### **Callback Handling** +Transformer implementations need to trigger callbacks at appropriate times: + ```go +// This is the callback input/output defined by transformer. Custom components need to satisfy the meaning of the structures when implementing. type TransformerCallbackInput struct { Input []*schema.Document Extra map[string]any @@ -202,7 +214,7 @@ type TransformerCallbackOutput struct { } ``` -### **Full Implementation Example** +### **Complete Implementation Example** ```go type MyTransformer struct { @@ -220,7 +232,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 Option + // 1. Handle Options options := &MyTransformerOptions{ ChunkSize: t.chunkSize, Overlap: t.overlap, @@ -228,15 +240,15 @@ func (t *MyTransformer) Transform(ctx context.Context, src []*schema.Document, o } options = document.GetTransformerImplSpecificOptions(options, opts...) - // 2. before-transform callback + // 2. Callback before transformation starts ctx = callbacks.OnStart(ctx, info, &document.TransformerCallbackInput{ Input: src, }) - // 3. perform transform + // 3. Execute transformation logic docs, err := t.doTransform(ctx, src, options) - // 4. handle error and finish callback + // 4. Handle errors and completion callback if err != nil { ctx = callbacks.OnError(ctx, info, err) return nil, err @@ -248,22 +260,20 @@ 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 document transform logic + // Implement document transformation logic return docs, nil } ``` ### **Notes** -- Preserve and manage metadata when transforming documents; keep original metadata and add custom metadata as needed. +- Pay attention to metadata handling for transformed documents, preserving original metadata and adding custom metadata -## **References** +## Other Reference Documents -- [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) +- [[🚧]Eino: Embedding User Guide](/docs/eino/core_modules/components/embedding_guide) +- [[🚧]Eino: Indexer User Guide](/docs/eino/core_modules/components/indexer_guide) +- [[🚧]Eino: Retriever User Guide](/docs/eino/core_modules/components/retriever_guide) +- [[🚧]Eino: Document Loader User 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 1a921998341..b0cf613360d 100644 --- a/content/en/docs/eino/core_modules/components/embedding_guide.md +++ b/content/en/docs/eino/core_modules/components/embedding_guide.md @@ -3,21 +3,21 @@ Description: "" date: "2025-07-21" lastmod: "" tags: [] -title: 'Eino: Embedding Guide' -weight: 7 +title: 'Eino: Embedding User Guide' +weight: 2 --- -## Introduction +## **Introduction** -The `Embedding` component converts text into vector representations, mapping content into a vector space where semantically similar texts are closer. It’s useful for: +The Embedding component is a component for converting text into vector representations. Its main purpose is to map text content into vector space, so that semantically similar texts are closer in vector space. This component plays an important role in the following scenarios: -- Text similarity +- Text similarity calculation - Semantic search -- Clustering analysis +- Text clustering analysis -## Component Definition +## **Component Definition** -### Interface +### **Interface Definition** ```go type Embedder interface { @@ -25,38 +25,40 @@ type Embedder interface { } ``` -#### EmbedStrings +#### **EmbedStrings Method** -- 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` +- Function: Convert a set of texts into vector representations +- Parameters: + - ctx: Context object for passing request-level information and Callback Manager + - texts: List of texts to be converted + - opts: Conversion options for configuring conversion behavior +- Return values: + - `[][]float64`: List of vector representations for the texts, the dimension of each vector is determined by the specific implementation + - error: Error information during conversion -### Common Options +### **Common Options** -Embedding uses `EmbeddingOption`. Implementations may define specific options and wrap via `WrapEmbeddingImplSpecificOptFn`. +The Embedding component uses EmbeddingOption to define optional parameters. Below are the abstracted common options. Each specific implementation can define its own specific Options, wrapped into a unified EmbeddingOption type through the WrapEmbeddingImplSpecificOptFn function. ```go type Options struct { + // Model is the model name used for generating vectors Model *string } ``` -Set options: +Options can be set in the following ways: ```go +// Set model name WithModel(model string) Option ``` -## Usage +## **Usage** -### Standalone +### **Standalone Usage** -> Code: `eino-ext/components/embedding/openai/examples/embedding` +> Code location: eino-ext/components/embedding/openai/examples/embedding ```go import "github.com/cloudwego/eino-ext/components/embedding/openai" @@ -71,33 +73,34 @@ embedder, _ := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{ vectorIDs, _ := embedder.EmbedStrings(ctx, []string{"hello", "how are you"}) ``` -### In Orchestration +### **Usage in Orchestration** -> Code: `eino-ext/components/embedding/openai/examples/embedding` +> Code location: eino-ext/components/embedding/openai/examples/embedding ```go -// Chain +// Use in Chain chain := compose.NewChain[[]string, [][]float64]() chain.AppendEmbedding(embedder) -// Graph +// Use in Graph graph := compose.NewGraph[[]string, [][]float64]() graph.AddEmbeddingNode("embedding_node", embedder) ``` -## Options and Callbacks +## **Option and Callback Usage** -### Options Example +### **Option Usage Example** ```go +// Using options (standalone component usage example) vectors, err := embedder.EmbedStrings(ctx, texts, embedding.WithModel("text-embedding-3-small"), ) ``` -### Callback Example +### **Callback Usage Example** -> Code: `eino-ext/components/embedding/openai/examples/embedding` +> Code location: eino-ext/components/embedding/openai/examples/embedding ```go import ( @@ -123,59 +126,86 @@ 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 +## **Existing Implementations** + +1. OpenAI Embedding: Generate vectors using OpenAI's text embedding models [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai) +2. ARK Embedding: Generate vectors using ARK platform models [Embedding - ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark) -1. OpenAI Embedding: [Embedding — OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai) -2. ARK Embedding: [Embedding — ARK](/docs/eino/ecosystem_integration/embedding/embedding_ark) +## **Implementation Reference** -## Implementation Notes +When implementing a custom Embedding component, note the following: -1. Handle common options -2. Implement callback mechanisms +1. Make sure to handle common options +2. Make sure to implement callback mechanism -### Options +### **Option Mechanism** + +Custom Embedding needs to implement its own Option mechanism: ```go +// Define Option struct type MyEmbeddingOptions struct { BatchSize int MaxRetries int Timeout time.Duration } +// Define Option functions 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 Structures +### **Callback Handling** + +Embedder implementations need to trigger callbacks at appropriate times. The framework has defined standard callback input/output structs: ```go +// CallbackInput is the input for embedding callback type CallbackInput struct { + // Texts is the list of texts to be converted Texts []string + // Config is the configuration for generating vectors Config *Config + // Extra is additional information for the callback Extra map[string]any } +// CallbackOutput is the output for embedding callback type CallbackOutput struct { + // Embeddings is the list of generated vectors Embeddings [][]float64 + // Config is the configuration 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 } ``` -### Full Implementation Example +### **Complete Implementation Example** ```go type MyEmbedder struct { @@ -191,7 +221,7 @@ func NewMyEmbedder(config *MyEmbedderConfig) (*MyEmbedder, error) { } func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...embedding.Option) ([][]float64, error) { - // 1. handle options + // 1. Handle options options := &MyEmbeddingOptions{ Options: &embedding.Options{}, BatchSize: e.batchSize, @@ -199,10 +229,10 @@ func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...e options.Options = embedding.GetCommonOptions(options.Options, opts...) options = embedding.GetImplSpecificOptions(options.Options, opts...) - // 2. get callback manager + // 2. Get callback manager cm := callbacks.ManagerFromContext(ctx) - // 3. before-embed callback + // 3. Callback before generation starts ctx = cm.OnStart(ctx, info, &embedding.CallbackInput{ Texts: texts, Config: &embedding.Config{ @@ -210,10 +240,10 @@ func (e *MyEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...e }, }) - // 4. perform embedding + // 4. Execute vector generation logic vectors, tokenUsage, err := e.doEmbed(ctx, texts, options) - // 5. handle error and finish callback + // 5. Handle errors and completion callback if err != nil { ctx = cm.OnError(ctx, info, err) return nil, err @@ -231,13 +261,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) { - // implement logic + // Implement logic return vectors, tokenUsage, nil } ``` -## References +## Other Reference Documents -- [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 User Guide](/docs/eino/core_modules/components/document_loader_guide) +- [Eino: Indexer User Guide](/docs/eino/core_modules/components/indexer_guide) +- [Eino: Retriever User 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 03d7432933d..08faa057797 100644 --- a/content/en/docs/eino/core_modules/components/indexer_guide.md +++ b/content/en/docs/eino/core_modules/components/indexer_guide.md @@ -3,15 +3,15 @@ Description: "" date: "2026-01-20" lastmod: "" tags: [] -title: 'Eino: Indexer Guide' -weight: 6 +title: 'Eino: Indexer User Guide' +weight: 5 --- ## **Introduction** -The Indexer component is used to store and index documents. Its main purpose is to store documents and their vector representations into backend storage systems and provide efficient retrieval capabilities. This component plays an important role in the following scenarios: +The Indexer component is a component for storing and indexing documents. Its main purpose is to store documents and their vector representations in backend storage systems and provide efficient retrieval capabilities. This component plays an important role in the following scenarios: -- Building vector databases for semantic search +- Building vector databases for semantic association search ## **Component Definition** @@ -27,12 +27,12 @@ type Indexer interface { #### **Store Method** -- Purpose: Store documents and build indexes +- Function: Store documents and build indexes - Parameters: - - ctx: Context object for passing request-level information and the Callback Manager - - docs: List of documents to store + - ctx: Context object for passing request-level information and Callback Manager + - docs: List of documents to be stored - opts: Storage options for configuring storage behavior -- Returns: +- Return values: - ids: List of successfully stored document IDs - error: Error information during storage @@ -42,14 +42,14 @@ The Indexer component uses IndexerOption to define optional parameters. Indexer ```go type Options struct { - // SubIndexes is the list of sub-indexes to build + // SubIndexes is the list of sub-indexes to be built SubIndexes []string // Embedding is the component used to generate document vectors Embedding embedding.Embedder } ``` -Options can be set as follows: +Options can be set in the following ways: ```go // Set sub-indexes @@ -73,18 +73,18 @@ import ( collectionName := "eino_test" /* - * In the following example, a dataset (collection) named eino_test is pre-created with fields: + * The example below pre-built a dataset (collection) named eino_test with the following field configuration: * Field Name Field Type Vector Dimension - * ID string - * vector vector 1024 + * ID string + * vector vector 1024 * sparse_vector sparse_vector - * content string + * content string * extra_field_1 string * - * 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 + * Notes when using the component: + * 1. Field names and types for ID / vector / sparse_vector / content should match the configuration above + * 2. Vector dimension should match the output dimension of the model corresponding to ModelName + * 3. Some models don't output sparse vectors, in which case UseSparse should be set to false, and the collection may not have a sparse_vector field */ cfg := &volc_vikingdb.IndexerConfig{ @@ -244,14 +244,14 @@ docs := []*schema.Document{ ids, err := indexer.Store(ctx, docs) ``` -### **Using in Orchestration** +### **Usage in Orchestration** ```go -// Using in Chain +// Use in Chain chain := compose.NewChain[[]*schema.Document, []string]() chain.AppendIndexer(indexer) -// Using in Graph +// Use in Graph graph := compose.NewGraph[[]*schema.Document, []string]() graph.AddIndexerNode("indexer_node", indexer) ``` @@ -302,7 +302,7 @@ handler := &callbacksHelper.IndexerCallbackHandler{ // OnError } -// Using callback handler +// Use callback handler helper := callbacksHelper.NewHandlerHelper(). Indexer(handler). Handler() @@ -310,7 +310,7 @@ helper := callbacksHelper.NewHandlerHelper(). chain := compose.NewChain[[]*schema.Document, []string]() chain.AppendIndexer(volcIndexer) -// Using at runtime +// Use at runtime run, _ := chain.Compile(ctx) outIDs, _ := run.Invoke(ctx, docs, compose.WithCallbacks(helper)) @@ -320,20 +320,20 @@ fmt.Printf("vikingDB store success, docs=%v, resp ids=%v\n", docs, outIDs) ## **Existing Implementations** -- Volc VikingDB Indexer: Vector database indexer based on Volcano Engine VikingDB [Indexer - VikingDB](/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb) +- Volc VikingDB Indexer: Vector database indexer based on Volcengine VikingDB [Indexer - VikingDB](/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb) - Milvus v2.5+ Indexer: Vector database indexer based on Milvus [Indexer - Milvus 2 (v2.5+)](/docs/eino/ecosystem_integration/indexer/indexer_milvusv2) - Milvus v2.4- Indexer: Vector database indexer based on Milvus [Indexer - Milvus (v2.4-)](/docs/eino/ecosystem_integration/indexer/indexer_milvus) - Elasticsearch 8 Indexer: General search engine indexer based on ES8 [Indexer - ElasticSearch 8](/docs/eino/ecosystem_integration/indexer/indexer_es8) -- ElasticSearch 7 Indexer: General search engine indexer based on ES7 [Indexer - Elasticsearch 7](/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7) +- ElasticSearch 7 Indexer: General search engine indexer based on ES7 [Indexer - Elasticsearch 7 ](/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7) - OpenSearch 3 Indexer: General search engine indexer based on OpenSearch 3 [Indexer - OpenSearch 3](/docs/eino/ecosystem_integration/indexer/indexer_opensearch3) - OpenSearch 2 Indexer: General search engine indexer based on OpenSearch 2 [Indexer - OpenSearch 2](/docs/eino/ecosystem_integration/indexer/indexer_opensearch2) -## **Custom Implementation Reference** +## **Implementation Reference** When implementing a custom Indexer component, note the following: -1. Handle common options and implementation-specific options properly -2. Handle callbacks properly +1. Pay attention to handling common options as well as implementation-level option handling +2. Pay attention to callback handling ### **Option Mechanism** @@ -346,7 +346,7 @@ type MyIndexerOptions struct { MaxRetries int } -// Define Option function +// Define Option functions func WithBatchSize(size int) indexer.Option { return indexer.WrapIndexerImplSpecificOptFn(func(o *MyIndexerOptions) { o.BatchSize = size @@ -356,7 +356,7 @@ func WithBatchSize(size int) indexer.Option { ### **Callback Handling** -Indexer implementations need to trigger callbacks at appropriate times. The framework has defined standard callback input/output structures: +Indexer implementations need to trigger callbacks at appropriate times. The framework has defined standard callback input/output structs: ```go // CallbackInput is the input for indexer callback @@ -399,7 +399,7 @@ func (i *MyIndexer) Store(ctx context.Context, docs []*schema.Document, opts ... // 2. Get callback manager cm := callbacks.ManagerFromContext(ctx) - // 3. Callback before storage + // 3. Callback before storage starts ctx = cm.OnStart(ctx, info, &indexer.CallbackInput{ Docs: docs, }) @@ -407,7 +407,7 @@ func (i *MyIndexer) Store(ctx context.Context, docs []*schema.Document, opts ... // 4. Execute storage logic ids, err := i.doStore(ctx, docs, options) - // 5. Handle error and completion callback + // 5. Handle errors and completion callback if err != nil { ctx = cm.OnError(ctx, info, err) return nil, err @@ -421,7 +421,7 @@ 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 (handle common option parameters) + // Implement document storage logic (pay attention to handling common option parameters) // 1. If Embedding component is set, generate vector representations for documents if opts.Embedding != nil { // Extract document content @@ -434,7 +434,7 @@ func (i *MyIndexer) doStore(ctx context.Context, docs []*schema.Document, opts * if err != nil { return nil, err } - // Store vectors in document MetaData + // Store vectors in document's MetaData for j, doc := range docs { doc.WithVector(vectors[j]) } 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 da5dd9fb077..f6786b854d4 100644 --- a/content/en/docs/eino/core_modules/components/lambda_guide.md +++ b/content/en/docs/eino/core_modules/components/lambda_guide.md @@ -3,122 +3,150 @@ Description: "" date: "2025-11-20" lastmod: "" tags: [] -title: 'Eino: Lambda Guide' -weight: 5 +title: 'Eino: Lambda User Guide' +weight: 4 --- -## Introduction +## **Introduction** -`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`. +Lambda is the most basic component type in Eino. It allows users to embed custom function logic in workflows. The Lambda component is composed of 4 types of execution functions based on whether input and output are streams, corresponding to 4 interaction modes: Invoke, Stream, Collect, Transform. -The framework converts among paradigms under defined rules. See [Overview](/docs/eino/overview) for details (Runnable section). +Users can implement one or more of these when building Lambda, and the framework will convert them according to certain rules. For detailed introduction, see: [Eino: Overview](/docs/eino/overview) (see the Runnable section) -## Definition and Construction +## **Component Definition and Implementation** -> Code: `eino/compose/types_lambda.go` +The core of the Lambda component is the `Lambda` struct, which wraps user-provided Lambda functions. Users can create a Lambda component through construction methods: + +> Code location: eino/compose/types_lambda.go ```go -type Lambda struct { executor *composableRunnable } +type Lambda struct { + executor *composableRunnable +} ``` -Lambda function signatures: +The four function types supported by Lambda are defined as follows, meaning user-provided Lambda functions need to satisfy these 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 -> Examples: [https://github.com/cloudwego/eino-examples/blob/main/components/lambda](https://github.com/cloudwego/eino-examples/blob/main/components/lambda) +> Code in examples references: [https://github.com/cloudwego/eino-examples/blob/main/components/lambda](https://github.com/cloudwego/eino-examples/blob/main/components/lambda) -### Constructors +### Construction Methods -Eino components generally accept `func(ctx, input, ...option) (output, error)`. For lambdas, simpler constructors are provided: +From the unified specification of Eino component interfaces, a component's callable method needs to have 3 input parameters and 2 output parameters: func (ctx, input, ...option) (output, error). However, in scenarios using Lambda, developers often want to add a Lambda node by providing a simple function implementation, so construction methods are divided into 3 types: -- Provide exactly one paradigm function - - Without custom options - - With custom options -- Provide any subset of the four paradigms via `AnyLambda` +- Provide only one selected interaction function with determined input/output stream types + - Without custom Option + - Using custom Option +- Customize n (n<=4) of the 4 interaction functions: AnyLambda -#### Without Custom Options +#### Without Custom Option -- `InvokableLambda` +- InvokableLambda ```go -// input and output can be any custom types -lambda := compose.InvokableLambda(func(ctx context.Context, input string) (string, error) { +// input and output types can be any custom types +lambda := compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) { // some logic }) ``` -- `StreamableLambda` +- StreamableLambda ```go -// input is any type; output must be *schema.StreamReader[O] -lambda := compose.StreamableLambda(func(ctx context.Context, input string) (*schema.StreamReader[string], error) { +// input can be any type, output must be *schema.StreamReader[O], where O can be any type +lambda := compose.StreamableLambda(func(ctx context.Context, input string) (output *schema.StreamReader[string], err error) { // some logic }) ``` -- `CollectableLambda` +- CollectableLambda ```go -// input must be *schema.StreamReader[I]; output can be any type -lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (string, error) { +// input must be *schema.StreamReader[I], where I can be any type, output can be any type +lambda := compose.CollectableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output string, err error) { // some logic }) ``` -- `TransformableLambda` +- TransformableLambda ```go -// input and output must be *schema.StreamReader[I] -lambda := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (*schema.StreamReader[string], error) { +// input and output must be *schema.StreamReader[I], where I can be any type +lambda := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) (output *schema.StreamReader[string], err error) { // some logic }) ``` -Shared options: +- The construction methods for the four Lambda methods have the following common Option options: +- compose.WithLambdaType(): Modify the Component type of the Lambda component, default is: Lambda +- compose.WithLambdaCallbackEnable(): Disable the Node Callback that is enabled by default for Lambda component in Graph -- `compose.WithLambdaType()` — change component type (default: Lambda) -- `compose.WithLambdaCallbackEnable()` — disable default node callbacks in Graph +#### Using Custom Option -#### With Custom Options +Each interaction mode has a corresponding construction method. Here's an example using Invoke: ```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) (string, error) { - // handle opts + func(ctx context.Context, input string, opts ...MyOption) (output string, err error) { + // Handle opts // some logic - }, + } ) ``` #### AnyLambda -Implement multiple paradigms at once: +AnyLambda allows implementing Lambda function types with multiple interaction modes simultaneously: ```go -type Options struct { Field1 string } +type Options struct { + Field1 string +} + type MyOption func(*Options) +// input and output types can be any custom types lambda, err := compose.AnyLambda( - 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) { /* ... */ }, + // 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 + }, ) ``` -### In Orchestration +### **Usage in Orchestration** + +#### Usage in Graph -#### Graph +Lambda nodes can be added in Graph through AddLambdaNode: ```go graph := compose.NewGraph[string, *MyStruct]() @@ -130,7 +158,9 @@ graph.AddLambdaNode( ) ``` -#### Chain +#### Usage in Chain + +Lambda nodes can be added in Chain through AppendLambda: ```go chain := compose.NewChain[string, string]() @@ -139,43 +169,46 @@ chain.AppendLambda(compose.InvokableLambda(func(ctx context.Context, input strin })) ``` -### Built-in Lambdas +### Two Built-in Lambdas #### ToList -Convert a single element into a one-item slice: +ToList is a built-in Lambda for converting a single input element to a slice containing that element: ```go +// Create a ToList Lambda lambda := compose.ToList[*schema.Message]() + +// Use in Chain chain := compose.NewChain[[]*schema.Message, []*schema.Message]() -chain.AppendChatModel(chatModel) -chain.AppendLambda(lambda) +chain.AppendChatModel(chatModel) // chatModel returns *schema.Message +chain.AppendLambda(lambda) // Convert *schema.Message to []*schema.Message ``` #### MessageParser -Parse a JSON message (often from an LLM) into a struct: +MessageParser is a built-in Lambda for parsing JSON messages (usually generated by LLM) into specified structs: ```go -// define target struct +// Define target struct for parsing type MyStruct struct { ID int `json:"id"` } -// create parser +// Create parser parser := schema.NewMessageJSONParser[*MyStruct](&schema.MessageJSONParseConfig{ ParseFrom: schema.MessageParseFromContent, - ParseKeyPath: "", // use "key.sub.grandsub" to parse subfields + ParseKeyPath: "", // If you only need to parse sub-fields, use "key.sub.grandsub" }) -// create parser lambda +// Create parser Lambda parserLambda := compose.MessageParser(parser) -// use in Chain +// Use in Chain chain := compose.NewChain[*schema.Message, *MyStruct]() chain.AppendLambda(parserLambda) -// example +// Usage example runner, err := chain.Compile(context.Background()) parsed, err := runner.Invoke(context.Background(), &schema.Message{ Content: `{"id": 1}`, @@ -183,10 +216,10 @@ parsed, err := runner.Invoke(context.Background(), &schema.Message{ // parsed.ID == 1 ``` -Parsing from tool call results is also supported: +MessageParser supports parsing data from message content (Content) or tool call results (ToolCall), which is commonly used in intent recognition scenarios: ```go -// parse from tool call results +// Parse from tool call results parser := schema.NewMessageJSONParser[*MyStruct](&schema.MessageJSONParseConfig{ ParseFrom: schema.MessageParseFromToolCall, }) 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 68aaadde243..308016c0311 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,31 +1,31 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: ToolsNode & Tool Guide' -weight: 3 +weight: 9 --- ## Introduction -`Tool` in Eino is an external capability that a `ChatModel` may choose to call, including local functions, MCP server tools, etc. +`Tool` in the Eino framework is defined as "external capabilities that ChatModel can choose to call", including local functions, MCP server tools, etc. -`ToolsNode` is the designated tool executor in Eino. Whether inside a Graph or in an Agent, tool execution is performed via `ToolsNode`: +`ToolsNode` is the designated "Tool executor" in the Eino framework. Whether inside a Graph or in an Agent, tool execution is performed via ToolsNode: ```go // compose/tool_node.go -// run tools using Invoke +// run tools using `Invoke` func (tn *ToolsNode) Invoke(ctx context.Context, input *schema.Message, opts ...ToolsNodeOption) ([]*schema.Message, error) -// run tools using Stream +// 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: +Configure ToolsNode with a list of tools and supporting policies: ```go // compose/tool_node.go @@ -43,9 +43,9 @@ type ToolsNodeConfig struct { } ``` -With this, `ToolsNode` can execute configured tools and gain extensibility such as execution ordering, error handling, argument processing, and middleware. +With this, ToolsNode "can execute configured tools" and gains extensibility such as execution ordering, error handling, argument processing, and middleware extensions. -How does `ToolsNode` decide which tool to run? It does not decide; it executes according to the incoming `*schema.Message`: +How does ToolsNode "decide" which Tool to execute? It doesn't decide - it executes according to the incoming `*schema.Message`: ```go // schema/message.go @@ -54,38 +54,56 @@ 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 + // here 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) +// 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 helps identify the chunk when multiple calls exist (for merging in stream mode) + // 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 identifies the specific tool call + // ID is the id of the tool call, it can be used to identify the specific tool call. ID string `json:"id"` - // Type of the tool call, default "function" + // Type is the type of the tool call, default is "function". Type string `json:"type"` - // Function payload + // Function is the function call to be made. Function FunctionCall `json:"function"` - // Extra information + + // 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` 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. +ChatModel (LLM) generates the []ToolCall to be called (including ToolName, Argument, etc.) and places them in a *schema.Message to pass to ToolsNode. ToolsNode actually executes a call for each ToolCall. + +If ExecuteSequentially is configured, ToolsNode will execute tools in the order they appear in []ToolCall. + +The result of each ToolCall execution is wrapped into a *schema.Message as part of the ToolsNode output. + +## Tool Definition -## Interfaces +### Interface Definition -Tool interfaces have three levels: +The Tool component provides two types of interfaces: **Standard Tool Interface** and **Enhanced Tool Interface**. -> Code: `eino/compose/tool/interface.go` +> Code location: eino/components/tool/interface.go + +#### Standard Tool Interface + +Standard tool interfaces return string type results: ```go // Basic tool interface, provides tool information @@ -93,81 +111,181 @@ type BaseTool interface { Info(ctx context.Context) (*schema.ToolInfo, error) } -// Invokable tool, supports synchronous calls +// Invokable tool interface, supports synchronous calls type InvokableTool interface { BaseTool InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error) } -// Streamable tool, supports streaming output +// Tool interface supporting streaming output type StreamableTool interface { BaseTool StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error) } ``` -### Info +#### Enhanced Tool Interface + +Enhanced tool interfaces support returning structured multimodal results (`*schema.ToolResult`), which can contain text, images, audio, video, and files: + +```go +// EnhancedInvokableTool is a tool interface that supports returning structured multimodal results +// Unlike InvokableTool which returns a string, this interface returns *schema.ToolResult +// which can contain text, images, audio, video, and files +type EnhancedInvokableTool interface { + BaseTool + InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error) +} + +// EnhancedStreamableTool is a streaming tool interface that supports returning structured multimodal results +// Provides a streaming reader for incremental access to multimodal content +type EnhancedStreamableTool interface { + BaseTool + StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error) +} +``` + +### Enhanced Tool Data Structures + +> Code location: eino/schema/message.go + +#### ToolArgument - Tool Input Parameters + +```go +// ToolArgument contains the input information for a tool call +type ToolArgument struct { + // TextArgument contains the tool call parameters in JSON format + TextArgument string +} +``` + +#### ToolResult - Tool Output Result + +```go +// ToolResult represents the structured multimodal output of tool execution +// Used when a tool needs to return more than just a simple string, +// such as images, files, or other structured data +type ToolResult struct { + // Parts contains multimodal output parts. Each part can be a different type of content, + // such as text, images, or files + Parts []ToolOutputPart `json:"parts,omitempty"` +} +``` + +#### ToolOutputPart - Output Content Part + +```go +// ToolPartType defines the content type of a tool output part +type ToolPartType string + +const ( + ToolPartTypeText ToolPartType = "text" // Text + ToolPartTypeImage ToolPartType = "image" // Image + ToolPartTypeAudio ToolPartType = "audio" // Audio + ToolPartTypeVideo ToolPartType = "video" // Video + ToolPartTypeFile ToolPartType = "file" // File +) + +// ToolOutputPart represents a part of the tool execution output +type ToolOutputPart struct { + Type ToolPartType `json:"type"` // Content type + Text string `json:"text,omitempty"` // Text content + Image *ToolOutputImage `json:"image,omitempty"` // Image content + Audio *ToolOutputAudio `json:"audio,omitempty"` // Audio content + Video *ToolOutputVideo `json:"video,omitempty"` // Video content + File *ToolOutputFile `json:"file,omitempty"` // File content + Extra map[string]any `json:"extra,omitempty"` // Extra information +} + +// Multimedia content structs, all containing URL or Base64 data and MIME type information +type ToolOutputImage struct { MessagePartCommon } +type ToolOutputAudio struct { MessagePartCommon } +type ToolOutputVideo struct { MessagePartCommon } +type ToolOutputFile struct { MessagePartCommon } +``` + +### Method Descriptions + +#### Info Method + +- Purpose: Get tool description information +- Parameters: + - ctx: Context object +- Returns: + - `*schema.ToolInfo`: Tool description information + - error: Errors during information retrieval + +#### InvokableRun Method (Standard Tool) + +- Purpose: Synchronously execute the tool +- Parameters: + - ctx: Context object for passing request-level information and Callback Manager + - `argumentsInJSON`: JSON format parameter string + - opts: Tool execution options +- Returns: + - string: Execution result + - error: Errors during execution + +#### InvokableRun Method (Enhanced Tool) -- Purpose: return tool description for the model -- Params: - - `ctx`: context +- Purpose: Synchronously execute the tool, returning multimodal results +- Parameters: + - ctx: Context object + - `toolArgument`: `*schema.ToolArgument` containing JSON format parameters + - opts: Tool execution options - Returns: - - `*schema.ToolInfo`: tool metadata (name/desc/params) - - `error` + - `*schema.ToolResult`: Execution result containing multimodal content + - error: Errors during execution -### InvokableRun +#### StreamableRun Method (Standard Tool) -- Purpose: synchronous tool execution -- Params: - - `ctx`: context (request-scoped; carries callback manager) - - `argumentsInJSON`: JSON string of arguments - - `opts`: tool options +- Purpose: Execute tool in streaming mode +- Parameters: + - ctx: Context object + - `argumentsInJSON`: JSON format parameter string + - opts: Tool execution options - Returns: - - `string`: execution result - - `error` + - `*schema.StreamReader[string]`: Streaming execution result + - error: Errors during execution -### StreamableRun +#### StreamableRun Method (Enhanced Tool) -- Purpose: streaming tool execution -- Params: - - `ctx`: context (request-scoped; carries callback manager) - - `argumentsInJSON`: JSON string of arguments - - `opts`: tool options +- Purpose: Execute tool in streaming mode, returning multimodal result stream +- Parameters: + - ctx: Context object + - `toolArgument`: `*schema.ToolArgument` containing JSON format parameters + - opts: Tool execution options - Returns: - - `*schema.StreamReader[string]`: streaming result - - `error` + - `*schema.StreamReader[*schema.ToolResult]`: Streaming multimodal execution result + - error: Errors during execution -## ToolInfo +### ToolInfo Struct -> Code: `eino/schema/tool.go` +> Code location: eino/schema/tool.go ```go type ToolInfo struct { - // Unique tool name, clearly expressing its purpose + // Unique tool name that clearly expresses its purpose Name string - // Guidance for the model on how/when/why to use the tool - // You can include brief examples in the description + // Used to tell the model how/when/why to use this tool + // Can include brief examples in the description Desc string - // Definition of accepted parameters - // Two ways to describe: - // 1. ParameterInfo: schema.NewParamsOneOfByParams(params) - // 2. OpenAPI v3: schema.NewParamsOneOfByOpenAPIV3(openAPIV3) + // Definition of parameters the tool accepts + // Can be described in two ways: + // 1. Using ParameterInfo: schema.NewParamsOneOfByParams(params) + // 2. Using OpenAPIV3: schema.NewParamsOneOfByOpenAPIV3(openAPIV3) *ParamsOneOf } ``` -- 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)` +### Common Options -## Options - -`ToolOption` configures tool behavior. ToolsNode has no global options; implementations define specific options via `WrapToolImplSpecificOptFn`. +The Tool component uses ToolOption to define optional parameters. ToolsNode has no abstracted common options. Each specific implementation can define its own specific Options, wrapped into the unified ToolOption type using the WrapToolImplSpecificOptFn function. ## Usage +### Standard Tool Usage + ```go import ( "github.com/cloudwego/eino/components/tool" @@ -175,19 +293,21 @@ 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": "深圳", "date": "tomorrow"}`, + Arguments: `{"city": "Shenzhen", "date": "tomorrow"}`, }, }, }, @@ -196,9 +316,161 @@ input := &schema.Message{ toolMessages, err := toolsNode.Invoke(ctx, input) ``` -ToolsNode is typically used after a `ChatModel` inside orchestration. +### Enhanced Tool Usage + +Enhanced tools are suitable for scenarios that need to return multimodal content, such as images, audio, video, or files. + +#### Method 1: Use InferEnhancedTool for Automatic Inference + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +// Define input parameter struct +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"description=Search keyword"` +} + +// Create enhanced tool +imageSearchTool, err := utils.InferEnhancedTool( + "image_search", + "Search and return related images", + func(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + // Execute image search logic... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "Found the following images:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil + }, +) +``` + +#### Method 2: Use NewEnhancedTool for Manual Creation + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type FileGeneratorInput struct { + FileName string `json:"file_name"` + Content string `json:"content"` +} + +// Manually define ToolInfo +toolInfo := &schema.ToolInfo{ + Name: "file_generator", + Desc: "Generate and return a file", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "file_name": {Type: "string", Desc: "File name"}, + "content": {Type: "string", Desc: "File content"}, + }), +} + +// Create enhanced tool +fileGenTool := utils.NewEnhancedTool[*FileGeneratorInput]( + toolInfo, + func(ctx context.Context, input *FileGeneratorInput) (*schema.ToolResult, error) { + fileURL := "https://example.com/files/" + input.FileName + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "File generated: " + input.FileName}, + { + Type: schema.ToolPartTypeFile, + File: &schema.ToolOutputFile{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &fileURL, + MIMEType: "text/plain", + }, + }, + }, + }, + }, nil + }, +) +``` + +#### Method 3: Implement EnhancedInvokableTool Interface + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/schema" +) + +type MyEnhancedTool struct { + info *schema.ToolInfo +} + +func (t *MyEnhancedTool) Info(ctx context.Context) (*schema.ToolInfo, error) { + return t.info, nil +} + +func (t *MyEnhancedTool) InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.ToolResult, error) { + // Parse arguments + // toolArgument.TextArgument contains JSON format parameters + + // Execute tool logic... + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "Execution result"}, + }, + }, nil +} +``` -### In Orchestration +#### Enhanced Streaming Tool + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type StreamInput struct { + Query string `json:"query"` +} + +// Create enhanced streaming tool +streamTool, err := utils.InferEnhancedStreamTool( + "stream_search", + "Streaming search tool", + func(ctx context.Context, input *StreamInput) (*schema.StreamReader[*schema.ToolResult], error) { + results := []*schema.ToolResult{ + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Searching..."}}}, + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Found results"}}}, + } + return schema.StreamReaderFromArray(results), nil + }, +) +``` + +### Usage in Orchestration ```go import ( @@ -207,26 +479,41 @@ import ( "github.com/cloudwego/eino/schema" ) -// Chain +// Create tools node +toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{ + Tools: []tool.BaseTool{ + searchTool, // Search tool + weatherTool, // Weather query tool + calculatorTool, // Calculator tool + }, + }) + +// In Chain chain := compose.NewChain[*schema.Message, []*schema.Message]() chain.AppendToolsNode(toolsNode) -// Graph +// In Graph graph := compose.NewGraph[*schema.Message, []*schema.Message]() -graph.AddToolsNode(toolsNode) +graph.AddToolsNode("tools", toolsNode) ``` +> **Note**: When a tool implements both standard and enhanced interfaces, ToolsNode will prioritize using the enhanced interface. + ## Option Mechanism +Custom Tools can implement specific Options as needed: + ```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 @@ -234,7 +521,72 @@ func WithTimeout(timeout time.Duration) tool.Option { } ``` -## Callbacks +## Middleware Mechanism + +ToolsNode supports intercepting and enhancing tool calls through Middleware. There are four types of Middleware: + +```go +// compose/tool_node.go + +// ToolMiddleware combines middleware hooks for invokable and streamable tool calls +type ToolMiddleware struct { + // Invokable is for non-streaming standard tool calls + Invokable InvokableToolMiddleware + + // Streamable is for streaming standard tool calls + Streamable StreamableToolMiddleware + + // EnhancedInvokable is for non-streaming enhanced tool calls + EnhancedInvokable EnhancedInvokableToolMiddleware + + // EnhancedStreamable is for streaming enhanced tool calls + EnhancedStreamable EnhancedStreamableToolMiddleware +} +``` + +### Middleware Usage Example + +```go +import ( + "context" + "fmt" + + "github.com/cloudwego/eino/compose" + "github.com/cloudwego/eino/schema" +) + +// Create ToolsNode with Middleware +toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{ + Tools: []tool.BaseTool{myEnhancedTool}, + ToolCallMiddlewares: []compose.ToolMiddleware{ + { + // Standard tool middleware + Invokable: func(next compose.InvokableToolEndpoint) compose.InvokableToolEndpoint { + return func(ctx context.Context, input *compose.ToolInput) (*compose.ToolOutput, error) { + fmt.Printf("Calling standard tool: %s\n", input.Name) + return next(ctx, input) + } + }, + // Enhanced tool middleware + EnhancedInvokable: func(next compose.EnhancedInvokableToolEndpoint) compose.EnhancedInvokableToolEndpoint { + return func(ctx context.Context, input *compose.ToolInput) (*compose.EnhancedInvokableToolOutput, error) { + fmt.Printf("Calling enhanced tool: %s\n", input.Name) + output, err := next(ctx, input) + if err != nil { + return nil, err + } + fmt.Printf("Enhanced tool returned %d content parts\n", len(output.Result.Parts)) + return output, nil + } + }, + }, + }, +}) +``` + +## Option and Callback Usage + +### Callback Usage Example ```go import ( @@ -300,21 +652,66 @@ result, err := runnable.Invoke(ctx, input, compose.WithCallbacks(helper)) In the tool function body or tool callback handler, you can use `compose.GetToolCallID(ctx)` to get the current Tool's ToolCallID. -## Implementations +## Existing Implementations + +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) +3. MCP: Use MCP server as tool [Tool - MCP](/docs/eino/ecosystem_integration/tool/tool_mcp) + +### v0.5.x → v0.6.x + +Based on the following two considerations: + +1. Major model vendor APIs and MCP Tool protocols specify using JSONSchema to describe tool input/output schemas. +2. The getkin/kin-openapi@v0.118.0 referenced by Eino has security issues, and secure versions of kin-openapi have incompatible updates. + +Eino has removed all OpenAPI schema 3.0 related definitions and methods, switching to JSONSchema 2020-12. For specific removed and added definitions and methods, see [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397). + +After upgrading, some eino-ext modules may error with "undefined: schema.NewParamsOneOfByOpenAPIV3" - upgrade the erroring eino-ext modules to the latest version. + +If schema migration is complex, you can use the helper tool methods provided in [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397). + +### v0.6.x New Enhanced Tools + +New `EnhancedInvokableTool` and `EnhancedStreamableTool` interfaces have been added, supporting structured multimodal results. + +**Main Changes:** + +1. **New Tool Interfaces**: + +- `EnhancedInvokableTool`: Receives `*schema.ToolArgument`, returns `*schema.ToolResult` +- `EnhancedStreamableTool`: Receives `*schema.ToolArgument`, returns `*schema.StreamReader[*schema.ToolResult]` + +2. **New Tool Helper Functions** (`components/tool/utils/`): + +- `InferEnhancedTool`: Automatically infer and create enhanced tool from function +- `InferEnhancedStreamTool`: Automatically infer and create enhanced streaming tool from function +- `NewEnhancedTool`: Manually create enhanced tool +- `NewEnhancedStreamTool`: Manually create enhanced streaming tool + +3. **New Data Structures** (`schema/message.go`): + +- `ToolPartType`: Tool output content type enum (text, image, audio, video, file) +- `ToolArgument`: Tool input parameter struct +- `ToolResult`: Tool multimodal output result struct +- `ToolOutputPart`: Tool output content part +- `ToolOutputImage/Audio/Video/File`: Various multimedia output structs -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) +4. **ToolsNode Enhancements**: -### v0.5.x → v0.6.x Migration +- Added `EnhancedInvokableToolMiddleware` and `EnhancedStreamableToolMiddleware` +- Supports mixed use of enhanced tools and standard tools +- When a tool implements both interfaces, the enhanced interface is prioritized -Eino removed all OpenAPI 3.0 related definitions and methods and switched to JSONSchema 2020-12 because: +5. **Callback Enhancements**: -- 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 +- `CallbackOutput` added `ToolOutput *schema.ToolResult` field for enhanced tool multimodal output -See details: https://github.com/cloudwego/eino/discussions/397 +**Use Cases:** -After upgrading, some `eino-ext` modules may error with `undefined: schema.NewParamsOneOfByOpenAPIV3`; upgrade those modules to the latest versions. +Enhanced tools are suitable for scenarios that need to return rich media content, such as: -If schema migration is complex, use the helper tooling linked in the discussion above. +- Image search tools returning found images +- File generation tools returning generated files +- Audio/video processing tools returning processed media files +- Multimodal AI Agent scenarios 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 5ce3d7b0ba7..485591ed101 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,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: How to Create a Tool @@ -9,12 +9,14 @@ weight: 1 ## Tool Structure Basics -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: +An agent calling a tool involves two steps: (1) the LLM constructs parameters according to the tool definition; (2) the tool is actually called. -- Tool metadata and parameter constraints -- An execution interface +These two basic steps require that a tool contains two parts: -In Eino, any tool must implement `Info()` to return tool metadata: +- Tool functionality description and parameter information needed to call the tool +- Interface to call the tool + +In Eino, the BaseTool interface requires any tool to have an Info() interface that returns tool information: ```go type BaseTool interface { @@ -22,7 +24,9 @@ type BaseTool interface { } ``` -Execution interfaces depend on whether the result is streaming: +### Standard Tool Interface + +Based on whether the tool's return structure is streaming, tools can be divided into InvokableTool and StreamableTool, also defined as interfaces: ```go type InvokableTool interface { @@ -39,13 +43,69 @@ type StreamableTool interface { } ``` +### Enhanced Tool Interface + +In addition to standard tool interfaces, Eino also provides enhanced tool interfaces that support returning structured multimodal results. Enhanced tools are suitable for scenarios that need to return images, audio, video, files, and other rich media content: + +```go +// EnhancedInvokableTool is a tool interface that supports returning structured multimodal results +// Unlike InvokableTool which returns a string, this interface returns *schema.ToolResult +// which can contain text, images, audio, video, and files +type EnhancedInvokableTool interface { + BaseTool + InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error) +} + +// EnhancedStreamableTool is a streaming tool interface that supports returning structured multimodal results +type EnhancedStreamableTool interface { + BaseTool + StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error) +} +``` + +#### Enhanced Tool Data Structures + +```go +// ToolArgument contains the input information for a tool call +type ToolArgument struct { + TextArgument string // Tool call parameters in JSON format +} + +// ToolResult represents the structured multimodal output of tool execution +type ToolResult struct { + Parts []ToolOutputPart `json:"parts,omitempty"` +} + +// ToolPartType defines the content type of a tool output part +type ToolPartType string + +const ( + ToolPartTypeText ToolPartType = "text" // Text + ToolPartTypeImage ToolPartType = "image" // Image + ToolPartTypeAudio ToolPartType = "audio" // Audio + ToolPartTypeVideo ToolPartType = "video" // Video + ToolPartTypeFile ToolPartType = "file" // File +) + +// ToolOutputPart represents a part of the tool execution output +type ToolOutputPart struct { + Type ToolPartType `json:"type"` + Text string `json:"text,omitempty"` + Image *ToolOutputImage `json:"image,omitempty"` + Audio *ToolOutputAudio `json:"audio,omitempty"` + Video *ToolOutputVideo `json:"video,omitempty"` + File *ToolOutputFile `json:"file,omitempty"` + Extra map[string]any `json:"extra,omitempty"` +} +``` + ## ToolInfo Representations -In LLM function-call flows, the model must understand whether generated parameters satisfy constraints. Based on developer habits and domain standards, Eino provides two representations: `params map[string]*ParameterInfo` and `JSON Schema`. +In the LLM function call process, the LLM generates the parameters for the function call, which requires the LLM to understand whether the generated parameters meet constraints. In Eino, based on developer habits and domain standards, two parameter constraint representations are provided: `params map[string]*ParameterInfo` and `*jsonschema.Schema`. -### 1) `map[string]*ParameterInfo` +### Method 1 - map[string]*ParameterInfo -Intuitive map-based parameter descriptions: +In many developers' intuitive habits, parameters can be described using a map where the key is the parameter name and the value is the detailed constraint for that parameter. Eino defines ParameterInfo to represent a parameter description: ```go // Full definition: https://github.com/cloudwego/eino/blob/main/schema/tool.go @@ -59,7 +119,7 @@ type ParameterInfo struct { } ``` -Example: +For example, a parameter representing a User can be expressed as: ```go map[string]*schema.ParameterInfo{ @@ -71,17 +131,19 @@ map[string]*schema.ParameterInfo{ Type: schema.Integer, }, "gender": &schema.ParameterInfo{ - Type: schema.String, + Type: schema.String, Enum: []string{"male", "female"}, }, } ``` -### 2) JSON Schema +This representation is very simple and intuitive, commonly used when parameters are manually maintained by developers through coding. + +### Method 2 - JSON Schema -Another common way to represent parameter constraints is `JSON Schema`: [https://json-schema.org/draft/2020-12](https://json-schema.org/draft/2020-12). +Another common way to represent parameter constraints is JSON Schema ([https://json-schema.org/draft/2020-12](https://json-schema.org/draft/2020-12)). -The JSON Schema standard provides very rich ways to constrain parameters. In practice, developers generally don't construct this structure themselves, but use methods to generate it. +The JSON Schema standard provides very rich ways to constrain parameters. In practice, developers generally don't construct this structure themselves but use methods to generate it. #### Using GoStruct2ParamsOneOf @@ -91,15 +153,15 @@ Eino provides a way to describe parameter constraints through go tags in structs func GoStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error) ``` -The tags used to extract parameter field names and descriptions from T are as follows: +The tags used to extract parameter field names and descriptions from T are: -- jsonschema_description:"xxx" [recommended] or jsonschema:"description=xxx" +- `jsonschema_description:"xxx"` [recommended] or `jsonschema:"description=xxx"` - Descriptions often contain commas, and commas in tags are separators for different fields and cannot be escaped. It's strongly recommended to use the separate jsonschema_description tag. -- jsonschema:"enum=xxx,enum=yyy,enum=zzz" -- Fields are required by default; json:"xxx,omitempty" => use json tag's omitempty to indicate not required -- Use utils.WithSchemaModifier to implement custom parsing methods +- `jsonschema:"enum=xxx,enum=yyy,enum=zzz"` +- `jsonschema:"required"` +- `json:"xxx,omitempty"` => can use json tag's omitempty to represent non-required -Example: +Use `utils.WithSchemaModifier` to implement custom parsing methods, see the example below: ```go package main @@ -110,7 +172,7 @@ import ( ) type User struct { - Name string `json:"name,omitempty" jsonschema_description=the name 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"` } @@ -120,13 +182,17 @@ func main() { } ``` -This method is generally not called by developers directly; instead, use `utils.GoStruct2ToolInfo()` to build ToolInfo, or use `utils.InferTool()` directly to build a tool. See the "Converting local functions to tools" section below for details. +This method is generally not called directly by developers. Instead, use `utils.GoStruct2ToolInfo()` to build ToolInfo, or use `utils.InferTool()` directly to build a tool. See the "Converting local functions to tools" section below for details. ## Ways to Implement a Tool -### Approach 1 — Implement Interfaces Directly +### Method 1 - Implement Interfaces Directly -Implement `InvokableTool`: +Since tool definitions are all interfaces, the most direct way to implement a tool is to implement the interface. + +#### Implementing Standard Tool Interface + +Using InvokableTool as an example: ```go type AddUser struct{} @@ -136,13 +202,13 @@ func (t *AddUser) Info(_ context.Context) (*schema.ToolInfo, error) { Name: "add_user", Desc: "add user", ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ - // omitted; see earlier for building params constraints + // omit, refer to the above method for building params constraints }), }, nil } func (t *AddUser) InvokableRun(_ context.Context, argumentsInJSON string, _ ...tool.Option) (string, error) { - // 1. Deserialize argumentsInJSON and handle options + // 1. Deserialize argumentsInJSON, handle options, etc. user, _ := json.Unmarshal([]byte(argumentsInJSON)) // 2. Handle business logic // 3. Serialize the result to string and return @@ -151,29 +217,82 @@ func (t *AddUser) InvokableRun(_ context.Context, argumentsInJSON string, _ ...t } ``` -Because the LLM always supplies a JSON string, the tool receives `argumentsInJSON`; you deserialize it and return a JSON string. +Since the LLM's function call parameters are always a string, corresponding to the Eino framework, the tool's call parameter input is also a JSON serialized into a string. Therefore, this method requires developers to handle parameter deserialization themselves, and the call result is also returned as a string. -### Approach 2 — Wrap a Local Function +#### Implementing Enhanced Tool Interface -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. +When you need to return multimodal content (such as images, audio, video, files, etc.), you can implement the EnhancedInvokableTool interface: -> See tests in `cloudwego/eino/components/tool/utils/invokable_func_test.go` and `streamable_func_test.go`. +```go +type ImageSearchTool struct{} -#### `NewTool` +func (t *ImageSearchTool) Info(_ context.Context) (*schema.ToolInfo, error) { + return &schema.ToolInfo{ + Name: "image_search", + Desc: "Search and return related images", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": { + Type: schema.String, + Desc: "Search keyword", + Required: true, + }, + }), + }, nil +} -For functions of signature: +func (t *ImageSearchTool) InvokableRun(_ context.Context, toolArgument *schema.ToolArgument, _ ...tool.Option) (*schema.ToolResult, error) { + // 1. Parse parameters (toolArgument.TextArgument contains JSON format parameters) + var input struct { + Query string `json:"query"` + } + json.Unmarshal([]byte(toolArgument.TextArgument), &input) + + // 2. Execute search logic... + imageURL := "https://example.com/image.png" + + // 3. Return multimodal result + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "Found the following images:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} +``` + +### Method 2 - Convert Local Functions to Tools + +During development, we often need to wrap a local function into an Eino tool. For example, our code already has an AddUser method, but to let the LLM autonomously decide how to call this method, we need to turn this method into a tool and bind it to the LLM. + +Eino provides the NewTool method to convert a function into a tool. Additionally, for scenarios where parameter constraints are expressed through struct tags, the InferTool method is provided to make the construction process simpler. + +For examples of the methods below, refer to unit tests in `cloudwego/eino/components/tool/utils/invokable_func_test.go` and `cloudwego/eino/components/tool/utils/streamable_func_test.go`. + +#### Standard Tool: Using NewTool Method + +When a function has the following signature, you can use NewTool to turn it into an InvokableTool: ```go type InvokeFunc[T, D any] func(ctx context.Context, input T) (output D, err error) ``` -Use: +The NewTool method is: ```go +// Code: 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 ``` -Example: +Similarly, NewStreamTool can create a StreamableTool. + +Using AddUser as an example, it can be built as follows: ```go import ( @@ -210,7 +329,7 @@ func createTool() tool.InvokableTool { Type: schema.Integer, }, "gender": &schema.ParameterInfo{ - Type: schema.String, + Type: schema.String, Enum: []string{"male", "female"}, }, }, @@ -221,15 +340,19 @@ func createTool() tool.InvokableTool { } ``` -#### `InferTool` +#### Standard Tool: Using InferTool Method -When parameter constraints live in the input struct tags, use `InferTool`: +From NewTool, we can see that building a tool requires separately passing ToolInfo and InvokeFunc, where ToolInfo contains the ParamsOneOf part representing the function's input parameter constraints, and InvokeFunc's signature also has an input parameter. This means: the ParamsOneOf part and InvokeFunc's input parameter need to be consistent. + +When a function is entirely implemented by developers themselves, developers need to manually maintain the input parameter and ParamsOneOf to keep them consistent. A more elegant solution is "maintaining parameter constraints directly in the input parameter type definition", refer to the GoStruct2ParamsOneOf introduction above. + +When parameter constraint information is contained in the input parameter type definition, you can use InferTool to implement it: ```go func InferTool[T, D any](toolName, toolDesc string, i InvokeFunc[T, D], opts ...Option) (tool.InvokableTool, error) ``` -Example: +Using AddUser as an example: ```go import ( @@ -239,9 +362,9 @@ import ( ) type User struct { - Name string `json:"name" jsonschema:"description=the name of the user"` - Age int `json:"age,omitempty" jsonschema:"description=the age of the user"` - Gender string `json:"gender,omitempty" jsonschema:"enum=male,enum=female"` + Name string `json:"name" jsonschema:"required,description=the name of the user"` + Age int `json:"age" jsonschema:"description=the age of the user"` + Gender string `json:"gender" jsonschema:"enum=male,enum=female"` } type Result struct { @@ -257,11 +380,201 @@ func createTool() (tool.InvokableTool, error) { } ``` -#### Using InferOptionableTool +#### Enhanced Tool: Using NewEnhancedTool Method + +When you need to return multimodal results, you can use the NewEnhancedTool method: + +```go +type EnhancedInvokeFunc[T any] func(ctx context.Context, input T) (output *schema.ToolResult, err error) + +func NewEnhancedTool[T any](desc *schema.ToolInfo, i EnhancedInvokeFunc[T], opts ...Option) tool.EnhancedInvokableTool +``` + +Example: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchInput struct { + Query string `json:"query"` +} + +func searchImages(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + // Execute image search logic... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "Found the following images:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} + +func createEnhancedTool() tool.EnhancedInvokableTool { + return utils.NewEnhancedTool(&schema.ToolInfo{ + Name: "image_search", + Desc: "Search and return related images", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": {Type: schema.String, Desc: "Search keyword", Required: true}, + }), + }, searchImages) +} +``` -The Option mechanism is a mechanism provided by Eino for passing dynamic parameters at runtime. For details, see [Eino: CallOption Capabilities and Conventions](/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities). This mechanism also applies to custom tools. +#### Enhanced Tool: Using InferEnhancedTool Method -When you need custom option parameters, use `InferOptionableTool`: +Similar to InferTool, InferEnhancedTool can automatically infer parameter constraints from function signatures: + +```go +func InferEnhancedTool[T any](toolName, toolDesc string, i EnhancedInvokeFunc[T], opts ...Option) (tool.EnhancedInvokableTool, error) +``` + +Example: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"required" jsonschema_description:"Search keyword"` +} + +func searchImages(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "Found the following images:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} + +func createEnhancedTool() (tool.EnhancedInvokableTool, error) { + return utils.InferEnhancedTool("image_search", "Search and return related images", searchImages) +} +``` + +#### Enhanced Streaming Tool: Using InferEnhancedStreamTool Method + +For scenarios that need to stream multimodal content, you can use InferEnhancedStreamTool: + +```go +func InferEnhancedStreamTool[T any](toolName, toolDesc string, s EnhancedStreamFunc[T], opts ...Option) (tool.EnhancedStreamableTool, error) +``` + +Example: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type StreamSearchInput struct { + Query string `json:"query" jsonschema:"required"` +} + +func streamSearch(ctx context.Context, input *StreamSearchInput) (*schema.StreamReader[*schema.ToolResult], error) { + results := []*schema.ToolResult{ + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Searching..."}}}, + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Found results"}}}, + } + return schema.StreamReaderFromArray(results), nil +} + +func createEnhancedStreamTool() (tool.EnhancedStreamableTool, error) { + return utils.InferEnhancedStreamTool("stream_search", "Streaming search tool", streamSearch) +} +``` + +#### Enhanced Tool: Using InferOptionableEnhancedTool Method + +When you need custom option parameters, you can use InferOptionableEnhancedTool: + +```go +func InferOptionableEnhancedTool[T any](toolName, toolDesc string, i OptionableEnhancedInvokeFunc[T], opts ...Option) (tool.EnhancedInvokableTool, error) +``` + +Example: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchOption struct { + MaxResults int + Quality string +} + +func WithMaxResults(n int) tool.Option { + return tool.WrapImplSpecificOptFn(func(o *ImageSearchOption) { + o.MaxResults = n + }) +} + +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"required"` +} + +func searchImagesWithOption(ctx context.Context, input *ImageSearchInput, opts ...tool.Option) (*schema.ToolResult, error) { + baseOption := &ImageSearchOption{MaxResults: 10, Quality: "high"} + option := tool.GetImplSpecificOptions(baseOption, opts...) + + // Use option.MaxResults and option.Quality to execute search... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: fmt.Sprintf("Returning %d images:", option.MaxResults)}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{URL: &imageURL}, + }, + }, + }, + }, nil +} + +func createOptionableEnhancedTool() (tool.EnhancedInvokableTool, error) { + return utils.InferOptionableEnhancedTool("image_search", "Search images", searchImagesWithOption) +} +``` + +#### Using InferOptionableTool Method (Standard Tool) + +The Option mechanism is a mechanism provided by Eino for passing dynamic parameters at runtime. For details, see Eino: CallOption Capabilities and Conventions. This mechanism also applies to custom tools. + +When developers need custom option parameters, they can use InferOptionableTool. Compared to InferTool's function signature requirements, this method adds an option parameter: ```go func InferOptionableTool[T, D any](toolName, toolDesc string, i OptionableInvokeFunc[T, D], opts ...Option) (tool.InvokableTool, error) @@ -309,15 +622,15 @@ func useInInvoke() { } ``` -### Approach 3 — Use tools from eino-ext +### Method 3 - Use Tools from eino-ext -Beyond custom tools that need to be implemented yourself, the eino-ext project has many general-purpose tool implementations that can be used out of the box, such as [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch), [Tool - DuckDuckGoSearch](/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search), wikipedia, httprequest, etc. See various implementations at [https://github.com/cloudwego/eino-ext/tree/main/components/tool](https://github.com/cloudwego/eino-ext/tree/main/components/tool). +Besides various custom tools that need to be implemented yourself, the eino-ext project has many general-purpose tool implementations that can be used out of the box, such as Tool - Googlesearch, Tool - DuckDuckGoSearch, wikipedia, httprequest, etc. See various implementations at [https://github.com/cloudwego/eino-ext/tree/main/components/tool](https://github.com/cloudwego/eino-ext/tree/main/components/tool). -### Approach 4 — Use MCP protocol +### Method 4 - Use MCP Protocol -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. +MCP (Model Context Protocol) is an open model context protocol. More and more tools and platforms are exposing their capabilities to LLMs based on this protocol. Eino can use tools provided via MCP as regular tools, greatly expanding the variety of tools available. -Using MCP tools in Eino is straightforward: +Using MCP-provided tools in Eino is very convenient: ```go import ( @@ -359,4 +672,8 @@ func getMCPTool(ctx context.Context) []tool.BaseTool { } ``` -Code reference: 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](https://github.com/cloudwego/eino-ext/blob/main/components/tool/mcp/examples/mcp.go) + +## Tool Type Selection Guide + +> **Note**: When a tool implements both standard and enhanced interfaces, ToolsNode will prioritize using the enhanced interface. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md new file mode 100644 index 00000000000..3528a927361 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md @@ -0,0 +1,205 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Backend: Ark Agentkit Sandbox' +weight: 1 +--- + +## Agentkit Sandbox Backend + +Package: `github.com/cloudwego/eino-ext/adk/backend/agentkit` + +Note: If your eino version is v0.8.0 or above, you need to use ark agentkit backend [v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.0-alpha.1) version. + +### Overview + +Agentkit Sandbox Backend is a remote sandbox implementation of EINO ADK FileSystem that executes file system operations in an isolated cloud environment through Volcengine Agentkit service. + +#### Core Features + +- Security Isolation - All operations execute in a remote sandbox environment +- Session Management - Supports session isolation with configurable TTL +- Request Signing - Automatic AK/SK authentication for Volcengine API + +### Installation + +```bash +go get github.com/cloudwego/eino-ext/adk/backend/agentkit +``` + +### Configuration + +#### Environment Variables + +```bash +export VOLC_ACCESS_KEY_ID="your_access_key" +export VOLC_SECRET_ACCESS_KEY="your_secret_key" +export VOLC_TOOL_ID="your_tool_id" +``` + +#### Config Structure + +```go +type Config struct { + // Required + AccessKeyID string // Access Key ID + SecretAccessKey string // Access Key Secret + ToolID string // Sandbox Tool ID + + // Optional + UserSessionID string // User session ID for isolation + Region Region // Region, defaults to cn-beijing + SessionTTL int // Session TTL (60-86400 seconds) + ExecutionTimeout int // Command execution timeout + HTTPClient *http.Client // Custom HTTP client +} +``` + +### Quick Start + +#### Basic Usage + +```go +import ( + "context" + "os" + "time" + + "github.com/cloudwego/eino-ext/adk/backend/agentkit" + "github.com/cloudwego/eino/adk/filesystem" +) + +func main() { + ctx := context.Background() + + backend, err := agentkit.NewSandboxToolBackend(&agentkit.Config{ + AccessKeyID: os.Getenv("VOLC_ACCESS_KEY_ID"), + SecretAccessKey: os.Getenv("VOLC_SECRET_ACCESS_KEY"), + ToolID: os.Getenv("VOLC_TOOL_ID"), + UserSessionID: "session-" + time.Now().Format("20060102-150405"), + Region: agentkit.RegionOfBeijing, + }) + if err != nil { + panic(err) + } + + // Write file + err = backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/home/gem/hello.txt", + Content: "Hello, Sandbox!", + }) + + // Read file + content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/home/gem/hello.txt", + }) +} +``` + +#### Integration with Agent + +```go +import ( + "github.com/cloudwego/eino/adk" + fsMiddleware "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +// Create Backend +backend, _ := agentkit.NewSandboxToolBackend(config) + +// Create Middleware +middleware, _ := fsMiddleware.New(ctx, &fsMiddleware.Config{ + Backend: backend, + Shell: backend, +}) + +// Create Agent +agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "SandboxAgent", + Description: "AI Agent with secure file system access capabilities", + Model: chatModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### API Reference + + + + + + + + + + +
    MethodDescription
    LsInfoList directory contents
    ReadRead file content (supports pagination)
    WriteCreate new file (error if exists)
    EditReplace file content
    GrepRawSearch file content
    GlobInfoFind files by pattern
    ExecuteExecute shell commands
    + +#### Examples + +```go +// List directory +files, _ := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/home/gem", +}) + +// Read file (paginated) +content, _ := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/home/gem/file.txt", + Offset: 0, + Limit: 100, +}) + +// Search content +matches, _ := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Path: "/home/gem", + Pattern: "keyword", + Glob: "*.txt", +}) + +// Find files +files, _ := backend.GlobInfo(ctx, &filesystem.GlobInfoRequest{ + Path: "/home/gem", + Pattern: "**/*.txt", +}) + +// Edit file +backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/home/gem/file.txt", + OldString: "old", + NewString: "new", + ReplaceAll: true, +}) + +// Execute command +result, _ := backend.Execute(ctx, &filesystem.ExecuteRequest{ + Command: "ls -la /home/gem", +}) +``` + +### Comparison with Local Backend + + + + + + + + +
    FeatureAgentkitLocal
    Execution ModelRemote SandboxLocal Direct
    Network DependencyRequiredNot Required
    Configuration ComplexityRequires CredentialsZero Config
    Security ModelIsolated SandboxOS Permissions
    Use CasesMulti-tenant/ProductionDevelopment/Local
    + +### FAQ + +**Q: Write returns "file already exists" error** + +This is a security feature. Use a different filename or use Edit to modify existing files. + +**Q: Authentication failed** + +Check environment variables, verify AK/SK match, and ensure account has Ark Sandbox permissions. + +**Q: Request timeout** + +Increase ExecutionTimeout or HTTPClient.Timeout configuration. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md new file mode 100644 index 00000000000..a81f29354c4 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Local_FileSystem.md @@ -0,0 +1,234 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Backend: Local FileSystem' +weight: 2 +--- + +## Local Backend + +Package: `github.com/cloudwego/eino-ext/adk/backend/local` + +Note: If your eino version is v0.8.0 or above, you need to use local backend [v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Flocal%2Fv0.2.0-alpha.1) version. + +### Overview + +Local Backend is the local file system implementation of EINO ADK FileSystem, directly operating on the local file system, providing native performance and zero-configuration experience. + +#### Core Features + +- Zero Configuration - Works out of the box +- Native Performance - Direct file system access, no network overhead +- Path Safety - Enforces absolute paths +- Streaming Execution - Supports real-time command output streaming +- Command Validation - Optional security validation hooks + +### Installation + +```bash +go get github.com/cloudwego/eino-ext/adk/backend/local +``` + +### Configuration + +```go +type Config struct { + // Optional: Command validation function for Execute() security control + ValidateCommand func(string) error +} +``` + +### Quick Start + +#### Basic Usage + +```go +import ( + "context" + + "github.com/cloudwego/eino-ext/adk/backend/local" + "github.com/cloudwego/eino/adk/filesystem" +) + +func main() { + ctx := context.Background() + + backend, err := local.NewBackend(ctx, &local.Config{}) + if err != nil { + panic(err) + } + + // Write file (must be absolute path) + err = backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/tmp/hello.txt", + Content: "Hello, Local Backend!", + }) + + // Read file + content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/tmp/hello.txt", + }) +} +``` + +#### With Command Validation + +```go +func validateCommand(cmd string) error { + allowed := map[string]bool{"ls": true, "cat": true, "grep": true} + parts := strings.Fields(cmd) + if len(parts) == 0 || !allowed[parts[0]] { + return fmt.Errorf("command not allowed: %s", parts[0]) + } + return nil +} + +backend, _ := local.NewBackend(ctx, &local.Config{ + ValidateCommand: validateCommand, +}) +``` + +#### Integration with Agent + +```go +import ( + "github.com/cloudwego/eino/adk" + fsMiddleware "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +// Create Backend +backend, _ := local.NewBackend(ctx, &local.Config{}) + +// Create Middleware +middleware, _ := fsMiddleware.New(ctx, &fsMiddleware.Config{ + Backend: backend, + StreamingShell: backend, +}) + +// Create Agent +agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "LocalFileAgent", + Description: "AI Agent with local file system access capabilities", + Model: chatModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### API Reference + + + + + + + + + + + +
    MethodDescription
    LsInfoList directory contents
    ReadRead file content (supports pagination, default 200 lines)
    WriteCreate new file (error if exists)
    EditReplace file content
    GrepRawSearch file content (literal match)
    GlobInfoFind files by pattern
    ExecuteExecute shell commands
    ExecuteStreamingExecute commands with streaming output
    + +#### Examples + +```go +// List directory +files, _ := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/home/user", +}) + +// Read file (paginated) +content, _ := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/path/to/file.txt", + Offset: 0, + Limit: 50, +}) + +// Search content (literal match, not regex) +matches, _ := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Path: "/home/user/project", + Pattern: "TODO", + Glob: "*.go", +}) + +// Find files +files, _ := backend.GlobInfo(ctx, &filesystem.GlobInfoRequest{ + Path: "/home/user", + Pattern: "**/*.go", +}) + +// Edit file +backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/tmp/file.txt", + OldString: "old", + NewString: "new", + ReplaceAll: true, +}) + +// Execute command +result, _ := backend.Execute(ctx, &filesystem.ExecuteRequest{ + Command: "ls -la /tmp", +}) + +// Streaming execution +reader, _ := backend.ExecuteStreaming(ctx, &filesystem.ExecuteRequest{ + Command: "tail -f /var/log/app.log", +}) +for { + resp, err := reader.Recv() + if err == io.EOF { + break + } + fmt.Print(resp.Stdout) +} +``` + +### Path Requirements + +All paths must be absolute paths (starting with `/`): + +```go +// Correct +backend.Read(ctx, &filesystem.ReadRequest{FilePath: "/home/user/file.txt"}) + +// Incorrect +backend.Read(ctx, &filesystem.ReadRequest{FilePath: "./file.txt"}) +``` + +Convert relative paths: + +```go +absPath, _ := filepath.Abs("./relative/path") +``` + +### Comparison with Agentkit Backend + + + + + + + + + + +
    FeatureLocalAgentkit
    Execution ModelLocal DirectRemote Sandbox
    Network DependencyNoneRequired
    Configuration ComplexityZero ConfigRequires Credentials
    Security ModelOS PermissionsIsolated Sandbox
    Streaming OutputSupportedNot Supported
    Platform SupportUnix/Linux/macOSAny
    Use CasesDevelopment/LocalMulti-tenant/Production
    + +### FAQ + +**Q: Why are absolute paths required?** + +To prevent directory traversal attacks. Use `filepath.Abs()` to convert. + +**Q: Write fails** + +File already exists (security feature), path is not absolute, or insufficient permissions. + +**Q: Does GrepRaw support regex?** + +No, it uses literal matching. For regex, use Execute to call system grep. + +**Q: Windows support?** + +Not supported, depends on `/bin/sh`. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md new file mode 100644 index 00000000000..1c2b968a311 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md @@ -0,0 +1,237 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: FileSystem' +weight: 1 +--- + +> 💡 +> Package: [https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) + +# Overview + +FileSystem Middleware provides Agents with file system access and large tool result offloading capabilities. +It defines a unified file system Backend interface, allowing users to use the default in-memory implementation directly or extend custom backends based on their needs. + +Core features include: + +- Access to virtual file system (ls/read/write/edit/glob/grep) +- Automatically offload oversized tool results to the file system, keeping summaries in the Agent context with content loaded on demand + +# Backend Interface + +FileSystem Middleware operates on the file system through the Backend interface in `github.com/cloudwego/eino/adk/filesystem`: + +```go +type Backend interface { + // List files at the specified path (structured information) + LsInfo(path string) ([]FileInfo, error) + // Read file content by offset and limit (returns LLM-friendly text) + Read(ctx context.Context, filePath string, offset, limit int) (string, error) + // Grep pattern in the specified path, returns list of matches + GrepRaw(ctx context.Context, pattern string, path, glob *string) ([]GrepMatch, error) + // Glob match based on pattern and path + GlobInfo(ctx context.Context, pattern, path string) ([]FileInfo, error) + // Write or update a file + Write(ctx context.Context, filePath, content string) error + // Replace string in a file + Edit(ctx context.Context, filePath, oldString, newString string, replaceAll bool) error +} +``` + +### **Extended Interfaces** + +```go +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +`InMemoryBackend` is an in-memory implementation of the `Backend` interface, storing files in a map with concurrent-safe access. + +```go +import "github.com/cloudwego/eino/adk/filesystem" + +ctx := context.Background() +backend := filesystem.NewInMemoryBackend() + +// Write a file +err := backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/example/test.txt", + Content: "Hello, World!\nLine 2\nLine 3", +}) + +// Read a file +content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/example/test.txt", + Offset: 1, + Limit: 10, +}) + +// List directory +files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/example", +}) + +// Search content +matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Pattern: "Hello", + Path: "/example", +}) + +// Edit a file +err = backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/example/test.txt", + OldString: "Hello", + NewString: "Hi", + ReplaceAll: false, +}) +``` + +Other Backend implementations: + +# Filesystem Middleware + +The Middleware automatically injects a set of tools and corresponding system prompts into the Agent, enabling it to directly operate on the file system. + +### **Creating the Middleware** + +It is recommended to use the `New` function to create the middleware (returns `ChatModelAgentMiddleware`): + +```go +import "github.com/cloudwego/eino/adk/middlewares/filesystem" + +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + // Set Shell or StreamingShell if shell command execution capability is needed + Shell: myShell, +}) +if err != nil { + // handle error +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### **Config Options** + +```go +type MiddlewareConfig struct { + // Backend provides file system operations, required + Backend filesystem.Backend + + // Shell provides shell command execution capability + // If set, the execute tool will be registered + // Optional, mutually exclusive with StreamingShell + Shell filesystem.Shell + + // StreamingShell provides streaming shell command execution capability + // If set, the streaming execute tool will be registered + // Optional, mutually exclusive with Shell + StreamingShell filesystem.StreamingShell + + // CustomSystemPrompt overrides the default system prompt + // Optional, defaults to ToolsSystemPrompt + CustomSystemPrompt *string + + // Custom names for each tool, all optional + CustomLsToolName *string // default "ls" + CustomReadFileToolName *string // default "read_file" + CustomWriteFileToolName *string // default "write_file" + CustomEditFileToolName *string // default "edit_file" + CustomGlobToolName *string // default "glob" + CustomGrepToolName *string // default "grep" + CustomExecuteToolName *string // default "execute" + + // Custom descriptions for each tool, all optional + CustomLsToolDesc *string + CustomReadFileToolDesc *string + CustomGrepToolDesc *string + CustomGlobToolDesc *string + CustomWriteFileToolDesc *string + CustomEditToolDesc *string + CustomExecuteToolDesc *string +} +``` + +> 💡 +> The `New` function returns `ChatModelAgentMiddleware`, providing better context propagation capabilities. For scenarios requiring large tool result offloading, please use the `NewMiddleware` function or use it together with the ToolReduction middleware. + +Injected tools: + +These tools all come with default English descriptions and built-in system prompts. To switch to Chinese, you can set it via `adk.SetLanguage()`: + +``` +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // Switch to Chinese +adk.SetLanguage(adk.LanguageEnglish) // Switch to English (default) +``` + +You can also customize the description text and tool names through `Config`: + +```go +type MiddlewareConfig struct { + // Override default System Prompt (optional) + CustomSystemPrompt *string + + // Override tool names (optional) + CustomLsToolName *string + CustomReadFileToolName *string + // ... + + // Override tool descriptions (optional) + CustomLsToolDesc *string + CustomReadFileToolDesc *string + // ... +} +``` + +# [deprecated] Tool Result Offloading + +> 💡 +> This feature will be deprecated in 0.8.0. Migrate to [Middleware: ToolReduction](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction) + +When tool call results are too large (e.g., reading large files, grep hitting a lot of content), continuing to put the complete results in the conversation context will cause: + +- Token count increases dramatically +- Agent history context pollution +- Inference efficiency deterioration + +To address this, the Middleware provides an automatic offloading mechanism: + +- When the result size exceeds the threshold (default 20,000 tokens) + → The complete content is not returned directly to the LLM +- The actual result is saved to the file system +- The context only includes: + - Summary + - File path (agent can call the tool again to read it) + + + +This feature is enabled by default and behavior can be adjusted through configuration: + +```go +type Config struct { + // other config... + + // Disable automatic offloading + WithoutLargeToolResultOffloading bool + + // Custom trigger threshold (default 20000 tokens) + LargeToolResultOffloadingTokenLimit int + + // Custom offload file path generation + // Default path format: /large_tool_result/{ToolCallID} + LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) +} +``` diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md new file mode 100644 index 00000000000..58919c396d3 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md @@ -0,0 +1,159 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: PatchToolCalls' +weight: 7 +--- + +adk/middlewares/patchtoolcalls + +> 💡 +> The PatchToolCalls middleware is used to fix "dangling tool calls" issues in the message history. This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Overview + +In multi-turn conversation scenarios, there may be cases where an Assistant message contains ToolCalls, but the corresponding Tool message response is missing from the conversation history. Such "dangling tool calls" can cause some model APIs to throw errors or produce abnormal behavior. + +**Common scenarios:** + +- User sent a new message before tool execution completed, causing the tool call to be interrupted +- Some tool call results were lost when restoring a session +- User canceled tool execution in a Human-in-the-loop scenario + +The PatchToolCalls middleware scans the message history before each model call and automatically inserts placeholder messages for tool calls that lack responses. + +## Quick Start + +```go +import ( + "context" + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/adk/middlewares/patchtoolcalls" +) + +// Create middleware with default configuration +mw, err := patchtoolcalls.New(ctx, nil) +if err != nil { + // Handle error +} + +// Use with ChatModelAgent +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{mw}, +}) +``` + +## Configuration Options + +```go +type Config struct { + // PatchedContentGenerator custom function to generate placeholder message content + // Optional, uses default message if not set + PatchedContentGenerator func(ctx context.Context, toolName, toolCallID string) (string, error) +} +``` + + + + +
    FieldTypeRequiredDescription
    PatchedContentGenerator
    func(ctx, toolName, toolCallID string) (string, error)
    NoCustom function to generate placeholder message content. Parameters include tool name and call ID, returns the content to fill in
    + +### Default Placeholder Message + +If `PatchedContentGenerator` is not set, the middleware uses a default placeholder message: + +**English (default):** + +``` +Tool call {toolName} with id {toolCallID} was cancelled - another message came in before it could be completed. +``` + +**Chinese:** + +``` +工具调用 {toolName}(ID 为 {toolCallID})已被取消——在其完成之前收到了另一条消息。 +``` + +You can switch languages via `adk.SetLanguage()`. + +## Usage Examples + +### Custom Placeholder Message + +```go +mw, err := patchtoolcalls.New(ctx, &patchtoolcalls.Config{ + PatchedContentGenerator: func(ctx context.Context, toolName, toolCallID string) (string, error) { + return fmt.Sprintf("[System Notice] Tool %s execution was skipped (Call ID: %s)", toolName, toolCallID), nil + }, +}) +``` + +### Combined with Other Middlewares + +```go +// PatchToolCalls should usually be placed at the front of the middleware chain +// to ensure dangling tool calls are fixed before other middlewares process messages +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{ + patchToolCallsMiddleware, // Fix messages first + summarizationMiddleware, // Then summarize + reductionMiddleware, // Finally reduce + }, +}) +``` + +## How It Works + + + +**Processing Logic:** + +1. Executes in the `BeforeModelRewriteState` hook +2. Iterates through all messages to find Assistant messages containing `ToolCalls` +3. For each ToolCall, checks if a corresponding Tool message exists in subsequent messages (matched by `ToolCallID`) +4. If no corresponding Tool message is found, inserts a placeholder message +5. Returns the repaired message list + +## Example Scenario + +### Message History Before Repair + +``` +[User] "Help me check the weather" +[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}] +[Tool] "call_1: Sunny, 25°C" +[User] "No need to check the location, just tell me Beijing's weather" <- User interrupts +``` + +### Message History After Repair + +``` +[User] "Help me check the weather" +[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}] +[Tool] "call_1: Sunny, 25°C" +[Tool] "call_2: Tool call get_location (ID: call_2) was cancelled..." <- Automatically inserted +[User] "No need to check the location, just tell me Beijing's weather" +``` + +## Multi-language Support + +Placeholder messages support both Chinese and English, switch via `adk.SetLanguage()`: + +```go +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // Chinese +adk.SetLanguage(adk.LanguageEnglish) // English (default) +``` + +## Notes + +> 💡 +> This middleware only modifies the history messages for the current run in the `BeforeModelRewriteState` hook, and does not affect the actual stored message history. The repair is temporary and only used for the current agent call. + +- It's recommended to place this middleware at the **front** of the middleware chain to ensure other middlewares process a complete message history +- If your scenario requires persisting the repaired messages, implement the corresponding logic in `PatchedContentGenerator` diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md new file mode 100644 index 00000000000..308aacb0c12 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md @@ -0,0 +1,292 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: PlanTask' +weight: 4 +--- + +# PlanTask Middleware + +adk/middlewares/plantask + +> 💡 +> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Overview + +`plantask` is a task management middleware that allows Agents to create and manage task lists. The middleware injects four tools through the `BeforeAgent` hook: + +- **TaskCreate**: Create a task +- **TaskGet**: View task details +- **TaskUpdate**: Update a task +- **TaskList**: List all tasks + +Main purposes: + +- Track progress of complex tasks +- Break large tasks into smaller steps +- Manage dependencies between tasks + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Agent │ +│ │ +│ ┌───────────────────────────────────────────────────────────────────┐ │ +│ │ BeforeAgent: Inject task tools │ │ +│ │ - TaskCreate │ │ +│ │ - TaskGet │ │ +│ │ - TaskUpdate │ │ +│ │ - TaskList │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ Backend │ +│ │ +│ Storage structure: │ +│ baseDir/ │ +│ ├── .highwatermark # ID counter │ +│ ├── 1.json # Task #1 │ +│ ├── 2.json # Task #2 │ +│ └── ... │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Configuration + +```go +type Config struct { + Backend Backend // Storage backend, required + BaseDir string // Task file directory, required +} +``` + +- Note that the Backend implementation should be isolated by session, with different sessions corresponding to different Backends (task lists) + +--- + +## Backend Interface + +```go +type Backend interface { + LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error) + Read(ctx context.Context, req *ReadRequest) (string, error) + Write(ctx context.Context, req *WriteRequest) error + Delete(ctx context.Context, req *DeleteRequest) error +} +``` + +--- + +## Task Structure + +```go +type task struct { + ID string `json:"id"` // Task ID + Subject string `json:"subject"` // Title + Description string `json:"description"` // Description + Status string `json:"status"` // Status + Blocks []string `json:"blocks"` // Tasks blocked by this one + BlockedBy []string `json:"blockedBy"` // Tasks blocking this one + ActiveForm string `json:"activeForm"` // Active form text + Owner string `json:"owner"` // Responsible agent + Metadata map[string]any `json:"metadata"` // Custom data +} +``` + +### Status + + + + + + + +
    StatusDescription
    pending
    Pending (default)
    in_progress
    In progress
    completed
    Completed
    deleted
    Deleted (will delete the file)
    + +Status transition: `pending` → `in_progress` → `completed`, any status can be directly `deleted`. + +--- + +## Tools + +### TaskCreate + +Create a task. + + + + + + + +
    ParameterTypeRequiredDescription
    subject
    stringYesTitle
    description
    stringYesDescription
    activeForm
    stringNoActive form text, e.g., "Running tests"
    metadata
    objectNoCustom data
    + +When to use: + +- The task is relatively complex with 3 or more steps +- The user has given a list of things to do +- You need to show progress to the user + +When not to use: + +- It's just a simple task +- Something that can be done quickly + +### TaskGet + +View task details. + + + + +
    ParameterTypeRequiredDescription
    taskId
    stringYesTask ID
    + +Returns complete information about the task: title, description, status, dependencies, etc. + +### TaskUpdate + +Update a task. + + + + + + + + + + + + +
    ParameterTypeRequiredDescription
    taskId
    stringYesTask ID
    subject
    stringNoNew title
    description
    stringNoNew description
    activeForm
    stringNoNew active form text
    status
    stringNoNew status
    addBlocks
    []stringNoAdd blocked tasks
    addBlockedBy
    []stringNoAdd tasks blocking this one
    owner
    stringNoResponsible agent
    metadata
    objectNoCustom data (set to null to delete)
    + +Notes: + +- `status: "deleted"` will directly delete the task file +- Circular dependencies are checked when adding dependencies +- Automatic cleanup occurs when all tasks are completed + +### TaskList + +List all tasks, no parameters required. + +Returns a summary of each task: ID, status, title, responsible agent, dependencies. + +--- + +## Usage Example + +```go +ctx := context.Background() + +// The plantask middleware should normally be session-scoped +// Different sessions correspond to different task lists +middleware, err := plantask.New(ctx, &plantask.Config{ + Backend: myBackend, + BaseDir: "/tasks", +}) +if err != nil { + return err +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: myModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### Typical Flow + +``` +1. Receive complex task + │ + ▼ +2. TaskCreate to create tasks + - #1: Analyze requirements + - #2: Write code + │ + ▼ +3. TaskUpdate to set dependencies + - #2 depends on #1 + - #3 depends on #2 + │ + ▼ +4. TaskList to see what tasks exist + │ + ▼ +5. TaskUpdate to start working + - Change #1 to in_progress + │ + ▼ +6. When done, TaskUpdate + - Change #1 to completed + │ + ▼ +7. Loop 4-6 until all completed + │ + ▼ +8. Automatic cleanup +``` + +--- + +## Dependency Management + +- **blocks**: These tasks can start after I complete +- **blockedBy**: I can start after these tasks complete + +``` +Task #1 (blocks: ["2"]) ────► Task #2 (blockedBy: ["1"]) + +#2 can only start after #1 completes +``` + +Circular dependencies will throw an error: + +``` +#1 blocks #2 +#2 blocks #1 ← Not allowed, circular +``` + +--- + +## Automatic Cleanup + +When all tasks are `completed`, all task files will be automatically deleted. + +--- + +## Notes + +- Task files are stored in JSON format in the `BaseDir` directory, with filenames as `{id}.json` +- The `.highwatermark` file is used to record the maximum assigned task ID, ensuring IDs don't repeat +- All tool operations are protected by mutex locks and are concurrency-safe +- The tool descriptions contain detailed usage guidelines that the Agent will follow + +--- + +## Multi-language Support + +Tool descriptions support Chinese and English switching via `adk.SetLanguage()`: + +```go +// Use Chinese descriptions +adk.SetLanguage(adk.LanguageChinese) + +// Use English descriptions (default) +adk.SetLanguage(adk.LanguageEnglish) +``` + +This setting is global and affects all ADK built-in prompts and tool descriptions. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md new file mode 100644 index 00000000000..5cf392861c8 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md @@ -0,0 +1,503 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: Skill' +weight: 2 +--- + +Skill Middleware provides Skill support for Eino ADK Agents, enabling Agents to dynamically discover and use predefined skills to complete tasks more accurately and efficiently. + +# What is a Skill + +A Skill is a folder containing instructions, scripts, and resources that Agents can discover and use on demand to extend their capabilities. The core of a Skill is a `SKILL.md` file containing metadata (at least name and description) and instructions guiding the Agent to perform specific tasks. + +``` +my-skill/ +├── SKILL.md # Required: Instructions + metadata +├── scripts/ # Optional: Executable code +├── references/ # Optional: Reference documentation +└── assets/ # Optional: Templates, resources +``` + +Skills use **Progressive Disclosure** to efficiently manage context: + +1. **Discovery**: At startup, the Agent only loads the name and description of each available Skill, enough to determine when the Skill might be needed +2. **Activation**: When a task matches a Skill's description, the Agent reads the complete `SKILL.md` content into context +3. **Execution**: The Agent follows instructions to execute the task, and can also load other files or execute bundled code as needed. This approach keeps the Agent responsive while allowing on-demand access to more context. + +> 💡 +> Ref: [https://agentskills.io/home](https://agentskills.io/home) + +# Interface Introduction + +## FrontMatter + +The metadata structure of a Skill, used for quick display of Skill information during discovery phase, avoiding loading full content: + +```go +type FrontMatter struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Context ContextMode `yaml:"context"` + Agent string `yaml:"agent"` + Model string `yaml:"model"` +} +``` + + + + + + + + +
    FieldTypeDescription
    Name
    string
    Unique identifier for the Skill. Agent calls the Skill by this name. Recommend using short, meaningful names (e.g.,
    pdf-processing
    ,
    web-research
    ). Corresponds to the
    name
    field in SKILL.md frontmatter
    Description
    string
    Description of Skill functionality. This is the key basis for Agent to decide whether to use the Skill, should clearly explain applicable scenarios and capabilities. Corresponds to the
    description
    field in SKILL.md frontmatter
    Context
    ContextMode
    Context mode. Possible values:
    fork
    (copy history messages to create new Agent for execution),
    isolate
    (create new Agent with isolated context for execution). Leave empty for inline mode (directly return Skill content)
    Agent
    string
    Specify the Agent name to use. Used with
    Context
    field, gets the corresponding Agent factory function via
    AgentHub
    . Leave empty to use default Agent
    Model
    string
    Specify the model name to use. Gets the corresponding model instance via
    ModelHub
    . In Context mode, passed to Agent factory; in inline mode, switches the model used by subsequent ChatModel calls
    + +### ContextMode + +```go +const ( + ContextModeFork ContextMode = "fork" // Copy history messages + ContextModeIsolate ContextMode = "isolate" // Isolate context +) +``` + + + + + + +
    ModeDescription
    Inline (default)Skill content is returned directly as tool result, processed by current Agent
    ForkCreate new Agent, copy current conversation history, execute Skill task independently and return result
    IsolateCreate new Agent with isolated context (only containing Skill content), execute independently and return result
    + +## Skill + +Complete Skill structure, containing metadata and actual instruction content: + +```go +type Skill struct { + FrontMatter + Content string + BaseDirectory string +} +``` + + + + + + +
    FieldTypeDescription
    FrontMatter
    FrontMatter
    Embedded metadata structure, including
    Name
    ,
    Description
    ,
    Context
    ,
    Agent
    ,
    Model
    Content
    string
    Body content after frontmatter in SKILL.md file. Contains detailed instructions, workflows, examples, etc. Agent reads this content after activating Skill
    BaseDirectory
    string
    Absolute path of Skill directory. Agent can use this path to access other resource files in the Skill directory (such as scripts, templates, reference docs, etc.)
    + +## Backend + +Skill backend interface, defining how skills are retrieved. The Backend interface decouples skill storage from usage, providing the following benefits: + +- **Flexible storage**: Skills can be stored in local file system, database, remote service, cloud storage, etc. +- **Extensibility**: Teams can implement custom Backends as needed, such as dynamically loading from Git repository, getting from config center, etc. +- **Test friendly**: Can easily create Mock Backend for unit testing + +```go +type Backend interface { + List(ctx context.Context) ([]FrontMatter, error) + Get(ctx context.Context, name string) (Skill, error) +} +``` + + + + + +
    MethodDescription
    List
    List metadata of all available skills. Called at Agent startup to build skill tool descriptions, letting Agent know which skills are available
    Get
    Get complete skill content by name. Called when Agent decides to use a skill, returns complete Skill structure with detailed instructions
    + +## AgentHub and ModelHub + +When Skills use Context mode (fork/isolate), AgentHub and ModelHub need to be configured: + +```go +// AgentFactory creates Agent instances +type AgentFactory func(ctx context.Context, m model.ToolCallingChatModel) (adk.Agent, error) + +// AgentHub provides Agent factory functions +type AgentHub interface { + Get(ctx context.Context, name string) (AgentFactory, error) +} + +// ModelHub provides model instances +type ModelHub interface { + Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) +} +``` + +### **NewBackendFromFilesystem** + +Backend implementation based on `filesystem.Backend` interface, reads skills from specified directory: + +```go +type BackendFromFilesystemConfig struct { + Backend filesystem.Backend + BaseDir string +} + +func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystemConfig) (Backend, error) +``` + + + + + +
    FieldTypeRequiredDescription
    Backend
    filesystem.Backend
    YesFile system backend implementation for file operations
    BaseDir
    string
    YesPath to skills root directory. Scans all first-level subdirectories under this directory, looking for directories containing
    SKILL.md
    files as skills
    + +How it works: + +- Scans first-level subdirectories under `BaseDir` +- Looks for `SKILL.md` file in each subdirectory +- Parses YAML frontmatter to get metadata +- Deeply nested `SKILL.md` files are ignored + +### **filesystem.Backend Implementations** + +The `filesystem.Backend` interface has the following two implementations to choose from: + +#### **Local Backend (Local File System)** + +Implementation based on local file system, suitable for Unix/MacOS environments: + +```go +import "github.com/cloudwego/eino-ext/adk/backend/local" + +type Config struct { + ValidateCommand func(string) error // optional +} + +func NewBackend(ctx context.Context, cfg *Config) (filesystem.Backend, error) +``` + + + + +
    FieldTypeRequiredDescription
    ValidateCommand
    func(string) error
    NoCommand validation function for security checks before command execution
    + +> **Note**: Only supports Unix/MacOS, does not support Windows. + +#### **Sandbox Backend (Sandbox Environment)** + +Implementation based on Volcengine AgentKit sandbox tool, suitable for scenarios requiring isolated execution environments: + +```go +import "github.com/cloudwego/eino-ext/adk/backend/agentkit" + +type Config struct { + AccessKeyID string + SecretAccessKey string + Region Region // Optional, default cn-beijing + ToolID string + SessionID string // Provide at least one of SessionID or UserSessionID + UserSessionID string // Provide at least one of SessionID or UserSessionID + SessionTTL int // Optional, default 1800 seconds + ExecutionTimeout int // Optional + HTTPClient *http.Client // Optional +} + +func NewSandboxToolBackend(config *Config) (filesystem.Backend, error) +``` + + + + + + + + + + + +
    FieldTypeRequiredDescription
    AccessKeyID
    string
    YesVolcengine Access Key ID
    SecretAccessKey
    string
    YesVolcengine Secret Access Key
    Region
    Region
    NoRegion, supports
    cn-beijing
    (default) and
    cn-shanghai
    ToolID
    string
    YesSandbox Tool ID
    SessionID
    string
    NoSession ID, provide at least one of SessionID or UserSessionID
    UserSessionID
    string
    NoUser Session ID, provide at least one of SessionID or UserSessionID
    SessionTTL
    int
    NoSession TTL (seconds), range 60-86400, default 1800
    ExecutionTimeout
    int
    NoCode execution timeout (seconds)
    + +> For more information see: [Volcengine AgentKit Documentation](https://www.volcengine.com/docs/86681/1847934) + +**LocalBackend** Built-in local file system backend implementation, reads skills from specified directory: + +```go +type LocalBackendConfig struct { + BaseDir string +} + +func NewLocalBackend(config *LocalBackendConfig) (*LocalBackend, error) +``` + + + + +
    FieldTypeRequiredDescription
    BaseDir
    string
    YesPath to skills root directory. LocalBackend scans all subdirectories under this directory, looking for directories containing
    SKILL.md
    files as skills
    + +How it works: + +- Scans first-level subdirectories under `BaseDir` +- Looks for `SKILL.md` file in each subdirectory +- Parses YAML frontmatter to get metadata + +## Initialization + +Create Skill Middleware (recommend using `NewChatModelAgentMiddleware`): + +```go +func NewChatModelAgentMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error) +``` + +Config is configured as: + +```go +type Config struct { + // Backend skill backend implementation, required + Backend Backend + + // SkillToolName skill tool name, default "skill" + SkillToolName *string + + // AgentHub provides Agent factory functions for Context mode + // Required when Skill uses "context: fork" or "context: isolate" + AgentHub AgentHub + + // ModelHub provides model instances for Skills specifying models + ModelHub ModelHub + + // CustomSystemPrompt custom system prompt + CustomSystemPrompt SystemPromptFunc + + // CustomToolDescription custom tool description + CustomToolDescription ToolDescriptionFunc +} +``` + + + + + + + + + +
    FieldTypeRequiredDefaultDescription
    Backend
    Backend
    Yes
  • Skill backend implementation. Responsible for skill storage and retrieval, can use built-in
    LocalBackend
    or custom implementation
    SkillToolName
    *string
    No
    "skill"
    Skill tool name. Agent calls skill tool by this name. If your Agent already has a tool with the same name, you can customize the name through this field to avoid conflicts
    AgentHub
    AgentHub
    No
  • Provides Agent factory functions. Required when Skill uses
    context: fork
    or
    context: isolate
    ModelHub
    ModelHub
    No
  • Provides model instances. Used when Skill specifies the
    model
    field
    CustomSystemPrompt
    SystemPromptFunc
    NoBuilt-in promptCustom system prompt function
    CustomToolDescription
    ToolDescriptionFunc
    NoBuilt-in descriptionCustom tool description function
    + +# Quick Start + +Taking loading pdf skill from local as an example, complete code at [https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill](https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill). + +- Create skills directory in working directory: + +```go +workdir/ +├── skills/ +│ └── pdf/ +│ ├── scripts +│ │ └── analyze.py +│ └── SKILL.md +└── other files +``` + +- Create local filesystem backend, and create Skill middleware based on backend: + +```go +import ( + "github.com/cloudwego/eino/adk/middlewares/skill" + "github.com/cloudwego/eino-ext/adk/backend/local" +) + + +be, err := local.NewBackend(ctx, &local.Config{}) +if err != nil { + log.Fatal(err) +} + +wd, _ := os.Getwd() +workDir := filepath.Join(wd, "adk", "middlewares", "skill", "workdir") +skillBackend, err := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{ + Backend: be, + BaseDir: skillsDir, +}) +``` + +- Create local Filesystem Middleware based on backend, for agent to read skill other files and execute scripts: + +```go +import ( + "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +fsm, err := filesystem.NewMiddleware(ctx, &filesystem.Config{ + Backend: be, + WithoutLargeToolResultOffloading: true, +}) +``` + +- Create Agent and configure middlewares + +```go +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "LogAnalysisAgent", + Description: "An agent that can analyze logs", + Instruction: "You are a helpful assistant.", + Model: cm, + Middlewares: []adk.ChatModelAgentMiddleware{fsm, skillMiddleware}, +}) +``` + +- Call Agent and observe results + +```go +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: agent, +}) + +input := fmt.Sprintf("Analyze the %s file", filepath.Join(workDir, "test.log")) +log.Println("User: ", input) + +iterator := runner.Query(ctx, input) +for { + event, ok := iterator.Next() + if !ok { + break + } + if event.Err != nil { + log.Printf("Error: %v\n", event.Err) + break + } + + prints.Event(event) +} +``` + +Agent output: + +```yaml +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool name: skill +arguments: {"skill":"log_analyzer"} + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool response: Launching skill: log_analyzer +Base directory for this skill: /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/skills/log_analyzer +# SKILL.md content + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool name: execute +arguments: {"command": "python3 /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/skills/log_analyzer/scripts/analyze.py /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/test.log"} + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool response: Analysis Result for /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/test.log: +Total Errors: 2 +Total Warnings: 1 + +Error Details: +Line 3: [2024-05-20 10:02:15] ERROR: Database connection failed. +Line 5: [2024-05-20 10:03:05] ERROR: Connection timed out. + +Warning Details: +Line 2: [2024-05-20 10:01:23] WARNING: High memory usage detected. + + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +answer: Here's the analysis result of the log file: + +### Summary +- **Total Errors**: 2 +- **Total Warnings**: 1 + +### Detailed Entries +#### Errors: +1. Line 3: [2024-05-20 10:02:15] ERROR: Database connection failed. +2. Line5: [2024-05-2010:03:05] ERROR: Connection timed out. + +#### Warnings: +1. Line2: [2024-05-2010:01:23] WARNING: High memory usage detected. + +The log file contains critical issues related to database connectivity and a warning about memory usage. Let me know if you need further analysis! +``` + +# Principles + +Skill middleware adds system prompt and skill tool to Agent, system prompt content is as follows, {tool_name} is the tool name of skill tool: + +```python +# Skills System + +**How to Use Skills (Progressive Disclosure):** + +Skills follow a **progressive disclosure** pattern - you see their name and description above, but only read full instructions when needed: + +1. **Recognize when a skill applies**: Check if the user's task matches a skill's description +2. **Read the skill's full instructions**: Use the '{tool_name}' tool to load skill +3. **Follow the skill's instructions**: tool result contains step-by-step workflows, best practices, and examples +4. **Access supporting files**: Skills may include helper scripts, configs, or reference docs - use absolute paths + +**When to Use Skills:** +- User's request matches a skill's domain (e.g., "research X" -> web-research skill) +- You need specialized knowledge or structured workflows +- A skill provides proven patterns for complex tasks + +**Executing Skill Scripts:** +Skills may contain Python scripts or other executable files. Always use absolute paths. + +**Example Workflow:** + +User: "Can you research the latest developments in quantum computing?" + +1. Check available skills -> See "web-research" skill +2. Call '{tool_name}' tool to read the full skill instructions +3. Follow the skill's research workflow (search -> organize -> synthesize) +4. Use any helper scripts with absolute paths + +Remember: Skills make you more capable and consistent. When in doubt, check if a skill exists for the task! +``` + +Skill tool receives the skill name to load, returns the complete content in the corresponding SKILL.md, and informs agent of all available skill names and descriptions in the tool description: + +```sql +Execute a skill within the main conversation + + +When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge. + +How to invoke: +- Use this tool with the skill name only (no arguments) +- Examples: + - `skill: pdf` - invoke the pdf skill + - `skill: xlsx` - invoke the xlsx skill + - `skill: ms-office-suite:pdf` - invoke using fully qualified name + +Important: +- When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action +- NEVER just announce or mention a skill in your text response without actually calling this tool +- This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task +- Only use skills listed in below +- Do not invoke a skill that is already running +- Do not use this tool for built-in CLI commands (like /help, /clear, etc.) + + + +{{- range .Matters }} + + +{{ .Name }} + + +{{ .Description }} + + +{{- end }} + +``` + +Execution example: + + + +> 💡 +> Skill Middleware only provides the ability to load SKILL.md as shown above. If the Skill itself needs to read files, execute scripts, etc., users need to configure the corresponding capabilities for the agent separately. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md new file mode 100644 index 00000000000..dd9cd72f6ba --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md @@ -0,0 +1,210 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: Summarization' +weight: 3 +--- + +## Overview + +The Summarization middleware automatically compresses conversation history when the token count exceeds a configured threshold. This helps maintain context continuity in long conversations while staying within the model's token limits. + +> 💡 +> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Quick Start + +```go +import ( + "context" + "github.com/cloudwego/eino/adk/middlewares/summarization" +) + +// Create middleware with minimal configuration +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, // Required: model used for generating summaries +}) +if err != nil { + // Handle error +} + +// Use with ChatModelAgent +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{mw}, +}) +``` + +## Configuration Options + + + + + + + + + + + + + + +
    FieldTypeRequiredDefaultDescription
    Modelmodel.BaseChatModelYes
  • Chat model used for generating summaries
    ModelOptions[]model.OptionNo
  • Options passed to the model when generating summaries
    TokenCounterTokenCounterFuncNo~4 chars/tokenCustom token counting function
    Trigger*TriggerConditionNo190,000 tokensCondition to trigger summarization
    InstructionstringNoBuilt-in promptCustom summarization instruction
    TranscriptFilePathstringNo
  • Full conversation transcript file path
    PreparePrepareFuncNo
  • Custom preprocessing function before summary generation
    FinalizeFinalizeFuncNo
  • Custom post-processing function for final messages
    CallbackCallbackFuncNo
  • Called after Finalize to observe state changes (read-only)
    EmitInternalEventsboolNofalseWhether to emit internal events
    PreserveUserMessages*PreserveUserMessagesNoEnabled: trueWhether to preserve original user messages in summary
    + +### TriggerCondition Structure + +```go +type TriggerCondition struct { + // ContextTokens triggers summarization when total token count exceeds this threshold + ContextTokens int +} +``` + +### PreserveUserMessages Structure + +```go +type PreserveUserMessages struct { + // Enabled whether to enable user message preservation + Enabled bool + + // MaxTokens maximum tokens for preserved user messages + // Only preserves the most recent user messages until this limit is reached + // Defaults to 1/3 of TriggerCondition.ContextTokens + MaxTokens int +} +``` + +### Configuration Examples + +**Custom Token Threshold** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Trigger: &summarization.TriggerCondition{ + ContextTokens: 100000, // Trigger at 100k tokens + }, +}) +``` + +**Custom Token Counter** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + TokenCounter: func(ctx context.Context, input *summarization.TokenCounterInput) (int, error) { + // Use your tokenizer + return yourTokenizer.Count(input.Messages) + }, +}) +``` + +**Set Transcript File Path** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + TranscriptFilePath: "/path/to/transcript.txt", +}) +``` + +**Custom Finalize Function** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Finalize: func(ctx context.Context, originalMessages []adk.Message, summary adk.Message) ([]adk.Message, error) { + // Custom logic to build final messages + return []adk.Message{ + schema.SystemMessage("Your system prompt"), + summary, + }, nil + }, +}) +``` + +**Using Callback to Observe State Changes/Store** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Callback: func(ctx context.Context, before, after adk.ChatModelAgentState) error { + log.Printf("Summarization completed: %d messages -> %d messages", + len(before.Messages), len(after.Messages)) + return nil + }, +}) +``` + +**Control User Message Preservation** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + PreserveUserMessages: &summarization.PreserveUserMessages{ + Enabled: true, + MaxTokens: 50000, // Preserve up to 50k tokens of user messages + }, +}) +``` + +## How It Works + +```mermaid +flowchart TD + A[BeforeModelRewriteState] --> B{Token count exceeds threshold?} + B -->|No| C[Return original state] + B -->|Yes| D[Emit BeforeSummary event] + D --> E{Has custom Prepare?} + E -->|Yes| F[Call Prepare] + E -->|No| G[Call model to generate summary] + F --> G + G --> H{Has custom Finalize?} + H -->|Yes| I[Call Finalize] + H -->|No| L{Has custom Callback?} + I --> L + L -->|Yes| M[Call Callback] + L -->|No| J[Emit AfterSummary event] + M --> J + J --> K[Return new state] + + style A fill:#e3f2fd + style G fill:#fff3e0 + style D fill:#e8f5e9 + style J fill:#e8f5e9 + style K fill:#c8e6c9 + style C fill:#f5f5f5 + style M fill:#fce4ec + style F fill:#fff3e0 + style I fill:#fff3e0 +``` + +## Internal Events + +When EmitInternalEvents is set to true, the middleware emits events at key points: + + + + + +
    Event TypeTrigger TimingCarried Data
    ActionTypeBeforeSummaryBefore generating summaryOriginal message list
    ActionTypeAfterSummaryAfter completing summaryFinal message list
    + +**Usage Example** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + EmitInternalEvents: true, +}) + +// Listen for events in your event handler +``` + +## Best Practices + +1. **Set TranscriptFilePath**: It's recommended to always provide a conversation transcript file path so the model can reference the original conversation when needed. +2. **Adjust Token Threshold**: Adjust `Trigger.MaxTokens` based on the model's context window size. Generally recommended to set it to 80-90% of the model's limit. +3. **Custom Token Counter**: In production environments, it's recommended to implement a custom `TokenCounter` that matches the model's tokenizer for accurate counting. diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md new file mode 100644 index 00000000000..6463d14c80a --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md @@ -0,0 +1,318 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: ToolReduction' +weight: 6 +--- + +# ToolReduction Middleware + +adk/middlewares/reduction + +> 💡 +> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Overview + +The `reduction` middleware is used to control the token count occupied by tool results, providing two strategies: + +1. **Truncation**: Immediately truncate overly long outputs when a tool returns, saving the complete content to Backend +2. **Clear**: When total tokens exceed the threshold, store old tool results to the file system + +--- + +## Architecture + +``` +Tool call returns result + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ WrapInvokableToolCall / WrapStreamableToolCall │ +│ │ +│ Truncation strategy (can be skipped) │ +│ Result length > MaxLengthForTrunc? │ +│ Yes → Truncate content, save full content to Backend │ +│ No → Return as-is │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ + Result added to Messages + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ BeforeModelRewriteState │ +│ │ +│ Clear strategy (can be skipped) │ +│ Total tokens > MaxTokensForClear? │ +│ Yes → Store old tool results to Backend, replace with │ +│ file paths │ +│ No → Do nothing │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ + Call Model +``` + +--- + +## Configuration + +### Config Main Configuration + +```go +type Config struct { + // Backend storage backend for saving truncated/cleared content + // Required when SkipTruncation is false + Backend Backend + + // SkipTruncation skip the truncation phase + SkipTruncation bool + + // SkipClear skip the clear phase + SkipClear bool + + // ReadFileToolName name of the tool for reading files + // After content is offloaded to a file, the agent needs this tool to read it + // Default "read_file" + ReadFileToolName string + + // RootDir root directory for saving content + // Default "/tmp" + // Truncated content saved to {RootDir}/trunc/{tool_call_id} + // Cleared content saved to {RootDir}/clear/{tool_call_id} + RootDir string + + // MaxLengthForTrunc maximum length to trigger truncation + // Default 50000 + MaxLengthForTrunc int + + // TokenCounter token counter + // Used to determine if clearing needs to be triggered + // Default uses character_count/4 estimation + TokenCounter func(ctx context.Context, msg []adk.Message, tools []*schema.ToolInfo) (int64, error) + + // MaxTokensForClear token threshold to trigger clearing + // Default 30000 + MaxTokensForClear int64 + + // ClearRetentionSuffixLimit how many recent conversation rounds to keep without clearing + // Default 1 + ClearRetentionSuffixLimit int + + // ClearPostProcess callback after clearing completes + // Can be used to save or notify current state + ClearPostProcess func(ctx context.Context, state *adk.ChatModelAgentState) context.Context + + // ToolConfig configuration for specific tools + // Takes precedence over global configuration + ToolConfig map[string]*ToolReductionConfig +} +``` + +### ToolReductionConfig Tool-level Configuration + +```go +type ToolReductionConfig struct { + // Backend storage backend for this tool + Backend Backend + + // SkipTruncation skip truncation for this tool + SkipTruncation bool + + // TruncHandler custom truncation handler + // Uses default handler if not set + TruncHandler func(ctx context.Context, detail *ToolDetail) (*TruncResult, error) + + // SkipClear skip clearing for this tool + SkipClear bool + + // ClearHandler custom clear handler + // Uses default handler if not set + ClearHandler func(ctx context.Context, detail *ToolDetail) (*ClearResult, error) +} +``` + +### ToolDetail Tool Details + +```go +type ToolDetail struct { + // ToolContext tool metadata (tool name, call ID) + ToolContext *adk.ToolContext + + // ToolArgument input parameters + ToolArgument *schema.ToolArgument + + // ToolResult output result + ToolResult *schema.ToolResult +} +``` + +### TruncResult Truncation Result + +```go +type TruncResult struct { + // NeedTrunc whether truncation is needed + NeedTrunc bool + + // ToolResult tool result after truncation + // Required when NeedTrunc is true + ToolResult *schema.ToolResult + + // NeedOffload whether offloading to storage is needed + NeedOffload bool + + // OffloadFilePath offload file path + // Required when NeedOffload is true + OffloadFilePath string + + // OffloadContent offload content + // Required when NeedOffload is true + OffloadContent string +} +``` + +### ClearResult Clear Result + +```go +type ClearResult struct { + // NeedClear whether clearing is needed + NeedClear bool + + // ToolArgument tool argument after clearing + // Required when NeedClear is true + ToolArgument *schema.ToolArgument + + // ToolResult tool result after clearing + // Required when NeedClear is true + ToolResult *schema.ToolResult + + // NeedOffload whether offloading to storage is needed + NeedOffload bool + + // OffloadFilePath offload file path + // Required when NeedOffload is true + OffloadFilePath string + + // OffloadContent offload content + // Required when NeedOffload is true + OffloadContent string +} +``` + +--- + +## Creating the Middleware + +### Basic Usage + +```go +import ( + "context" + "github.com/cloudwego/eino/adk/middlewares/reduction" +) + +// Use default configuration +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, // Required: storage backend +}) + +// Use with ChatModelAgent +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### Custom Configuration + +```go +config := &reduction.Config{ + Backend: myBackend, + RootDir: "/data/agent", + MaxLengthForTrunc: 30000, + MaxTokensForClear: 100000, + ClearRetentionSuffixLimit: 2, + TokenCounter: myTokenCounter, + ClearPostProcess: func(ctx context.Context, state *adk.ChatModelAgentState) context.Context { + log.Printf("Clear completed, messages: %d", len(state.Messages)) + return ctx + }, + ToolConfig: map[string]*reduction.ToolReductionConfig{ + "grep": { + Backend: grepBackend, + SkipTruncation: false, + }, + "read_file": { + Backend: readFileBackend, + SkipClear: true, // Read file tool doesn't need clearing + }, + }, +} + +middleware, err := reduction.New(ctx, config) +``` + +### Using Truncation Strategy Only + +```go +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, + SkipClear: true, // Skip clear phase +}) +``` + +### Using Clear Strategy Only + +```go +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, + SkipTruncation: true, // Skip truncation phase +}) +``` + +--- + +## How It Works + +### Truncation + +Handled in `WrapInvokableToolCall` / `WrapStreamableToolCall`: + +1. Tool returns result +2. Call TruncHandler to determine if truncation is needed +3. If truncation needed, save full content to Backend +4. Return truncated content with hint text telling the agent where to find the full content + +### Clear + +Handled in `BeforeModelRewriteState`: + +1. Use TokenCounter to calculate total tokens +2. Only process if exceeds MaxTokensForClear +3. Iterate from old messages, skipping already processed ones and the most recent ClearRetentionSuffixLimit rounds +4. For each tool call in range, call ClearHandler +5. If clearing needed, write to Backend and replace message result with file path +6. Call ClearPostProcess callback + +--- + +## Multi-language Support + +Truncation and clear hint text supports Chinese and English, switch via `adk.SetLanguage()`: + +```go +adk.SetLanguage(adk.LanguageChinese) // Chinese +adk.SetLanguage(adk.LanguageEnglish) // English (default) +``` + +--- + +## Notes + +- When `SkipTruncation` is false, `Backend` must be set +- The default TokenCounter uses `character_count / 4` estimation, which is not accurate for Chinese; consider using `github.com/tiktoken-go/tokenizer` as a replacement +- Already processed messages are marked and won't be processed again +- Configuration in `ToolConfig` takes precedence over global configuration diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md new file mode 100644 index 00000000000..4b7bc301c2d --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md @@ -0,0 +1,146 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: ToolSearch' +weight: 5 +--- + +# ToolSearch Middleware + +adk/middlewares/dynamictool/toolsearch + +> 💡 +> This middleware was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Overview + +The `toolsearch` middleware implements dynamic tool selection. When the tool library is large, passing all tools to the model would overflow the context. This middleware's approach is: + +1. Add a `tool_search` meta-tool that accepts regex patterns to search tool names +2. Initially hide all dynamic tools +3. After the model calls `tool_search`, matched tools become available in subsequent calls + +--- + +## Architecture + +``` +Agent initialization + │ + ▼ +┌───────────────────────────────────────────┐ +│ BeforeAgent │ +│ - Inject tool_search tool │ +│ - Add DynamicTools to Tools list │ +└───────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────┐ +│ WrapModel │ +│ Before each Model call: │ +│ 1. Scan message history to find all tool_search return results │ +│ 2. Full Tools minus unselected DynamicTools = tools for this Model │ +│ call │ +└────────────────────────────────────────────┘ + │ + ▼ + Model call +``` + +--- + +## Configuration + +```go +type Config struct { + // Tools that can be dynamically searched and loaded + DynamicTools []tool.BaseTool +} +``` + +--- + +## tool_search Tool + +The tool injected by the middleware. + +**Parameters:** + + + + +
    ParameterTypeRequiredDescription
    regex_pattern
    stringYesRegex pattern to match tool names
    + +**Returns:** + +```json +{ + "selectedTools": ["tool_a", "tool_b"] +} +``` + +--- + +## Usage Example + +```go +middleware, err := toolsearch.New(ctx, &toolsearch.Config{ + DynamicTools: []tool.BaseTool{ + weatherTool, + stockTool, + currencyTool, + // ... many tools + }, +}) +if err != nil { + return err +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: myModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +--- + +## How It Works + +### BeforeAgent + +1. Get all DynamicTools +2. Create `tool_search` tool using DynamicTools +3. Add `tool_search` and all DynamicTools to `runCtx.Tools`, at this point Agent has full Tools + +### WrapModel + +Before each Model call: + +1. Iterate through message history to find all `tool_search` return results +2. Collect selected tool names +3. Filter out unselected DynamicTools from full tools +4. Call Model with filtered tool list + +### Tool Selection Flow + +``` +Round 1: + Model can only see tool_search + Model calls tool_search(regex_pattern="weather.*") + Returns {"selectedTools": ["weather_forecast", "weather_history"]} + +Round 2: + Model can see tool_search + weather_forecast + weather_history + Model calls weather_forecast(...) +``` + +--- + +## Notes + +- DynamicTools cannot be empty +- Regex matches tool names, not descriptions +- Selected tools remain available unless the tool_search call result is deleted or modified +- tool_search can be called multiple times, results accumulate diff --git a/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md new file mode 100644 index 00000000000..9a86ea95204 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md @@ -0,0 +1,528 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino ADK: ChatModelAgentMiddleware' +weight: 8 +--- + +## Overview + +## ChatModelAgentMiddleware Interface + +`ChatModelAgentMiddleware` defines the interface for customizing `ChatModelAgent` behavior. + +**Important:** This interface is designed specifically for `ChatModelAgent` and Agents built on top of it (such as `DeepAgent`). + +> 💡 +> The ChatModelAgentMiddleware interface was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) + +### Why Use ChatModelAgentMiddleware Instead of AgentMiddleware? + + + + + + +
    FeatureAgentMiddleware (struct)ChatModelAgentMiddleware (interface)
    ExtensibilityClosed, users cannot add new methodsOpen, users can implement custom handlers
    Context PropagationCallbacks only return errorAll methods return
    (context.Context, ..., error)
    Configuration ManagementScattered in closuresCentralized in struct fields
    + +### Interface Definition + +```go +type ChatModelAgentMiddleware interface { + // BeforeAgent is called before each agent run, allows modifying instruction and tools configuration + BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) + + // BeforeModelRewriteState is called before each model call + // The returned state will be persisted to the agent's internal state and passed to the model + // The returned context will be propagated to the model call and subsequent handlers + BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + + // AfterModelRewriteState is called after each model call + // The input state contains the model response as the last message + AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + + // WrapInvokableToolCall wraps the synchronous execution of a tool with custom behavior + // If no wrapping is needed, return the original endpoint and nil error + // Only called for tools that implement InvokableTool + WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error) + + // WrapStreamableToolCall wraps the streaming execution of a tool with custom behavior + // If no wrapping is needed, return the original endpoint and nil error + // Only called for tools that implement StreamableTool + WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error) + + // WrapEnhancedInvokableToolCall wraps the synchronous execution of an enhanced tool with custom behavior + WrapEnhancedInvokableToolCall(ctx context.Context, endpoint EnhancedInvokableToolCallEndpoint, tCtx *ToolContext) (EnhancedInvokableToolCallEndpoint, error) + + // WrapEnhancedStreamableToolCall wraps the streaming execution of an enhanced tool with custom behavior + WrapEnhancedStreamableToolCall(ctx context.Context, endpoint EnhancedStreamableToolCallEndpoint, tCtx *ToolContext) (EnhancedStreamableToolCallEndpoint, error) + + // WrapModel wraps the chat model with custom behavior + // If no wrapping is needed, return the original model and nil error + // Called at request time, executed before each model call + WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) +} +``` + +### Using BaseChatModelAgentMiddleware + +Embed `*BaseChatModelAgentMiddleware` to get default no-op implementations: + +```go +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) BeforeModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + return ctx, state, nil +} +``` + +--- + +## Tool Call Endpoint Types + +Tool wrapping uses function types instead of interfaces, more clearly expressing the wrapping intent: + +```go +// InvokableToolCallEndpoint is the function signature for synchronous tool calls +type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) + +// StreamableToolCallEndpoint is the function signature for streaming tool calls +type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error) + +// EnhancedInvokableToolCallEndpoint is the function signature for enhanced synchronous tool calls +type EnhancedInvokableToolCallEndpoint func(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.ToolResult, error) + +// EnhancedStreamableToolCallEndpoint is the function signature for enhanced streaming tool calls +type EnhancedStreamableToolCallEndpoint func(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.StreamReader[*schema.ToolResult], error) +``` + +### Why Use Separate Endpoint Types? + +The previous `ToolCall` interface contained both `InvokableRun` and `StreamableRun`, but most tools only implement one of them. +Separate endpoint types enable: + +- Corresponding wrap methods are only called when the tool implements the respective interface +- Clearer contract for wrapper authors +- No ambiguity about which method to implement + +--- + +## ChatModelAgentContext + +`ChatModelAgentContext` contains runtime information passed to handlers before each `ChatModelAgent` run. + +```go +type ChatModelAgentContext struct { + // Instruction is the instruction for the current Agent execution + // Includes agent-configured instructions, framework and AgentMiddleware appended extra instructions, + // and modifications applied by previous BeforeAgent handlers + Instruction string + + // Tools are the original tools (without any wrappers or tool middleware) currently configured for Agent execution + // Includes tools passed in AgentConfig, tools implicitly added by the framework (like transfer/exit tools), + // and other tools added by middleware + Tools []tool.BaseTool + + // ReturnDirectly is the set of tool names currently configured to make the Agent return directly + ReturnDirectly map[string]bool +} +``` + +--- + +## ChatModelAgentState + +`ChatModelAgentState` represents the state of the chat model agent during conversation. This is the primary state type for `ChatModelAgentMiddleware` and `AgentMiddleware` callbacks. + +```go +type ChatModelAgentState struct { + // Messages contains all messages in the current conversation session + Messages []Message +} +``` + +--- + +## ToolContext + +`ToolContext` provides metadata about the tool being wrapped. Created at request time, contains information about the current tool call. + +```go +type ToolContext struct { + // Name is the tool name + Name string + + // CallID is the unique identifier for this specific tool call + CallID string +} +``` + +### Usage Example: Tool Call Wrapping + +```go +func (h *MyHandler) WrapInvokableToolCall(ctx context.Context, endpoint adk.InvokableToolCallEndpoint, tCtx *adk.ToolContext) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + log.Printf("Tool %s (call %s) starting with args: %s", tCtx.Name, tCtx.CallID, argumentsInJSON) + + result, err := endpoint(ctx, argumentsInJSON, opts...) + + if err != nil { + log.Printf("Tool %s failed: %v", tCtx.Name, err) + return "", err + } + + log.Printf("Tool %s completed with result: %s", tCtx.Name, result) + return result, nil + }, nil +} +``` + +--- + +## ModelContext + +`ModelContext` contains context information passed to `WrapModel`. Created at request time, contains tool configuration for the current model call. + +```go +type ModelContext struct { + // Tools is the list of tools currently configured for the agent + // Populated at request time, contains the tools that will be sent to the model + Tools []*schema.ToolInfo + + // ModelRetryConfig contains the retry configuration for the model + // Populated at request time from the agent's ModelRetryConfig + // Used by EventSenderModelWrapper to appropriately wrap stream errors + ModelRetryConfig *ModelRetryConfig +} +``` + +### Usage Example: Model Wrapping + +```go +func (h *MyHandler) WrapModel(ctx context.Context, m model.BaseChatModel, mc *adk.ModelContext) (model.BaseChatModel, error) { + return &myModelWrapper{ + inner: m, + tools: mc.Tools, + }, nil +} + +type myModelWrapper struct { + inner model.BaseChatModel + tools []*schema.ToolInfo +} + +func (w *myModelWrapper) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) { + log.Printf("Model called with %d tools", len(w.tools)) + return w.inner.Generate(ctx, msgs, opts...) +} + +func (w *myModelWrapper) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { + return w.inner.Stream(ctx, msgs, opts...) +} +``` + +--- + +## Run-Local Storage API + +`SetRunLocalValue`, `GetRunLocalValue`, and `DeleteRunLocalValue` provide the ability to store, retrieve, and delete values during the current agent Run() call. + +```go +// SetRunLocalValue sets a key-value pair that persists during the current agent Run() call +// The value is scoped to this specific execution and is not shared between different Run() calls or agent instances +// +// Values stored here are compatible with interrupt/resume cycles - they are serialized and restored when the agent resumes +// For custom types, they must be registered in init() using schema.RegisterName[T]() to ensure proper serialization +// +// This function can only be called from within a ChatModelAgentMiddleware during agent execution +// Returns an error if called outside of agent execution context +func SetRunLocalValue(ctx context.Context, key string, value any) error + +// GetRunLocalValue retrieves a value set during the current agent Run() call +// The value is scoped to this specific execution and is not shared between different Run() calls or agent instances +// +// Values stored via SetRunLocalValue are compatible with interrupt/resume cycles - they are serialized and restored when the agent resumes +// For custom types, they must be registered in init() using schema.RegisterName[T]() to ensure proper serialization +// +// This function can only be called from within a ChatModelAgentMiddleware during agent execution +// Returns (value, true, nil) if found, (nil, false, nil) if not found, +// returns error if called outside of agent execution context +func GetRunLocalValue(ctx context.Context, key string) (any, bool, error) + +// DeleteRunLocalValue deletes a value set during the current agent Run() call +// +// This function can only be called from within a ChatModelAgentMiddleware during agent execution +// Returns an error if called outside of agent execution context +func DeleteRunLocalValue(ctx context.Context, key string) error +``` + +### Usage Example: Sharing Data Across Handler Points + +```go +func init() { + schema.RegisterName[*MyCustomData]("my_package.MyCustomData") +} + +type MyCustomData struct { + Count int + Name string +} + +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) WrapInvokableToolCall(ctx context.Context, endpoint adk.InvokableToolCallEndpoint, tCtx *adk.ToolContext) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + result, err := endpoint(ctx, argumentsInJSON, opts...) + + data := &MyCustomData{Count: 1, Name: tCtx.Name} + if err := adk.SetRunLocalValue(ctx, "my_handler.last_tool", data); err != nil { + log.Printf("Failed to set run local value: %v", err) + } + + return result, err + }, nil +} + +func (h *MyHandler) AfterModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + if val, found, err := adk.GetRunLocalValue(ctx, "my_handler.last_tool"); err == nil && found { + if data, ok := val.(*MyCustomData); ok { + log.Printf("Last tool was: %s (count: %d)", data.Name, data.Count) + } + } + return ctx, state, nil +} +``` + +--- + +## SendEvent API + +`SendEvent` allows sending custom `AgentEvent` to the event stream during agent execution. + +```go +// SendEvent sends a custom AgentEvent to the event stream during agent execution +// Allows ChatModelAgentMiddleware implementations to emit custom events, +// which will be received by callers iterating over the agent's event stream +// +// This function can only be called from within a ChatModelAgentMiddleware during agent execution +// Returns an error if called outside of agent execution context +func SendEvent(ctx context.Context, event *AgentEvent) error +``` + +--- + +## State Type (To Be Deprecated) + +`State` holds agent runtime state, including messages and user-extensible storage. + +**⚠️ Deprecation Warning:** This type will be made unexported in v1.0.0. Please use `ChatModelAgentState` in `ChatModelAgentMiddleware` and `AgentMiddleware` callbacks. Direct use of `compose.ProcessState[*State]` is not recommended and will stop working in v1.0.0; please use the handler API instead. + +```go +type State struct { + Messages []Message + extra map[string]any // unexported, access via SetRunLocalValue/GetRunLocalValue + + // The following are internal fields - do not access directly + // Kept exported for backward compatibility with existing checkpoints + ReturnDirectlyToolCallID string + ToolGenActions map[string]*AgentAction + AgentName string + RemainingIterations int + + internals map[string]any +} +``` + +--- + +## Architecture Diagram + +The following diagram shows how `ChatModelAgentMiddleware` works during `ChatModelAgent` execution: + +``` +Agent.Run(input) + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ BeforeAgent(ctx, *ChatModelAgentContext) │ +│ Input: Current Instruction, Tools and other Agent runtime env │ +│ Output: Modified Agent runtime env │ +│ Purpose: Called once at Run start, modifies config for entire Run │ +│ lifecycle │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ ReAct Loop │ +│ ┌───────────────────────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ BeforeModelRewriteState(ctx, *ChatModelAgentState, *MC) │ │ │ +│ │ │ Input: Persistent state like message history, plus Model │ │ │ +│ │ │ runtime env │ │ │ +│ │ │ Output: Modified persistent state, returns new ctx │ │ │ +│ │ │ Purpose: Modify persistent state across iterations │ │ │ +│ │ │ (mainly message list) │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ WrapModel(ctx, BaseChatModel, *ModelContext) │ │ │ +│ │ │ Input: ChatModel being wrapped, plus Model runtime env │ │ │ +│ │ │ Output: Wrapped Model (onion model) │ │ │ +│ │ │ Purpose: Modify input, output and config for single │ │ │ +│ │ │ Model request │ │ │ +│ │ │ │ │ │ │ +│ │ │ ▼ │ │ │ +│ │ │ ┌───────────────┐ │ │ │ +│ │ │ │ Model │ │ │ │ +│ │ │ │ Generate/Stream│ │ │ │ +│ │ │ └───────────────┘ │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ AfterModelRewriteState(ctx, *ChatModelAgentState, *MC) │ │ │ +│ │ │ Input: Persistent state like message history (with Model │ │ │ +│ │ │ response), plus Model runtime env │ │ │ +│ │ │ Output: Modified persistent state │ │ │ +│ │ │ Purpose: Modify persistent state across iterations │ │ │ +│ │ │ (mainly message list) │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌──────────────────┐ │ │ +│ │ │ Model return? │ │ │ +│ │ └──────────────────┘ │ │ +│ │ │ │ │ │ +│ │ Final response│ │ ToolCalls │ │ +│ │ │ ▼ │ │ +│ │ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ │ WrapInvokableToolCall / WrapStream │ │ │ +│ │ │ │ ableToolCall(ctx, endpoint, *TC) │ │ │ +│ │ │ │ Input: Tool being wrapped plus │ │ │ +│ │ │ │ Tool runtime env │ │ │ +│ │ │ │ Output: Wrapped endpoint │ │ │ +│ │ │ │ (onion model) │ │ │ +│ │ │ │ Purpose: Modify input, output │ │ │ +│ │ │ │ and config for single │ │ │ +│ │ │ │ Tool request │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ ▼ │ │ │ +│ │ │ │ ┌─────────────┐ │ │ │ +│ │ │ │ │ Tool.Run() │ │ │ │ +│ │ │ │ └─────────────┘ │ │ │ +│ │ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ │ │ +│ │ │ │ (Result added to Messages) │ │ +│ │ │ │ │ │ +│ │ │ ┌─────────┘ │ │ +│ │ │ │ │ │ +│ │ │ └──────────► Continue loop │ │ +│ │ │ │ │ +│ └─────────────────────┼─────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Loop until complete or maxIterations reached │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ + Agent.Run() ends +``` + +### Handler Method Description + + + + + + + + + +
    MethodInputOutputScope
    BeforeAgent
    Agent runtime env (
    *ChatModelAgentContext
    )
    Modified Agent runtime envEntire Run lifecycle, called only once
    BeforeModelRewriteState
    Persistent state + Model runtime envModified persistent statePersistent state across iterations (message list)
    WrapModel
    ChatModel being wrapped + Model runtime envWrapped ModelSingle Model request input, output and config
    AfterModelRewriteState
    Persistent state (with response) + Model runtime envModified persistent statePersistent state across iterations (message list)
    WrapInvokableToolCall
    Tool being wrapped + Tool runtime envWrapped endpointSingle Tool request input, output and config
    WrapStreamableToolCall
    Tool being wrapped + Tool runtime envWrapped endpointSingle Tool request input, output and config
    + +--- + +## Execution Order + +### Model Call Lifecycle (wrapper chain from outer to inner) + +1. `AgentMiddleware.BeforeChatModel` (hook, runs before model call) +2. `ChatModelAgentMiddleware.BeforeModelRewriteState` (hook, can modify state before model call) +3. `retryModelWrapper` (internal - retries on failure, if configured) +4. `eventSenderModelWrapper` preprocessing (internal - prepares event sending) +5. `ChatModelAgentMiddleware.WrapModel` preprocessing (wrapper, wrapped at request time, first registered runs first) +6. `callbackInjectionModelWrapper` (internal - injects callbacks if not enabled) +7. `Model.Generate/Stream` +8. `callbackInjectionModelWrapper` postprocessing +9. `ChatModelAgentMiddleware.WrapModel` postprocessing (wrapper, first registered runs last) +10. `eventSenderModelWrapper` postprocessing (internal - sends model response event) +11. `retryModelWrapper` postprocessing (internal - handles retry logic) +12. `ChatModelAgentMiddleware.AfterModelRewriteState` (hook, can modify state after model call) +13. `AgentMiddleware.AfterChatModel` (hook, runs after model call) + +### Tool Call Lifecycle (from outer to inner) + +1. `eventSenderToolHandler` (internal ToolMiddleware - sends tool result event after all processing) +2. `ToolsConfig.ToolCallMiddlewares` (ToolMiddleware) +3. `AgentMiddleware.WrapToolCall` (ToolMiddleware) +4. `ChatModelAgentMiddleware.WrapInvokableToolCall/WrapStreamableToolCall` (wrapped at request time, first registered is outermost) +5. `Tool.InvokableRun/StreamableRun` + +--- + +## Migration Guide + +### Migrating from AgentMiddleware to ChatModelAgentMiddleware + +**Before (AgentMiddleware):** + +```go +middleware := adk.AgentMiddleware{ + BeforeChatModel: func(ctx context.Context, state *adk.ChatModelAgentState) error { + return nil + }, +} +``` + +**After (ChatModelAgentMiddleware):** + +```go +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) BeforeModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + newCtx := context.WithValue(ctx, myKey, myValue) + return newCtx, state, nil +} +``` + +### Migrating from compose.ProcessState[*State] + +**Before:** + +```go +compose.ProcessState(ctx, func(_ context.Context, st *adk.State) error { + st.Extra["myKey"] = myValue + return nil +}) +``` + +**After (using SetRunLocalValue/GetRunLocalValue):** + +```go +if err := adk.SetRunLocalValue(ctx, "myKey", myValue); err != nil { + return ctx, state, err +} + +if val, found, err := adk.GetRunLocalValue(ctx, "myKey"); err == nil && found { +} +``` diff --git a/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md b/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md new file mode 100644 index 00000000000..c75581d8489 --- /dev/null +++ b/content/en/docs/eino/core_modules/eino_adk/adk_agent_callback.md @@ -0,0 +1,345 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino ADK: Agent Callback' +weight: 9 +--- + +This feature adds Callback support to ADK Agents, similar to the callback mechanism in the compose package. Through callbacks, users can observe the Agent's execution lifecycle for logging, tracing, monitoring, and other purposes. + +## Overview + +The ADK Agent Callback mechanism shares the same infrastructure as the callback system in Eino compose: + +- Uses the same `callbacks.Handler` interface +- Uses the same `callbacks.RunInfo` structure +- Can be combined with other component callbacks (such as ChatModel, Tool, etc.) + +> 💡 +> Through Agent Callback, you can hook into key points of Agent execution to implement observability capabilities like tracing, logging, and metrics. This feature was introduced in [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1). + +## Core Types + +### ComponentOfAgent + +Component type identifier used to identify Agent-related events in callbacks: + +```go +const ComponentOfAgent components.Component = "Agent" +``` + +Used in `callbacks.RunInfo.Component` to filter callback events related only to Agents. + +### AgentCallbackInput + +Input type for Agent callbacks, passed in the `OnStart` callback: + +```go +type AgentCallbackInput struct { + // Input contains the Agent input for new runs. Nil when resuming execution. + Input *AgentInput + // ResumeInfo contains information when resuming from an interrupt. Nil for new runs. + ResumeInfo *ResumeInfo +} +``` + + + + + +
    Call MethodField Values
    Agent.Run()
    Input
    field has value,
    ResumeInfo
    is nil
    Agent.Resume()
    ResumeInfo
    field has value,
    Input
    is nil
    + +### AgentCallbackOutput + +Output type for Agent callbacks, passed in the `OnEnd` callback: + +```go +type AgentCallbackOutput struct { + // Events provides the Agent event stream. Each handler receives an independent copy. + Events *AsyncIterator[*AgentEvent] +} +``` + +> 💡 +> **Important**: The `Events` iterator should be consumed **asynchronously** to avoid blocking Agent execution. Each callback handler receives an independent copy of the event stream without interfering with each other. + +## API Usage + +### WithCallbacks + +Run option to add callback handlers to receive Agent lifecycle events: + +```go +func WithCallbacks(handlers ...callbacks.Handler) AgentRunOption +``` + +### Type Conversion Functions + +Convert generic callback types to Agent-specific types: + +```go +// Convert input type +func ConvAgentCallbackInput(input callbacks.CallbackInput) *AgentCallbackInput + +// Convert output type +func ConvAgentCallbackOutput(output callbacks.CallbackOutput) *AgentCallbackOutput +``` + +Functions return nil if the type doesn't match. + +## Usage Examples + +### Method 1: Using HandlerBuilder + +Use `callbacks.NewHandlerBuilder()` to build a generic callback handler: + +```go +import ( + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/callbacks" +) + +handler := callbacks.NewHandlerBuilder(). + OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + if info.Component == adk.ComponentOfAgent { + agentInput := adk.ConvAgentCallbackInput(input) + if agentInput.Input != nil { + fmt.Printf("Agent %s started with new run\n", info.Name) + } else { + fmt.Printf("Agent %s resumed from interrupt\n", info.Name) + } + } + return ctx + }). + OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + if info.Component == adk.ComponentOfAgent { + agentOutput := adk.ConvAgentCallbackOutput(output) + // Consume event stream asynchronously + go func() { + for { + event, ok := agentOutput.Events.Next() + if !ok { + break + } + // Process event... + fmt.Printf("Event from %s: %+v\n", event.AgentName, event) + } + }() + } + return ctx + }). + Build() + +iter := agent.Run(ctx, input, adk.WithCallbacks(handler)) +``` + +### Method 2: Using HandlerHelper (Recommended) + +Using `template.HandlerHelper` makes type conversion more convenient: + +```go +import ( + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/callbacks" + template "github.com/cloudwego/eino/utils/callbacks" +) + +helper := template.NewHandlerHelper(). + Agent(&template.AgentCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *adk.AgentCallbackInput) context.Context { + if input.Input != nil { + fmt.Printf("Agent %s started with input\n", info.Name) + } else { + fmt.Printf("Agent %s resumed\n", info.Name) + } + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + // Consume events asynchronously + go func() { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // Process event... + } + }() + return ctx + }, + }). + Handler() + +iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +``` + +> 💡 +> `HandlerHelper` automatically performs type conversion, making the code more concise. It also supports combining callback handlers for multiple components. + +## Tracing Application + +The most common use case for Agent Callback is implementing distributed tracing. Here's an example using OpenTelemetry for tracing: + +```go +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +tracer := otel.Tracer("my-agent-tracer") + +handler := callbacks.NewHandlerBuilder(). + OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + // Create span + ctx, span := tracer.Start(ctx, info.Name, + trace.WithAttributes( + attribute.String("component", string(info.Component)), + attribute.String("type", info.Type), + )) + + // Agent-specific attributes + if info.Component == adk.ComponentOfAgent { + agentInput := adk.ConvAgentCallbackInput(input) + if agentInput != nil && agentInput.Input != nil { + span.SetAttributes(attribute.Bool("is_new_run", true)) + } else { + span.SetAttributes(attribute.Bool("is_resume", true)) + } + } + + return ctx + }). + OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + span := trace.SpanFromContext(ctx) + span.End() + return ctx + }). + OnErrorFn(func(ctx context.Context, info *callbacks.RunInfo, err error) context.Context { + span := trace.SpanFromContext(ctx) + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + span.End() + return ctx + }). + Build() +``` + +### Combining with compose Callbacks + +Since ADK Agent callbacks share the same infrastructure as compose callbacks, you can use the same handler to process callbacks from both Agents and other components (like ChatModel, Tool): + +```go +helper := template.NewHandlerHelper(). + // Agent callback + Agent(&template.AgentCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *adk.AgentCallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "agent:"+info.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + // ChatModel callback + ChatModel(&template.ModelCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *model.CallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "model:"+info.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *model.CallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + // Tool callback + Tool(&template.ToolCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "tool:"+input.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + Handler() + +// Use combined handler +iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +``` + +> 💡 +> **Tip**: For the cozeloop ADK trace version, see [https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1](https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1) + +## Agent Type Identifiers + +Built-in Agents implement the `components.Typer` interface, returning their type identifier, which populates the `callbacks.RunInfo.Type` field: + + + + + + + + +
    Agent TypeGetType() Return Value
    ChatModelAgent
    "ChatModel"
    workflowAgent (Sequential)
    "Sequential"
    workflowAgent (Parallel)
    "Parallel"
    workflowAgent (Loop)
    "Loop"
    DeterministicTransfer Agent
    "DeterministicTransfer"
    + +## Callback Behavior + +### Callback Invocation Timing + + +
    +Run Method1. Initialize callback context2. Process input3. Call
    OnStart
    4. Execute Agent logic5. Register
    OnEnd
    (when event stream is created)
    +Resume Method1. Build ResumeInfo2. Initialize callback context3. Call
    OnStart
    4. Resume Agent execution5. Register
    OnEnd
    (when event stream is created)
    + +### OnEnd Invocation Timing + +The `OnEnd` callback is registered **when the iterator is created**, not when the generator closes. This allows handlers to consume events while the stream is being transmitted. + +## Notes + +### 1. Consume Event Stream Asynchronously + +The `AgentCallbackOutput.Events` in callback handlers **must** be consumed asynchronously, otherwise it will block Agent execution: + +```go +// ✅ Correct +OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + go func() { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // Process event + } + }() + return ctx +} + +// ❌ Wrong - will cause deadlock +OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // Process event + } + return ctx +} +``` + +### 2. No OnError Callback + +Since `Agent.Run()` and `Agent.Resume()` method signatures don't return errors, Agent callbacks **do not support** `OnError`. Error information is passed through the `AgentEvent.Err` field in the event stream. + +### 3. Event Stream Copying Mechanism + +When there are multiple callback handlers, each handler receives an independent copy of the event stream without interfering with each other. The last handler receives the original events to reduce memory allocation. 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 index dbd84e4e400..5c012b3651f 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_collaboration.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_collaboration.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino ADK: Agent Collaboration' @@ -9,70 +9,70 @@ weight: 4 # Agent Collaboration -The overview document has provided basic explanations for Agent collaboration. Below, we will introduce the design and implementation of collaboration and composition primitives with code: +The overview document has provided basic explanations of Agent collaboration. Below we will introduce the design and implementation of collaboration and composition primitives in combination with code: ## Collaboration Primitives -### Agent Collaboration Modes +### Inter-Agent Collaboration Methods - - - + + +
    ModeDescription
    TransferDirectly transfer the task to another Agent. The current Agent exits after execution and does not care about the task execution status of the transferred Agent
    ToolCall(AgentAsTool)Treat an Agent as a ToolCall, wait for the Agent's response, and obtain the output result of the called Agent for the next round of processing
    Collaboration MethodDescription
    TransferDirectly transfers the task to another Agent. The current Agent exits after execution completes, without concern for the task execution status of the transferred Agent
    ToolCall (AgentAsTool)Invokes the Agent as a ToolCall, waits for the Agent's response, and can obtain the output result of the called Agent for the next round of processing
    ### AgentInput Context Strategies - - + +
    Context StrategyDescription
    Upstream Agent Full DialogueGet the complete conversation history of the upstream Agent
    New Task DescriptionIgnore the complete conversation history of the upstream Agent and provide a new task summary as the AgentInput for the sub-Agent
    Upstream Agent Full ConversationGets the complete conversation history of the current Agent's upstream Agent
    Fresh Task DescriptionIgnores the complete conversation history of the upstream Agent and provides a completely new task summary as the AgentInput for the sub-Agent
    ### Decision Autonomy - - + +
    Decision AutonomyDescription
    Autonomous DecisionWithin the Agent, based on its available downstream Agents, autonomously select a downstream Agent for assistance when needed. Generally, the Agent makes decisions based on LLM internally, but even if selection is based on preset logic, it is still considered autonomous decision from the Agent's external perspective
    Preset DecisionPre-set the next Agent after an Agent executes a task. The execution order of Agents is predetermined and predictable
    Autonomous DecisionWithin the Agent, based on its available downstream Agents, when assistance is needed, it autonomously selects a downstream Agent for assistance. Generally, the Agent makes decisions based on LLM internally, but even if selection is based on preset logic, it is still considered autonomous decision from the Agent's external perspective
    Preset DecisionThe next Agent after an Agent executes a task is predetermined. The execution order of Agents is predetermined and predictable
    ### Composition Primitives - - - - - - + + + + + +
    TypeDescriptionRunning ModeCollaboration ModeContext StrategyDecision Autonomy
    SubAgentsCombine the user-provided agent as the parent Agent and the user-provided subAgents list as child Agents to form an Agent capable of autonomous decision-making, where Name and Description serve as the Agent's name identifier and description.
  • Currently limited to one Agent having only one parent Agent
  • Use the SetSubAgents function to build a "multi-branch tree" form of Multi-Agent
  • In this "multi-branch tree", AgentName must remain unique
  • TransferUpstream Agent Full DialogueAutonomous Decision
    SequentialCombine the user-provided SubAgents list into a Sequential Agent that executes in order, where Name and Description serve as the Sequential Agent's name identifier and description. When the Sequential Agent executes, it runs the SubAgents list in order until all Agents have been executed once.TransferUpstream Agent Full DialoguePreset Decision
    ParallelCombine the user-provided SubAgents list into a Parallel Agent that executes concurrently based on the same context, where Name and Description serve as the Parallel Agent's name identifier and description. When the Parallel Agent executes, it runs the SubAgents list concurrently and finishes after all Agents complete execution.TransferUpstream Agent Full DialoguePreset Decision
    LoopExecute the user-provided SubAgents list in array order, cycling repeatedly, to form a Loop Agent, where Name and Description serve as the Loop Agent's name identifier and description. When the Loop Agent executes, it runs the SubAgents list in order and finishes after all Agents complete execution.TransferUpstream Agent Full DialoguePreset Decision
    AgentAsToolConvert an Agent into a Tool to be used as a regular Tool by other Agents. Whether an Agent can call other Agents as Tools depends on its own implementation. The ChatModelAgent provided in adk supports the AgentAsTool functionalityToolCallNew Task DescriptionAutonomous Decision
    TypeDescriptionRunning ModeCollaboration MethodContext StrategyDecision Autonomy
    SubAgentsCombines the user-provided agent as the parent Agent and the user-provided subAgents list as child Agents to form an Agent capable of autonomous decision-making. The Name and Description serve as the Agent's name identifier and description.
  • Currently, an Agent can only have one parent Agent
  • Use the SetSubAgents function to build a "multi-tree" form of Multi-Agent
  • Within this "multi-tree", AgentName must remain unique
  • TransferUpstream Agent Full ConversationAutonomous Decision
    SequentialCombines the user-provided SubAgents list into a Sequential Agent that executes in order. The Name and Description serve as the Sequential Agent's name identifier and description. When the Sequential Agent executes, it runs the SubAgents list in order until all Agents have been executed.TransferUpstream Agent Full ConversationPreset Decision
    ParallelCombines the user-provided SubAgents list into a Parallel Agent that executes concurrently based on the same context. The Name and Description serve as the Parallel Agent's name identifier and description. When the Parallel Agent executes, it runs the SubAgents list concurrently and finishes when all Agents have completed.TransferUpstream Agent Full ConversationPreset Decision
    LoopExecutes the user-provided SubAgents list in array order sequentially and repeatedly, forming a Loop Agent. The Name and Description serve as the Loop Agent's name identifier and description. When the Loop Agent executes, it runs the SubAgents list in order and finishes when all Agents have completed.TransferUpstream Agent Full ConversationPreset Decision
    AgentAsToolConverts an Agent into a Tool to be used by other Agents as a regular Tool. Whether an Agent can call other Agents as Tools depends on its own implementation. The ChatModelAgent provided in ADK supports the AgentAsTool functionalityToolCallFresh Task DescriptionAutonomous Decision
    ## Context Passing -When building multi-Agent systems, efficient and accurate information sharing between different Agents is crucial. Eino ADK provides two core context passing mechanisms to meet different collaboration needs: History and SessionValues. +When building multi-Agent systems, efficient and accurate sharing of information between different Agents is crucial. Eino ADK provides two core context passing mechanisms to meet different collaboration needs: History and SessionValues. ### History #### Concept -History corresponds to the [Upstream Agent Full Dialogue Context Strategy]. Every AgentEvent produced by each Agent in a multi-Agent system is saved to History. When calling a new Agent (Workflow/Transfer), the AgentEvents in History are converted and appended to the AgentInput. +History corresponds to the [Upstream Agent Full Conversation context strategy]. Every AgentEvent produced by each Agent in a multi-Agent system is saved to History. When calling a new Agent (Workflow/Transfer), the AgentEvents in History are converted and concatenated into the AgentInput. -By default, Assistant or Tool Messages from other Agents are converted to User Messages. This is equivalent to telling the current LLM: "Just now, Agent_A called some_tool and returned some_result. Now it's your turn to decide." +By default, Assistant or Tool Messages from other Agents are converted to User Messages. This is equivalent to telling the current LLM: "Just now, Agent_A called some_tool and returned some_result. Now it's your turn to make a decision." Through this approach, other Agents' behaviors are treated as "external information" or "factual statements" provided to the current Agent, rather than its own behaviors, thus avoiding LLM context confusion. -In Eino ADK, when building AgentInput for an Agent, the History it can see is "all AgentEvents produced before me." +In Eino ADK, when building AgentInput for an Agent, the History it can see is "all AgentEvents produced before me". -It's worth mentioning ParallelWorkflowAgent: two parallel sub-Agents (A, B) cannot see each other's AgentEvents during parallel execution, because neither parallel A nor B comes before the other. +Worth mentioning is ParallelWorkflowAgent: two parallel sub-Agents (A, B) cannot see each other's AgentEvents during parallel execution because neither A nor B precedes the other. #### RunPath -Each AgentEvent in History is "produced by a specific Agent in a specific execution sequence", meaning AgentEvent has its own RunPath. The purpose of RunPath is to convey this information; it does not carry other functions in the eino framework. +Each AgentEvent in History is "produced by a specific Agent in a specific execution sequence", meaning AgentEvent has its own RunPath. The purpose of RunPath is to convey this information; it doesn't carry other functions in the eino framework. -The table below shows the specific RunPath for Agent execution in various orchestration modes: +The table below shows the specific RunPath when Agents execute under various orchestration modes: @@ -84,7 +84,7 @@ The table below shows the specific RunPath for Agent execution in various orches #### Customization -In some cases, the History content needs to be adjusted before Agent execution. At this time, you can use AgentWithOptions to customize how the Agent generates AgentInput from History: +In some cases, the History content needs to be adjusted before the Agent runs. At this point, you can customize how the Agent generates AgentInput from History using AgentWithOptions: ```go // github.com/cloudwego/eino/adk/flow.go @@ -98,9 +98,9 @@ func WithHistoryRewriter(h HistoryRewriter) AgentOption #### Concept -SessionValues is a global temporary KV storage that persists throughout a single run, used to support cross-Agent state management and data sharing. Any Agent in a single run can read and write SessionValues at any time. +SessionValues is a global temporary KV store that persists throughout a single run, used to support cross-Agent state management and data sharing. Any Agent in a single run can read and write SessionValues at any time. -Eino ADK provides multiple methods for concurrent-safe reading and writing of Session Values during Agent runtime: +Eino ADK provides multiple methods for Agents to read and write Session Values in a concurrency-safe manner at runtime: ```go // github.com/cloudwego/eino/adk/runctx.go @@ -109,19 +109,19 @@ Eino ADK provides multiple methods for concurrent-safe reading and writing of Se func GetSessionValues(ctx context.Context) map[string]any // Batch set SessionValues func AddSessionValues(ctx context.Context, kvs map[string]any) -// Get a single value from SessionValues by key, returns false as second value if key doesn't exist, otherwise true +// Get a value from SessionValues by specified key. Returns false as the second value if key doesn't exist, otherwise true func GetSessionValue(ctx context.Context, key string) (any, bool) // Set a single SessionValue func AddSessionValue(ctx context.Context, key string, value any) ``` -Note that since the SessionValues mechanism is implemented based on Context, and the Runner re-initializes the Context when running, injecting SessionValues via `AddSessionValues` or `AddSessionValue` outside the Run method will not take effect. +Note that since the SessionValues mechanism is implemented based on Context, and Runner reinitializes the Context when running, injecting SessionValues via `AddSessionValues` or `AddSessionValue` outside of the Run method will not take effect. -If you need to inject data into SessionValues before Agent execution, you need to use a dedicated Option to assist, as follows: +If you need to inject data into SessionValues before the Agent runs, you need to use a dedicated Option to assist with this. Usage is as follows: ```go // github.com/cloudwego/eino/adk/call_option.go -// WithSessionValues injects SessionValues before Agent execution +// WithSessionValues injects SessionValues before Agent runs func WithSessionValues(v map[string]any) AgentRunOption // Usage: @@ -138,7 +138,7 @@ iterator := runner.Run(ctx, []adk.Message{schema.UserMessage("xxx")}, ### Concept -Transfer corresponds to the [Transfer Collaboration Mode]. When an Agent produces an AgentEvent containing a TransferAction during runtime, Eino ADK calls the Agent specified by the Action. The called Agent is called a sub-Agent (SubAgent). +Transfer corresponds to the [Transfer collaboration method]. When an Agent produces an AgentEvent containing TransferAction during runtime, Eino ADK calls the Agent specified by the Action. The called Agent is called a SubAgent. TransferAction can be quickly created using `NewTransferToAgentAction`: @@ -148,7 +148,7 @@ import "github.com/cloudwego/eino/adk" event := adk.NewTransferToAgentAction("dest agent name") ``` -To allow Eino ADK to find and run the sub-Agent instance when receiving a TransferAction, you need to call `SetSubAgents` to register possible sub-Agents with Eino ADK before running: +For Eino ADK to find and run the SubAgent instance upon receiving TransferAction, you need to first call `SetSubAgents` to register possible SubAgents with Eino ADK before running: ```go // github.com/cloudwego/eino/adk/flow.go @@ -156,12 +156,12 @@ func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, e ``` > 💡 -> The meaning of Transfer is to **hand off** the task to the sub-Agent, not delegate or assign, therefore: +> The meaning of Transfer is to **hand over** the task to the SubAgent, not delegate or assign. Therefore: > -> 1. Unlike ToolCall, when calling a sub-Agent through Transfer, the parent Agent will not summarize content or perform the next operation after the sub-Agent finishes running. -> 2. When calling a sub-Agent, the sub-Agent's input is still the original input, and the parent Agent's output serves as context for the sub-Agent's reference. +> 1. Unlike ToolCall, when calling a SubAgent through Transfer, after the SubAgent finishes running, the parent Agent will not be called again to summarize content or perform the next operation. +> 2. When calling a SubAgent, the SubAgent's input is still the original input, and the parent Agent's output serves as context for the SubAgent's reference. -When triggering SetSubAgents, both parent and child Agents need to be processed to complete initialization. Eino ADK defines the `OnSubAgents` interface to support this functionality: +When triggering SetSubAgents, both parent and child Agents need to process to complete initialization. Eino ADK defines the `OnSubAgents` interface to support this functionality: ```go // github.com/cloudwego/eino/adk/interface.go @@ -172,11 +172,11 @@ type OnSubAgents interface { } ``` -If an Agent implements the `OnSubAgents` interface, `SetSubAgents` will call the corresponding methods to register with the Agent, such as the `ChatModelAgent` implementation. +If an Agent implements the `OnSubAgents` interface, `SetSubAgents` will call the corresponding methods to register with the Agent. For example, `ChatModelAgent`'s implementation. ### Example -Next, we demonstrate the Transfer capability with a multi-functional dialogue Agent. The goal is to build an Agent that can query weather or chat with users. The Agent structure is as follows: +Below we demonstrate the Transfer capability with a multi-functional conversation Agent. The goal is to build an Agent that can query weather or chat with users. The Agent structure is as follows: @@ -268,7 +268,7 @@ func NewRouterAgent() adk.Agent { } ``` -Then use Eino ADK's Transfer capability to build Multi-Agent and run. ChatModelAgent implements the OnSubAgent interface, and in the adk.SetSubAgents method, this interface is used to register parent/child Agents with ChatModelAgent, without requiring users to handle TransferAction generation: +Then use Eino ADK's Transfer capability to build a Multi-Agent and run it. ChatModelAgent implements the OnSubAgent interface. In the adk.SetSubAgents method, this interface is used to register parent/child Agents with ChatModelAgent, without requiring users to handle TransferAction generation: ```go import ( @@ -374,10 +374,10 @@ usage: &{206 23 229} ====== ``` -The other two methods of OnSubAgents are called when an Agent is used as a sub-Agent in SetSubAgents: +The other two methods of OnSubAgents are called when an Agent acts as a SubAgent in SetSubAgents: - OnSetAsSubAgent is used to register parent Agent information with the Agent -- OnDisallowTransferToParent is called when the Agent is set with WithDisallowTransferToParent option, used to inform the Agent not to produce TransferAction to the parent Agent. +- OnDisallowTransferToParent is called when the Agent sets the WithDisallowTransferToParent option, to inform the Agent not to produce TransferAction to the parent Agent. ```go adk.SetSubAgents( @@ -389,9 +389,9 @@ adk.SetSubAgents( ) ``` -### Static Configuration Transfer +### Static Transfer Configuration -AgentWithDeterministicTransferTo is an Agent Wrapper that generates preset TransferAction after the original Agent executes, enabling static configuration of Agent jumping: +AgentWithDeterministicTransferTo is an Agent Wrapper that generates a preset TransferAction after the original Agent executes, enabling static configuration of Agent jumping: ```go // github.com/cloudwego/eino/adk/flow.go @@ -404,7 +404,7 @@ type DeterministicTransferConfig struct { func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent ``` -In Supervisor mode, after a sub-Agent finishes execution, it returns to the Supervisor, and the Supervisor generates the next task target. In this case, AgentWithDeterministicTransferTo can be used: +In Supervisor mode, after a SubAgent finishes execution, it always returns to the Supervisor, which generates the next task objective. AgentWithDeterministicTransferTo can be used here: @@ -432,13 +432,13 @@ func NewSupervisor(ctx context.Context, conf *SupervisorConfig) (adk.Agent, erro ## Workflow Agents -WorkflowAgent supports running Agents according to a preset flow in code. Eino ADK provides three basic Workflow Agents: Sequential, Parallel, and Loop. They can be nested with each other to complete more complex tasks. +WorkflowAgent supports running Agents according to workflows preset in code. Eino ADK provides three basic Workflow Agents: Sequential, Parallel, and Loop. They can be nested within each other to complete more complex tasks. -By default, each Agent's input in a Workflow is generated using the method described in the History section. You can customize AgentInput generation using WithHistoryRewriter. +By default, the input for each Agent in a Workflow is generated using the method described in the History section. You can customize the AgentInput generation method using WithHistoryRewriter. -When an Agent produces an ExitAction Event, the Workflow Agent will exit immediately, regardless of whether there are other Agents that need to run afterward. +When an Agent produces an ExitAction Event, the Workflow Agent will immediately exit, regardless of whether there are other Agents that need to run afterward. -For details and usage examples, see: [Eino ADK: Workflow Agents](/docs/eino/core_modules/eino_adk/agent_implementation/workflow) +For detailed explanations and use case references, see: [Eino ADK: Workflow Agents](/docs/eino/core_modules/eino_adk/agent_implementation/workflow) ### SequentialAgent @@ -458,7 +458,7 @@ func NewSequentialAgent(ctx context.Context, config *SequentialAgentConfig) (Age ### LoopAgent -LoopAgent is implemented based on SequentialAgent. After SequentialAgent completes running, it starts from the beginning again: +LoopAgent is implemented based on SequentialAgent. After SequentialAgent completes, it runs from the beginning again: @@ -468,7 +468,7 @@ type LoopAgentConfig struct { Description string SubAgents []Agent - MaxIterations int // Maximum number of iterations + MaxIterations int // Maximum number of loop iterations } func NewLoopAgent(ctx context.Context, config *LoopAgentConfig) (Agent, error) @@ -492,30 +492,30 @@ func NewParallelAgent(ctx context.Context, config *ParallelAgentConfig) (Agent, ## AgentAsTool -When an Agent only needs clear and explicit instructions rather than complete running context (History), the Agent can be converted to a Tool for calling: +When running an Agent requires only clear and explicit instructions rather than a complete running context (History), the Agent can be converted to a Tool for invocation: ```go func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool ``` -After being converted to a Tool, the Agent can be called by ChatModels that support function calling, and can also be called by all LLM-driven Agents. The calling method depends on the Agent implementation. +After converting to a Tool, the Agent can be called by ChatModels that support function calling, and can also be called by all LLM-driven Agents. The calling method depends on the Agent implementation. -Message History Isolation: An Agent as a Tool will not inherit the message history (History) of the parent Agent. +Message history isolation: An Agent as a Tool does not inherit the message history (History) of the parent Agent. -SessionValues Sharing: However, it will share the SessionValues of the parent Agent, i.e., read and write the same KV map. +SessionValues sharing: However, it shares the SessionValues of the parent Agent, i.e., reads and writes the same KV map. -Internal Event Exposure: An Agent as a Tool is still an Agent and will produce AgentEvents. By default, these internal AgentEvents will not be exposed through the `AsyncIterator` returned by `Runner`. In some business scenarios, if you need to expose internal AgentTool's AgentEvents to users, you need to add configuration in the parent `ChatModelAgent`'s `ToolsConfig` to enable internal event exposure: +Internal event exposure: An Agent as a Tool is still an Agent and produces AgentEvents. By default, these internal AgentEvents are not exposed through the `AsyncIterator` returned by `Runner`. In some business scenarios, if you need to expose the internal AgentTool's AgentEvents to users, you need to add configuration in the parent `ChatModelAgent`'s `ToolsConfig` to enable internal event exposure: ```go // from adk/chatmodel.go -**type **ToolsConfig **struct **{ +type ToolsConfig struct { // other configurations... - _// EmitInternalEvents indicates whether internal events from agentTool should be emitted_ -_ // to the parent generator via a tool option injection at run-time._ -_ _EmitInternalEvents bool + // EmitInternalEvents indicates whether internal events from agentTool should be emitted + // to the parent generator via a tool option injection at run-time. + EmitInternalEvents bool } ``` -These internal events will not enter the parent agent's context (except for the last message that would enter anyway), and various AgentActions will not take effect (except for InterruptAction). +These internal events will not enter the parent agent's context (except for the last message which would enter anyway), and various AgentActions will not take effect (except InterruptAction). 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 index a2cb5b62ee4..2532cbd00ec 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_extension.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_extension.md @@ -3,29 +3,31 @@ Description: "" date: "2025-11-20" lastmod: "" tags: [] -title: 'Eino ADK: Agent Runner & Extensions' -weight: 3 +title: 'Eino ADK: Agent Runner and Extension' +weight: 6 --- # 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. +Runner is the core engine in Eino ADK responsible for executing Agents. Its main purpose is to manage and control the entire lifecycle of Agents, such as handling multi-Agent collaboration, saving and passing context, etc. Cross-cutting capabilities like interrupt, callback, etc. all rely on Runner for implementation. Any Agent should be run through 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. +Agent Runner provides runtime interrupt and resume functionality. This allows a running Agent to proactively interrupt its execution and save the current state, supporting resumption from the interrupt point. This functionality is commonly used in scenarios where the Agent processing flow requires external input, long waits, or pausable operations. -Three key points in one interrupt→resume process: +Below we introduce three key points in an interrupt-to-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 +1. Interrupted Action: Thrown by the Agent as an interrupt event, intercepted by Agent Runner +2. Checkpoint: Agent Runner intercepts the event and saves the current running state +3. Resume: After running conditions are ready again, Agent Runner resumes running from the checkpoint ### 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: +During the Agent's execution, you can proactively interrupt the Runner's operation by producing an AgentEvent containing an Interrupted Action. + +When the Event's Interrupted is not empty, the Agent Runner considers an interrupt to have occurred: ```go // github.com/cloudwego/eino/adk/interface.go @@ -41,13 +43,13 @@ type InterruptInfo struct { } ``` -When interruption occurs, use `InterruptInfo` to attach custom data. This data: +When an interrupt occurs, you can attach custom interrupt information through the InterruptInfo structure. This information: -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 +1. Will be passed to the caller, which can be used to explain the reason for the interrupt, etc. +2. If the Agent run needs to be resumed later, the InterruptInfo will be re-passed to the interrupted Agent upon resumption, and the Agent can use this information to resume running ```go -// e.g., ChatModelAgent emits the following AgentEvent on interrupt: +// For example, when ChatModelAgent interrupts, it sends the following AgentEvent: h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{ Interrupted: &InterruptInfo{ Data: &ChatModelAgentInterruptInfo{Data: data, Info: info}, @@ -57,7 +59,7 @@ h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{ ### State Persistence (Checkpoint) -Runner terminates the current run after capturing an event with Interrupted Action. If: +When Runner captures this Event with Interrupted Action, it immediately terminates the current execution flow. If: 1. CheckPointStore is set in Runner @@ -75,25 +77,28 @@ type CheckPointStore interface { } ``` -2. WithCheckPointID is passed when calling Runner +1. CheckPointID is passed via AgentRunOption WithCheckPointID when calling Runner ```go // github.com/cloudwego/eino/adk/interrupt.go -func WithCheckPointID(id string) _AgentRunOption_ +func WithCheckPointID(id string) AgentRunOption ``` -Runner persists current state (original input, history, etc.) and the Agent’s InterruptInfo into CheckPointStore under CheckPointID. +After terminating running, Runner persists the current running state (original input, conversation history, etc.) and the InterruptInfo thrown by the Agent to CheckPointStore using CheckPointID as the key. -> 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. +> 💡 +> To preserve the original types of data in interfaces, Eino ADK uses gob ([https://pkg.go.dev/encoding/gob](https://pkg.go.dev/encoding/gob)) to serialize running state. Therefore, when using custom types, you need to register the types in advance using gob.Register or gob.RegisterName (the latter is more recommended; the former uses path plus type name as the default name, so both the type's location and name cannot change). Eino automatically registers types built into the framework. ### Resume +When running is interrupted, calling Runner's Resume interface with the CheckPointID from the interrupt can resume running: + ```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: +Resuming Agent running requires the interrupted Agent to implement the ResumableAgent interface. Runner reads the running state from CheckPointerStore and resumes running, where the InterruptInfo and the EnableStreaming configured in the previous run are provided as input to the Agent: ```go // github.com/cloudwego/eino/adk/interface.go @@ -106,8 +111,8 @@ type ResumableAgent interface { // github.com/cloudwego/eino/adk/interrupt.go type ResumeInfo struct { EnableStreaming bool - *_InterruptInfo_ + *InterruptInfo } ``` -If Resume needs to pass new inputs to the Agent, define AgentRunOption and supply it when calling Runner.Resume. +To pass new information to the Agent during Resume, you can define an AgentRunOption and pass 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 index 7895b55bdfa..d812543b7c4 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_hitl.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_hitl.md @@ -1,19 +1,19 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino Human-in-the-Loop Framework: Architecture Guide' -weight: 6 +title: 'Eino Human-in-the-Loop Framework: Technical Architecture Guide' +weight: 7 --- ## Overview -This document provides technical details of Eino's human-in-the-loop (Human-in-the-Loop, HITL) framework architecture, focusing on the interrupt/resume mechanism and the underlying addressing system. +This document provides technical details of Eino's human-in-the-loop (HITL) framework architecture, focusing on the interrupt/resume mechanism and the underlying addressing system. -## Human-in-the-Loop Requirements +## Requirements for Human-in-the-Loop -The following diagram illustrates the key questions each component must answer during the interrupt/resume process. Understanding these requirements is key to grasping the rationale behind the architecture design. +The diagram below illustrates the key questions that each component must answer during the interrupt/resume process. Understanding these requirements is crucial for grasping the rationale behind the architecture design. ```mermaid graph TD @@ -22,8 +22,8 @@ graph TD 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?] + D2[What info should the user
    see about this interrupt?] + D3[What state should I preserve
    for later resumption?] D1 --> D2 --> D3 end @@ -32,7 +32,7 @@ graph TD F1[Where in the execution hierarchy
    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?] + F4[What info does the user need
    to understand the interrupt?] F1 --> F2 --> F3 --> F4 end @@ -44,10 +44,10 @@ graph TD subgraph "End User" direction TB U1[Where in the flow did
    the interrupt occur?] - U2[What information did the
    developer provide?] + U2[What type of info 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?] + U4[Should I provide data
    for resumption?] + U5[What type of resume data
    should I provide?] U1 --> U2 --> U3 --> U4 --> U5 end end @@ -57,8 +57,8 @@ graph TD 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?] + FR1[Which entity is interrupting
    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 @@ -67,8 +67,8 @@ graph TD direction TB DR1[Am I the explicit
    resume target?] DR2[If not target, 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?] + DR3[What state did I preserve
    at interrupt time?] + DR4[If user resume data is provided,
    how to handle it?] DR1 --> DR2 --> DR3 --> DR4 end @@ -90,11 +90,11 @@ Therefore, our goals are: 1. Help developers answer the above questions as easily as possible. 2. Help end users answer the above questions as easily as possible. -3. Enable the framework to answer the above questions automatically and out-of-the-box. +3. Enable the framework to automatically answer the above questions out of the box. ## Quick Start -We demonstrate the functionality with a simple ticket booking Agent that asks the user for "confirmation" before actually completing the booking. The user can "approve" or "reject" the booking operation. The complete code for this example is at: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/1_approval](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/1_approval) +We demonstrate the functionality with a simple ticket booking Agent. This Agent requests "confirmation" from the user before actually completing the booking, and users can "approve" or "reject" the booking operation. The complete code for this example is at: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/1_approval](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/1_approval) 1. Create a ChatModelAgent and configure a Tool for booking tickets. @@ -156,7 +156,7 @@ Based on the user's request, use the "BookTicket" tool to book tickets.`, } ``` -2. Create a Runner, configure CheckPointStore, and run with a CheckPointID. Eino uses CheckPointStore to save the Agent's running state when interrupted. Here we use InMemoryStore, which saves in memory. In actual use, a distributed store like Redis is recommended. Additionally, Eino uses CheckPointID to uniquely identify and link the "before interrupt" and "after interrupt" runs (or multiple runs). +1. Create a Runner, configure CheckPointStore, and run it, passing in a CheckPointID. Eino uses CheckPointStore to save the Agent's running state when interrupted. Here we use InMemoryStore, which stores in memory. In actual use, distributed storage like redis is recommended. Additionally, Eino uses CheckPointID to uniquely identify and link the "before interrupt" and "after interrupt" runs (or multiple runs). ```go a := NewTicketBookingAgent() @@ -172,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. Get the interrupt information from AgentEvent `event.Action.Interrupted.InterruptContexts[0].Info`, which here is "preparing to book which train for whom, do you approve". You will also get an InterruptID (`event.Action.Interrupted.InterruptContexts[0].ID`), which the Eino framework uses to identify "where the interrupt occurred". Here it's printed directly to the terminal; in actual use, it may need to be returned as an HTTP response to the frontend. +1. Get interrupt information from AgentEvent `event.Action.Interrupted.InterruptContexts[0].Info`, which in this case is "about to book which train for whom, approve or not". You'll also get an InterruptID (`event.Action.Interrupted.InterruptContexts[0].ID`), which the Eino framework uses to identify "where the interrupt occurred". Here it's printed directly to the terminal; in actual use, it may need to be returned to the frontend as an HTTP response. ```go var lastEvent *adk.AgentEvent @@ -195,7 +195,7 @@ for { interruptID := lastEvent.Action.Interrupted.InterruptContexts[0].ID ``` -4. Show the interrupt information to the user and receive the user's response, such as "approve". In this example, everything is displayed to and received from the user on the local terminal. In actual applications, a ChatBot might be used for input/output. +1. Display interrupt information to the user and receive the user's response, such as "approve". In this example, both display to user and receiving user input happen on the local terminal. In actual applications, it might use a ChatBot for input/output. ```go var apResult *tool.ApprovalResult @@ -237,7 +237,7 @@ tool 'BookTicket' interrupted with arguments '{"location":"Beijing","passenger_n your input here: Y ``` -5. Call Runner.ResumeWithParams, passing the same InterruptID and the data for resumption, which here is "approve". In this example, the initial `Runner.Query` and subsequent `Runner.ResumeWithParams` are in the same instance. In real scenarios, it might be two requests from a ChatBot frontend hitting two different server instances. As long as the CheckPointID is the same both times and the CheckPointStore configured for the Runner is distributed storage, Eino can achieve cross-instance interrupt resumption. +1. Call Runner.ResumeWithParams, passing in the same InterruptID and the data for resumption, which here is "approve". In this example, the initial `Runner.Query` and the subsequent `Runner.ResumeWithParams` are in the same instance. In real scenarios, it might be two requests from a ChatBot frontend hitting two different server instances. As long as the CheckPointID is the same for both, and the CheckPointStore configured for Runner is distributed storage, Eino can achieve cross-instance interrupt recovery. ```go // here we directly resumes right in the same instance where the original `Runner.Query` happened. @@ -292,17 +292,17 @@ answer: The ticket for Martin to Beijing on 2025-12-01 has been successfully boo ### More Examples -- Review and Edit Mode: Allows human review and in-place editing of tool call parameters before execution. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/2_review-and-edit](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/2_review-and-edit) -- Feedback Loop Mode: Iterative improvement mode where the agent generates content and humans provide qualitative feedback for improvement. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/3_feedback-loop](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/3_feedback-loop) -- Follow-up Mode: Proactive mode where the agent identifies insufficient tool output and requests clarification or next steps. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/4_follow-up](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/4_follow-up) +- Review and Edit Mode: Allows human review and in-place editing of tool call arguments before execution. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/2_review-and-edit](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/2_review-and-edit) +- Feedback Loop Mode: Iterative refinement pattern where agent generates content and human provides qualitative feedback for improvement. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/3_feedback-loop](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/3_feedback-loop) +- Follow-up Mode: Proactive pattern where agent identifies insufficient tool output and requests clarification or next steps. [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/4_follow-up](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/4_follow-up) - Interrupt within supervisor architecture: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/5_supervisor](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/5_supervisor) - Interrupt within plan-execute-replan architecture: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/6_plan-execute-replan](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/6_plan-execute-replan) - Interrupt within deep-agents architecture: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/7_deep-agents](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/7_deep-agents) -- Interrupt when a sub-agent of supervisor is plan-execute-replan: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/8_supervisor-plan-execute](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/8_supervisor-plan-execute) +- Interrupt when supervisor's sub-agent is plan-execute-replan: [https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/8_supervisor-plan-execute](https://github.com/cloudwego/eino-examples/tree/main/adk/human-in-the-loop/8_supervisor-plan-execute) ## Architecture Overview -The following flowchart illustrates the high-level interrupt/resume flow: +The following flowchart illustrates the high-level interrupt/resume process: ```mermaid flowchart TD @@ -328,13 +328,13 @@ flowchart TD ResumeData end - Run -->|any number of transfer / call| E - R <-->|persist/restore| C - Resume -->|replay transfer / call| E - C -->|auto assigned to| E + Run -->|Any number of transfer / call| E + R <-->|Store/Restore| C + Resume -->|Replay transfer / call| E + C -->|Automatically assigned to| E ``` -The following sequence diagram shows the time-ordered interaction flow between the three main participants: +The following sequence diagram shows the chronological interaction flow between the three main participants: ```mermaid sequenceDiagram @@ -344,23 +344,23 @@ sequenceDiagram Note over D,F: 1. Interrupt Phase - D->>F: call StatefulInterrupt()
    specify info and state - F->>F: persist InterruptID->{address, state} + 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} + 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 + F->>F: Route to interrupted entity + F->>D: Provide state and resume data + D->>D: Handle resumption ``` -## ADK Package APIs +## ADK Package API The ADK package provides high-level abstractions for building interruptible agents with human-in-the-loop capabilities. @@ -368,7 +368,7 @@ The ADK package provides high-level abstractions for building interruptible agen #### `Interrupt` -Creates a basic interrupt action. Use when an agent needs to pause execution to request external input or intervention, but does not need to save any internal state for resumption. +Creates a basic interrupt action. Use when an agent needs to pause execution to request external input or intervention but doesn't need to save any internal state for resumption. ```go func Interrupt(ctx context.Context, info any) *AgentEvent @@ -379,7 +379,7 @@ func Interrupt(ctx context.Context, info any) *AgentEvent - `ctx`: The context of the running component. - `info`: User-facing data describing the reason for the interrupt. -**Returns:** `*AgentEvent` with an interrupt action. +**Returns:** `*AgentEvent` with interrupt action. **Example:** @@ -394,7 +394,7 @@ return adk.Interrupt(ctx, "User query is unclear, please clarify.") #### `StatefulInterrupt` -Creates an interrupt action while saving the agent's internal state. Use when an agent has internal state that must be restored to continue correctly. +Creates an interrupt action while also saving the agent's internal state. Use when an agent has internal state that must be restored to continue correctly. ```go func StatefulInterrupt(ctx context.Context, info any, state any) *AgentEvent @@ -406,14 +406,14 @@ func StatefulInterrupt(ctx context.Context, info any, state any) *AgentEvent - `info`: User-facing data describing the interrupt. - `state`: The agent's internal state object, which will be serialized and stored. -**Returns:** `*AgentEvent` with an interrupt action. +**Returns:** `*AgentEvent` with interrupt action. **Example:** ```go // Inside an agent's Run method: -// Define the state to save. +// Define state to save. type MyAgentState struct { ProcessedItems int CurrentTopic string @@ -432,7 +432,7 @@ return adk.StatefulInterrupt(ctx, "Need user feedback before continuing", curren #### `CompositeInterrupt` -Creates an interrupt action for components that coordinate multiple sub-components. It combines interrupts from one or more child agents into a single, cohesive interrupt. Any agent containing child agents (e.g., custom `Sequential` or `Parallel` agents) uses this to propagate their children's interrupts. +Creates an interrupt action for components that coordinate multiple sub-components. It combines one or more sub-agent interrupts into a single, cohesive interrupt. Any agent containing sub-agents (e.g., custom `Sequential` or `Parallel` agents) uses this to propagate their children's interrupts. ```go func CompositeInterrupt(ctx context.Context, info any, state any, @@ -443,29 +443,29 @@ func CompositeInterrupt(ctx context.Context, info any, state any, - `ctx`: The context of the running component. - `info`: User-facing data describing the coordinator's own interrupt reason. -- `state`: The coordinator agent's own state (e.g., the index of the interrupted child agent). -- `subInterruptSignals`: A variadic list of `InterruptSignal` objects from interrupted child agents. +- `state`: The coordinator agent's own state (e.g., index of interrupted sub-agent). +- `subInterruptSignals`: Variadic list of `InterruptSignal` objects from interrupted sub-agents. -**Returns:** `*AgentEvent` with an interrupt action. +**Returns:** `*AgentEvent` with interrupt action. **Example:** ```go -// In a custom sequential agent running two child agents... +// In a custom sequential agent that runs two sub-agents... subAgent1 := &myInterruptingAgent{} subAgent2 := &myOtherAgent{} // If subAgent1 returns an interrupt event... subInterruptEvent := subAgent1.Run(ctx, input) -// The parent agent must capture it and wrap it in CompositeInterrupt. +// The parent agent must catch it and wrap it in a CompositeInterrupt. if subInterruptEvent.Action.Interrupted != nil { - // The parent agent can add its own state, like which child agent was interrupted. + // The parent agent can add its own state, like which child was interrupted. parentState := map[string]int{"interrupted_child_index": 0} // Bubble up the interrupt. return adk.CompositeInterrupt(ctx, - "A child agent needs attention", + "A sub-agent needs attention", parentState, subInterruptEvent.Action.Interrupted.internalInterrupted, ) @@ -482,17 +482,17 @@ When agent execution is interrupted, the `AgentEvent` contains structured interr ```go type InterruptCtx struct { - // ID is the unique, fully qualified address of the interrupt point, used for targeted resume. - // For example: "agent:A;node:graph_a;tool:tool_call_123" + // ID is the unique, fully-qualified address of the interrupt point, used for targeted resumption. + // Example: "agent:A;node:graph_a;tool:tool_call_123" ID string // Address is a structured sequence of AddressSegment segments leading to the interrupt point. Address Address - // Info is the user-facing information associated with the interrupt, provided by the component that triggered it. + // Info is user-facing information associated with the interrupt, provided by the component that triggered it. Info any - // IsRootCause indicates whether the interrupt point is the exact root cause of the interrupt. + // IsRootCause indicates if the interrupt point is the exact root cause of the interrupt. IsRootCause bool // Parent points to the context of the parent component in the interrupt chain (nil for top-level interrupts). @@ -503,7 +503,7 @@ type InterruptCtx struct { The following example shows how to access this information: ```go -// At the application layer, after an interrupt: +// At the application layer, after interruption: if event.Action != nil && event.Action.Interrupted != nil { interruptInfo := event.Action.Interrupted @@ -517,11 +517,11 @@ if event.Action != nil && event.Action.Interrupted != nil { } ``` -### 3. APIs for End User Resume +### 3. APIs for End User Resumption #### `(*Runner).ResumeWithParams` -Continues interrupted execution from a checkpoint using the "explicit targeted resume" strategy. This is the most common and powerful way to resume, allowing you to target specific interrupt points and provide data for them. +Continues interrupted execution from a checkpoint using the "explicit targeted resumption" strategy. This is the most common and powerful way to resume, allowing you to target specific interrupt points and provide data to them. When using this method: @@ -537,7 +537,7 @@ func (r *Runner) ResumeWithParams(ctx context.Context, checkPointID string, - `ctx`: The context for resumption. - `checkPointID`: The identifier of the checkpoint to resume from. -- `params`: Interrupt parameters containing a mapping from interrupt IDs to resume data. These IDs can point to any interruptible component throughout the execution graph. +- `params`: Interrupt parameters containing a map of interrupt IDs to resume data. These IDs can point to any interruptible component across the execution graph. - `opts`: Additional run options. **Returns:** An async iterator of agent events. @@ -570,24 +570,24 @@ for event := range resumeIterator.Events() { } ``` -### 4. APIs for Developer Resume +### 4. APIs for Developer Resumption #### `ResumeInfo` Struct -`ResumeInfo` holds all the information needed to resume an interrupted agent execution. It is created by the framework and passed to the agent's `Resume` method. +`ResumeInfo` holds all information needed to resume interrupted agent execution. It is created by the framework and passed to the agent's `Resume` method. ```go type ResumeInfo struct { - // WasInterrupted indicates whether this agent had an interrupt in the previous Runner run. + // WasInterrupted indicates if this agent had an interrupt in a previous Runner run. WasInterrupted bool // InterruptState holds the state saved via StatefulInterrupt or CompositeInterrupt. InterruptState any - // IsResumeTarget indicates whether this agent is a specific target of ResumeWithParams. + // IsResumeTarget indicates if this agent is a specific target of ResumeWithParams. IsResumeTarget bool - // ResumeData holds the data provided by the user for this agent. + // ResumeData holds user-provided data for this agent. ResumeData any // ... other fields @@ -608,16 +608,16 @@ import ( // Inside an agent's Resume method: func (a *myAgent) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] { if !info.WasInterrupted { - // Already entered the Resume method, must have WasInterrupted = true + // Already entered Resume method, must have WasInterrupted = true return adk.NewAsyncIterator([]*adk.AgentEvent{{Err: errors.New("not an interrupt")}}, nil) } if !info.IsResumeTarget { - // This agent is not a specific target, so it must re-interrupt to preserve its state. + // This agent is not a specific target, so must re-interrupt to preserve its state. return adk.StatefulInterrupt(ctx, "Waiting for another part of the workflow to be resumed", info.InterruptState) } - // This agent is the target. Process the resume data. + // This agent is the target. Handle the resume data. if info.ResumeData != nil { userInput, ok := info.ResumeData.(string) if ok { @@ -633,15 +633,15 @@ func (a *myAgent) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk. } ``` -## Compose Package APIs +## Compose Package API -The `compose` package provides low-level building blocks for creating complex, interruptible workflows. Suitable for throwing interrupts and handling resumes in Graph Nodes. +The `compose` package provides low-level building blocks for creating complex, interruptible workflows. Suitable for throwing interrupts and handling resumption in Graph Nodes. ### 1. APIs for Interrupting #### `Interrupt` -Creates a special error that signals the execution engine to interrupt the current run at the component's specific address and save a checkpoint. This is the standard way for a single, non-composite component to signal a resumable interrupt. +Creates a special error that signals the execution engine to interrupt the current run at a component's specific address and save a checkpoint. This is the standard way for a single, non-composite component to signal a resumable interrupt. ```go func Interrupt(ctx context.Context, info any) error @@ -656,7 +656,7 @@ func Interrupt(ctx context.Context, info any) error #### `StatefulInterrupt` -Similar to `Interrupt`, but also saves the component's internal state. The state is saved in the checkpoint and provided back to the component via `GetInterruptState` on resume. +Similar to `Interrupt`, but also saves the component's internal state. The state is saved in the checkpoint and provided back to the component via `GetInterruptState` upon resumption. ```go func StatefulInterrupt(ctx context.Context, info any, state any) error @@ -666,13 +666,13 @@ func StatefulInterrupt(ctx context.Context, info any, state any) error - `ctx`: The context of the running component. - `info`: User-facing information about the interrupt. -- `state`: The internal state that the interrupted component needs to persist. +- `state`: The internal state that the interrupting component needs to persist. --- #### `CompositeInterrupt` -Creates a special error representing a composite interrupt. It is designed for "composite" nodes (like `ToolsNode`) or any component that coordinates multiple independent, interruptible sub-processes. It bundles multiple child interrupt errors into a single error that the engine can deconstruct into a flat list of resumable points. +Creates a special error representing a composite interrupt. It is designed for "composite" nodes (like `ToolsNode`) or any component that coordinates multiple independent, interruptible sub-processes. It bundles multiple sub-interrupt errors into a single error that the engine can deconstruct into a flat list of resumable points. ```go func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error) error @@ -681,8 +681,8 @@ func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error) **Parameters:** - `ctx`: The context of the running composite node. -- `info`: User-facing information for the composite node itself (can be `nil`). -- `state`: The state of the composite node itself (can be `nil`). +- `info`: User-facing information from the composite node itself (can be `nil`). +- `state`: The composite node's own state (can be `nil`). - `errs`: A list of errors from sub-processes. These can be `Interrupt`, `StatefulInterrupt`, or nested `CompositeInterrupt` errors. **Example:** @@ -698,7 +698,7 @@ for _, process := range processes { } } -// If any sub-process interrupts, bundle them together. +// If any sub-process interrupted, bundle them up. if len(errs) > 0 { // The composite node can save its own state, e.g., which processes have completed. return compose.CompositeInterrupt(ctx, "Parallel execution needs input", parentState, errs...) @@ -709,7 +709,7 @@ if len(errs) > 0 { #### `ExtractInterruptInfo` -Extracts a structured `InterruptInfo` object from an error returned by a `Runnable`'s `Invoke` or `Stream` method. This is the primary way for applications to get a list of all interrupt points after execution pauses. +Extracts the structured `InterruptInfo` object from an error returned by a `Runnable`'s `Invoke` or `Stream` method. This is the primary way for an application to get a list of all interrupt points after execution pauses. ```go composeInfo, ok := compose.ExtractInterruptInfo(err) @@ -722,23 +722,23 @@ if ok { **Example:** ```go -// After calling a graph that interrupts... +// After calling a graph that interrupted... _, err := graph.Invoke(ctx, "initial input") if err != nil { interruptInfo, isInterrupt := compose.ExtractInterruptInfo(err) if isInterrupt { - fmt.Printf("Execution was interrupted by %d interrupt points.\n", len(interruptInfo.InterruptContexts)) + fmt.Printf("Execution was interrupted with %d interrupt points.\n", len(interruptInfo.InterruptContexts)) // Now you can inspect interruptInfo.InterruptContexts to decide how to resume. } } ``` -### 3. APIs for End User Resume +### 3. APIs for End User Resumption #### `Resume` -Prepares the context for an "explicit targeted resume" operation by targeting one or more components without providing data. This is useful when the resume action itself is the signal. +Prepares the context for an "explicit targeted resumption" operation by targeting one or more components without providing data. Useful when the resumption action itself is the signal. ```go func Resume(ctx context.Context, interruptIDs ...string) context.Context @@ -747,7 +747,7 @@ func Resume(ctx context.Context, interruptIDs ...string) context.Context **Example:** ```go -// After an interrupt, we get two interrupt IDs: id1 and id2. +// After interrupt, we get two interrupt IDs: id1 and id2. // We want to resume both without providing specific data. resumeCtx := compose.Resume(context.Background(), id1, id2) @@ -759,7 +759,7 @@ resumeCtx := compose.Resume(context.Background(), id1, id2) #### `ResumeWithData` -Prepares a context to resume a single specific component with data. It is a convenience wrapper around `BatchResumeWithData`. +Prepares a context to resume a single specific component with data. It's a convenience wrapper around `BatchResumeWithData`. ```go func ResumeWithData(ctx context.Context, interruptID string, data any) context.Context @@ -778,7 +778,7 @@ resumeCtx := compose.ResumeWithData(context.Background(), interruptID, "This is #### `BatchResumeWithData` -This is the core function for preparing a resume context. It injects a mapping of resume targets (interrupt IDs) and their corresponding data into the context. Components whose interrupt IDs exist as keys will receive `isResumeFlow = true` when calling `GetResumeContext`. +This is the core function for preparing a resume context. It injects a map of resume targets (interrupt IDs) and their corresponding data into the context. Components whose interrupt IDs exist as keys will receive `isResumeFlow = true` when calling `GetResumeContext`. ```go func BatchResumeWithData(ctx context.Context, resumeData map[string]any) context.Context @@ -799,11 +799,11 @@ resumeCtx := compose.BatchResumeWithData(context.Background(), resumeData) // Pass this context to the next Invoke/Stream call. ``` -### 4. APIs for Developer Resume +### 4. APIs for Developer Resumption #### `GetInterruptState` -Provides a type-safe way to check and retrieve the persisted state from a previous interrupt. This is the primary function for components to learn about their past state. +Provides a type-safe way to check and retrieve persisted state from a previous interrupt. This is the primary function for a component to learn about its past state. ```go func GetInterruptState[T any](ctx context.Context) (wasInterrupted bool, hasState bool, state T) @@ -812,7 +812,7 @@ func GetInterruptState[T any](ctx context.Context) (wasInterrupted bool, hasStat **Return values:** - `wasInterrupted`: `true` if the node was part of a previous interrupt. -- `hasState`: `true` if state was provided and successfully converted to type `T`. +- `hasState`: `true` if state was provided and successfully cast to type `T`. - `state`: The typed state object. **Example:** @@ -843,8 +843,8 @@ func GetResumeContext[T any](ctx context.Context) (isResumeTarget bool, hasData **Return values:** -- `isResumeTarget`: `true` if the component was explicitly targeted by the resume call. If `false`, the component must re-interrupt to preserve its state. Note that if a component is not directly targeted but is a parent of a directly targeted component, `isResumeTarget` will still be `true`. For example, if NodeA is interrupted and targeted for resume, then GraphA containing NodeA will also be a resume target. -- `hasData`: `true` if data was provided for this component. +- `isResumeTarget`: `true` if the component was explicitly targeted by the resume call. If `false`, the component must re-interrupt to preserve its state. Note that if a component is not directly targeted but is a parent of a directly targeted component, `isResumeTarget` will still be `true`. For example, if NodeA is interrupted and targeted for resumption, then GraphA containing NodeA will also be a resume target. +- `hasData`: `true` if resume data was provided for this component. - `data`: The typed data provided by the user. **Example:** @@ -860,37 +860,37 @@ if wasInterrupted { if hasData { fmt.Printf("Resuming with user data: %s\n", resumeData) } - // Complete work using restored state and resume data + // Complete the work using restored state and resume data result := processWithStateAndData(state, resumeData) return result, nil } else { - // This component is not the target, so it must re-interrupt. + // This component is not the target, so must re-interrupt. return compose.StatefulInterrupt(ctx, "Waiting for another component to be resumed", oldState) } } ``` -## Tool Package APIs +## Tool Package API -Completely symmetric with the Compose Package APIs, used for throwing interrupts and handling resumes inside Tools. See the `components/tool/interrupt.go` file. +Completely symmetric to the Compose Package API, used for throwing interrupts and handling resumption inside Tools. See the `components/tool/interrupt.go` file. -## Underlying Architecture: Addressing System +## Underlying Architecture: The Addressing System ### The Need for Addresses -The addressing system is designed to solve three fundamental needs in effective human-in-the-loop interaction: +The addressing system is designed to solve three fundamental requirements for effective human-in-the-loop interactions: 1. **State Attachment**: To attach local state to interrupt points, we need a stable, unique locator for each interrupt point. -2. **Targeted Resume**: To provide targeted resume data for specific interrupt points, we need a way to precisely identify each point. +2. **Targeted Resumption**: To provide targeted resume data to specific interrupt points, we need a way to precisely identify each point. 3. **Interrupt Localization**: To tell end users exactly where in the execution hierarchy the interrupt occurred. -### How Addresses Meet These Needs +### How Addresses Meet These Requirements -The address system meets these needs through three key properties: +The address system meets these requirements through three key properties: -- **Stability**: Addresses remain consistent across multiple executions, ensuring the same interrupt point can be reliably identified. -- **Uniqueness**: Each interrupt point has a unique address, enabling precise targeting during resume. -- **Hierarchical Structure**: Addresses provide a clear hierarchical path showing exactly where in the execution flow the interrupt occurred. +- **Stability**: Addresses remain consistent across multiple executions, ensuring the same interrupt points can be reliably identified. +- **Uniqueness**: Each interrupt point has a unique address, enabling precise targeting during resumption. +- **Hierarchy**: Addresses provide a clear hierarchical path showing exactly where in the execution flow the interrupt occurred. ### Address Structure and Segment Types @@ -910,7 +910,7 @@ type AddressSegment struct { #### Address Structure Diagram -The following diagrams illustrate the hierarchical structure of `Address` and its `AddressSegment` from both ADK and Compose perspectives: +The following diagrams illustrate the hierarchical structure of `Address` and its `AddressSegment` from both ADK and Compose layer perspectives: **ADK Layer Perspective** (Simplified, Agent-centric view): @@ -943,7 +943,7 @@ graph TD style D3 fill:#e8f5e8 ``` -**Compose Layer Perspective** (Detailed, full hierarchy view): +**Compose Layer Perspective** (Detailed, complete hierarchical view): ```mermaid graph TD @@ -996,16 +996,16 @@ const ( ) ``` -**Key Features:** +**Key characteristics:** -- **Agent Segment**: Represents agent-level execution segments (typically omits `SubID`). -- **Tool Segment**: Represents tool-level execution segments (`SubID` is used to ensure uniqueness). -- **Simplified View**: Abstracts away underlying complexity for agent developers. -- **Example Path**: `Agent:A → Agent:B → Tool:search_tool:1` +- **Agent segments**: Represent agent-level execution segments (typically `SubID` is omitted). +- **Tool segments**: Represent tool-level execution segments (`SubID` is used to ensure uniqueness). +- **Simplified view**: Abstracts away underlying complexity for agent developers. +- **Example path**: `Agent:A → Agent:B → Tool:search_tool:1` #### Compose Layer Segment Types -The `compose` layer provides fine-grained control and visibility over the entire execution hierarchy: +The `compose` layer provides fine-grained control and visibility into the entire execution hierarchy: ```go type AddressSegmentType = core.AddressSegmentType @@ -1017,19 +1017,19 @@ const ( ) ``` -**Key Features:** +**Key characteristics:** -- **Runnable Segment**: Represents top-level executables (Graph, Workflow, Chain). -- **Node Segment**: Represents individual nodes in the execution graph. -- **Tool Segment**: Represents specific tool calls within a `ToolsNode`. -- **Detailed View**: Provides full visibility into the execution hierarchy. -- **Example Path**: `Runnable:my_graph → Node:sub_graph → Node:tools_node → Tool:mcp_tool:1` +- **Runnable segments**: Represent top-level executables (Graph, Workflow, Chain). +- **Node segments**: Represent individual nodes in an execution graph. +- **Tool segments**: Represent specific tool calls within a `ToolsNode`. +- **Detailed view**: Provides full visibility into the execution hierarchy. +- **Example path**: `Runnable:my_graph → Node:sub_graph → Node:tools_node → Tool:mcp_tool:1` ### Extensibility and Design Principles -The address segment type system is designed to be **extensible**. Framework developers can add new segment types to support additional execution modes or custom components while maintaining backward compatibility. +The address segment type system is designed to be **extensible**. Framework developers can add new segment types to support additional execution patterns or custom components while maintaining backward compatibility. -**Key Design Principle**: The ADK layer provides simplified, agent-centric abstractions, while the `compose` layer handles the full complexity of the execution hierarchy. This layered approach allows developers to work at the abstraction level that suits their needs. +**Key design principle**: The ADK layer provides simplified, agent-centric abstractions while the `compose` layer handles the full complexity of the execution hierarchy. This layered approach allows developers to work at the abstraction level that suits their needs. ## Backward Compatibility @@ -1037,25 +1037,25 @@ The human-in-the-loop framework maintains full backward compatibility with exist ### 1. Graph Interrupt Compatibility -Previous graph interrupt flows using the deprecated `NewInterruptAndRerunErr` or `InterruptAndRerun` in nodes/tools will continue to be supported, but require one critical additional step: **error wrapping**. +Previous graph interrupt flows using the deprecated `NewInterruptAndRerunErr` or `InterruptAndRerun` in nodes/tools will continue to be supported, but with one critical additional step: **error wrapping**. -Since these functions are unaware of the new addressing system, it is the responsibility of the component calling them to capture the error and wrap the address information into it using the `WrapInterruptAndRerunIfNeeded` helper function. This is typically done inside the composite node (like the official ToolsNode) that calls the legacy component. +Since these functions are not aware of the new addressing system, the component calling them is responsible for catching the error and wrapping it with address information using the `WrapInterruptAndRerunIfNeeded` helper function. This is typically done inside composite nodes (like the official ToolsNode) that call legacy components. -> **Note**: If you choose **not** to use `WrapInterruptAndRerunIfNeeded`, the original behavior of these functions will be preserved. End users can still use `ExtractInterruptInfo` to get information from the error as before. However, since the resulting interrupt context will lack the correct address, the new targeted resume APIs will not be available for that specific interrupt point. To fully enable the new address-aware functionality, wrapping is required. +> **Note**: If you choose **not** to use `WrapInterruptAndRerunIfNeeded`, the original behavior of these functions will be preserved. End users can still use `ExtractInterruptInfo` to get information from the error as before. However, since the resulting interrupt context will lack the correct address, you will not be able to use the new targeted resumption APIs for that specific interrupt point. To fully enable the new address-aware features, wrapping is required. -```go +```java // 1. A legacy tool using deprecated interrupt func myLegacyTool(ctx context.Context, input string) (string, error) { // ... tool logic // This error is not address-aware. - return "", compose.NewInterruptAndRerunErr("Requires user approval") + return "", compose.NewInterruptAndRerunErr("Need user approval") } -// 2. A composite node calling the legacy tool +// 2. A composite node that calls the legacy tool var legacyToolNode = compose.InvokableLambda(func(ctx context.Context, input string) (string, error) { out, err := myLegacyTool(ctx, input) if err != nil { - // Critical: The caller must wrap the error to add the address. + // CRITICAL: Caller must wrap the error to add address. // The "tool:legacy_tool" segment will be appended to the current address. segment := compose.AddressSegment{Type: "tool", ID: "legacy_tool"} return "", compose.WrapInterruptAndRerunIfNeeded(ctx, segment, err) @@ -1063,12 +1063,12 @@ var legacyToolNode = compose.InvokableLambda(func(ctx context.Context, input str return out, nil }) -// 3. End user code can now see the full address. +// 3. End-user code can now see the full address. _, err := graph.Invoke(ctx, input) if err != nil { interruptInfo, exists := compose.ExtractInterruptInfo(err) if exists { - // The interrupt context will now have a correct, fully qualified address. + // The interrupt context will now have a correct, fully-qualified address. fmt.Printf("Interrupt Address: %s\n", interruptInfo.InterruptContexts[0].Address.String()) } } @@ -1078,15 +1078,15 @@ if err != nil { Static interrupts added via `WithInterruptBeforeNodes` or `WithInterruptAfterNodes` continue to work, but the way state is handled has been improved. -When a static interrupt is triggered, an `InterruptCtx` is generated with an address pointing to the graph (or subgraph) where the interrupt was defined. The key is that the `InterruptCtx.Info` field now directly exposes the graph's state. +When a static interrupt is triggered, an `InterruptCtx` is generated with an address pointing to the graph (or subgraph) that defined the interrupt. The key is that the `InterruptCtx.Info` field now directly exposes that graph's state. This enables a more direct and intuitive workflow: 1. The end user receives the `InterruptCtx` and can inspect the graph's live state via the `.Info` field. -2. They can directly modify this state object. -3. They can then pass the modified graph state object back via `ResumeWithData` and `InterruptCtx.ID` to resume execution. +2. They can modify this state object directly. +3. They can then pass the modified graph state object back via `ResumeWithData` with the `InterruptCtx.ID` to resume execution. -This new pattern often eliminates the need for the old `WithStateModifier` option, although it remains available for full backward compatibility. +This new pattern often makes the old `WithStateModifier` option unnecessary, though it remains available for full backward compatibility. ```go // 1. Define a graph with its own local state @@ -1097,24 +1097,24 @@ type MyGraphState struct { g := compose.NewGraph[string, string](compose.WithGenLocalState(func(ctx context.Context) *MyGraphState { return &MyGraphState{SomeValue: "initial"} })) -// ... add nodes 1 and 2 to the graph ... +// ... add nodes1 and node2 to graph ... -// 2. Compile the graph with a static interrupt point -// This will interrupt the graph itself after the "node_1" node completes. +// 2. Compile graph with static interrupt point +// This will interrupt the graph itself after "node_1" node completes. graph, err := g.Compile(ctx, compose.WithInterruptAfterNodes([]string{"node_1"})) -// 3. Run the graph, which will trigger the static interrupt +// 3. Run graph, this will trigger static interrupt _, err = graph.Invoke(ctx, "start") -// 4. Extract the interrupt context and the graph's state +// 4. Extract interrupt context and graph's state interruptInfo, isInterrupt := compose.ExtractInterruptInfo(err) if isInterrupt { interruptCtx := interruptInfo.InterruptContexts[0] - // The .Info field exposes the graph's current state + // .Info field exposes the graph's current state graphState, ok := interruptCtx.Info.(*MyGraphState) if ok { - // 5. Directly modify the state + // 5. Modify state directly fmt.Printf("Original state value: %s\n", graphState.SomeValue) // Prints "initial" graphState.SomeValue = "a-new-value-from-user" @@ -1128,7 +1128,7 @@ if isInterrupt { ### 3. Agent Interrupt Compatibility -Compatibility with legacy agents is maintained at the data structure level, ensuring that old agent implementations continue to work within the new framework. The key lies in how the `adk.InterruptInfo` and `adk.ResumeInfo` structs are populated. +Compatibility with legacy agents is maintained at the data structure level, ensuring old agent implementations continue to function within the new framework. The key lies in how the `adk.InterruptInfo` and `adk.ResumeInfo` structs are populated. **For End Users (Application Layer):** @@ -1137,16 +1137,16 @@ When receiving an interrupt from an agent, the `adk.InterruptInfo` struct will h - The new, structured `InterruptContexts` field. - The legacy `Data` field, which will contain the original interrupt information (e.g., `ChatModelAgentInterruptInfo` or `WorkflowInterruptInfo`). -This allows end users to gradually migrate their application logic to use the richer `InterruptContexts` while still having access to the old `Data` field when needed. +This allows end users to gradually migrate their application logic to use the richer `InterruptContexts` while still being able to access the old `Data` field when needed. **For Agent Developers:** -When a legacy agent's `Resume` method is called, the `adk.ResumeInfo` struct it receives still contains the now-deprecated embedded `InterruptInfo` field. This field is populated with the same legacy data structures, allowing agent developers to maintain their existing resume logic without immediately updating to the new address-aware APIs. +When a legacy agent's `Resume` method is called, the `adk.ResumeInfo` struct it receives still contains the now-deprecated embedded `InterruptInfo` field. This field is populated with the same legacy data structures, allowing agent developers to maintain their existing resumption logic without immediate updates to the new address-aware APIs. ```go // --- End User Perspective --- -// After an agent run, you receive an interrupt event. +// After agent run, you receive an interrupt event. if event.Action != nil && event.Action.Interrupted != nil { interruptInfo := event.Action.Interrupted @@ -1155,10 +1155,10 @@ if event.Action != nil && event.Action.Interrupted != nil { fmt.Printf("New structured context available: %+v\n", interruptInfo.InterruptContexts[0]) } - // 2. Old way (still works): Access the legacy Data field + // 2. Old way (still works): Access legacy Data field if chatInterrupt, ok := interruptInfo.Data.(*adk.ChatModelAgentInterruptInfo); ok { fmt.Printf("Legacy ChatModelAgentInterruptInfo still accessible.\n") - // ... logic using the old struct + // ... logic using old struct } } @@ -1166,11 +1166,11 @@ if event.Action != nil && event.Action.Interrupted != nil { // Inside a legacy agent's Resume method: func (a *myLegacyAgent) Resume(ctx context.Context, info *adk.ResumeInfo) *adk.AsyncIterator[*adk.AgentEvent] { - // The deprecated embedded InterruptInfo field will still be populated. - // This allows old resume logic to continue working. + // The deprecated embedded InterruptInfo field is still populated. + // This allows old resumption logic to continue working. if info.InterruptInfo != nil { if chatInterrupt, ok := info.InterruptInfo.Data.(*adk.ChatModelAgentInterruptInfo); ok { - // ... existing resume logic relying on the old ChatModelAgentInterruptInfo struct + // ... existing resumption logic relying on old ChatModelAgentInterruptInfo struct fmt.Println("Resuming based on legacy InterruptInfo.Data field.") } } @@ -1179,11 +1179,11 @@ func (a *myLegacyAgent) Resume(ctx context.Context, info *adk.ResumeInfo) *adk.A } ``` -### Migration Advantages +### Migration Benefits -- **Preserve Legacy Behavior**: Existing code will continue to work as it did. Old interrupt patterns will not cause crashes, but they also won't automatically gain new address-aware capabilities without modification. -- **Gradual Adoption**: Teams can selectively enable new features on a case-by-case basis. For example, you can wrap legacy interrupts with `WrapInterruptAndRerunIfNeeded` only in workflows where you need targeted resume. -- **Enhanced Functionality**: The new addressing system provides richer structured context (`InterruptCtx`) for all interrupts, while the old data fields are still populated for full compatibility. +- **Preserved Legacy Behavior**: Existing code will continue to work as it did before. Old interrupt patterns won't cause crashes, but they also won't automatically gain the new address-aware capabilities without modification. +- **Gradual Adoption**: Teams can selectively enable new features on a case-by-case basis. For example, you can wrap legacy interrupts with `WrapInterruptAndRerunIfNeeded` only in the workflows where you need targeted resumption. +- **Enhanced Functionality**: The new addressing system provides richer, structured context (`InterruptCtx`) for all interrupts, while the old data fields are still populated for full compatibility. - **Flexible State Management**: For static graph interrupts, you can choose modern, direct state modification via the `.Info` field, or continue using the old `WithStateModifier` option. -This backward compatibility model ensures a smooth transition for existing users while providing a clear path to adopting the new human-in-the-loop features. +This backward compatibility model ensures a smooth transition for existing users while providing a clear path to adopting the new human-in-the-loop features. \ No newline at end of file diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md index e3d6f1aa740..7029aa204d0 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino ADK: ChatModelAgent' @@ -15,22 +15,22 @@ weight: 1 ## What is ChatModelAgent -`ChatModelAgent` is a core prebuilt Agent in Eino ADK. It encapsulates the complex logic of interacting with large language models (LLMs) and supports using tools to accomplish tasks. +`ChatModelAgent` is a core prebuilt Agent in Eino ADK that encapsulates the complex logic of interacting with Large Language Models (LLMs) and supports using tools to complete tasks. -## ChatModelAgent ReAct Mode +## ChatModelAgent ReAct Pattern -`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 for `ChatModelAgent`, its internal execution follows the ReAct pattern: +`ChatModelAgent` uses the [ReAct](https://react-lm.github.io/) pattern internally, which is designed to solve complex problems by having the ChatModel perform explicit, step-by-step "thinking". After configuring tools for `ChatModelAgent`, its internal execution flow follows the ReAct pattern: - 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 +- LLM returns tool call request (Action) +- ChatModelAgent executes tool (Act) +- It returns the tool result to ChatModel (Observation), then starts a new cycle until ChatModel determines no Tool call is needed and ends. -When no tools are configured, `ChatModelAgent` falls back to a single ChatModel call. +When no tools are configured, `ChatModelAgent` degrades to a single ChatModel call. -Configure tools for ChatModelAgent via ToolsConfig: +You can configure Tools for ChatModelAgent through ToolsConfig: ```go // github.com/cloudwego/eino/adk/chatmodel.go @@ -48,12 +48,12 @@ type ToolsConfig struct { } ``` -ToolsConfig reuses Eino Graph ToolsNodeConfig. See: [Eino: ToolsNode & Tool Guide](/docs/eino/core_modules/components/tools_node_guide). Additionally, it provides the ReturnDirectly configuration - ChatModelAgent will exit directly after calling a tool configured in ReturnDirectly. +ToolsConfig reuses Eino Graph ToolsNodeConfig, see [Eino: ToolsNode & Tool Usage Guide](/docs/eino/core_modules/components/tools_node_guide) for details. Additionally, it provides the ReturnDirectly configuration. ChatModelAgent will exit directly after calling a Tool configured in ReturnDirectly. ## ChatModelAgent Configuration Fields > 💡 -> Note: By default, GenModelInput renders the Instruction using F-String format via adk.GetSessionValues(). To disable this behavior, you can customize the GenModelInput method. +> Note: GenModelInput by default renders the Instruction in F-String format using adk.GetSessionValues(). To disable this behavior, customize the GenModelInput method. ```go type ChatModelAgentConfig struct { @@ -116,21 +116,21 @@ type GenModelInput func(ctx context.Context, instruction string, input *AgentInp - `Name`: Agent name - `Description`: Agent description - `Instruction`: System Prompt when calling ChatModel, supports f-string rendering -- `Model`: The ChatModel used for execution, must support tool calling +- `Model`: ChatModel used for running, must support tool calling - `ToolsConfig`: Tool configuration - - ToolsConfig reuses Eino Graph ToolsNodeConfig. See: [Eino: ToolsNode & Tool Guide](/docs/eino/core_modules/components/tools_node_guide). - - ReturnDirectly: After ChatModelAgent calls a tool configured in ReturnDirectly, it will immediately exit with the result without returning to ChatModel in ReAct mode. If multiple tools match, only the first one returns. Map key is the tool name. - - EmitInternalEvents: When using adk.AgentTool() to call an Agent as a SubAgent via ToolCall, by default this SubAgent won't emit AgentEvents and will only return the final result as ToolResult. -- `GenModelInput`: When the Agent is called, this method transforms `Instruction` and `AgentInput` into Messages for calling ChatModel. The Agent provides a default GenModelInput method: - 1. Prepend `Instruction` as a `System Message` to `AgentInput.Messages` + - ToolsConfig reuses Eino Graph ToolsNodeConfig, see [Eino: ToolsNode & Tool Usage Guide](/docs/eino/core_modules/components/tools_node_guide) for details. + - ReturnDirectly: When ChatModelAgent calls a Tool configured in ReturnDirectly, it will immediately exit with the result, without returning to ChatModel per the react pattern. If multiple Tools are hit, only the first Tool is returned. Map key is the Tool name. + - EmitInternalEvents: When using adk.AgentTool() to treat an Agent as a SubAgent through ToolCall, by default, this SubAgent will not send AgentEvents, only returning the final result as ToolResult. +- `GenModelInput`: When the Agent is called, it uses this method to convert `Instruction` and `AgentInput` into Messages for calling ChatModel. The Agent provides a default GenModelInput method: + 1. Add `Instruction` as `System Message` before `AgentInput.Messages` 2. Render `SessionValues` as variables into the message list from step 1 > 💡 -> The default `GenModelInput` uses pyfmt rendering. Text in the message list is treated as a pyfmt template, meaning '{' and '}' are treated as keywords. To use these characters literally, escape them as '{{' and '}}' +> The default `GenModelInput` uses pyfmt rendering. Text in the message list is treated as a pyfmt template, meaning '{' and '}' in the text are treated as keywords. If you want to input these two characters directly, they need to be escaped as '{{' and '}}'. -- `OutputKey`: When configured, the last Message produced by ChatModelAgent will be stored in `SessionValues` with `OutputKey` as the key -- `MaxIterations`: Maximum number of ChatModel generations in ReAct mode. The Agent will exit with an error if exceeded. Default is 20 -- `Exit`: Exit is a special Tool. When the model calls and executes this tool, ChatModelAgent will exit directly, similar to `ToolsConfig.ReturnDirectly`. ADK provides a default ExitTool implementation: +- `OutputKey`: When configured, the last Message produced by ChatModelAgent running will be set in `SessionValues` with `OutputKey` as the key +- `MaxIterations`: Maximum number of ChatModel generations in react mode. Agent will exit with error when exceeded. Default value is 20 +- `Exit`: Exit is a special Tool. When the model calls this tool and executes it, ChatModelAgent will exit directly, with an effect similar to `ToolsConfig.ReturnDirectly`. ADK provides a default ExitTool implementation for users: ```go type ExitTool struct{} @@ -159,7 +159,7 @@ func (et ExitTool) InvokableRun(ctx context.Context, argumentsInJSON string, _ . } ``` -- `ModelRetryConfig`: When configured, various errors during ChatModel requests (including direct errors and errors during streaming responses) will be retried according to the configured policy. If an error occurs during streaming response, that streaming response will still be returned immediately via AgentEvent. If the error during streaming will be retried according to the policy, consuming the message stream in AgentEvent will yield a `WillRetryError`. Users can handle this error for display purposes, as shown below: +- `ModelRetryConfig`: When configured, various errors during ChatModel request (including direct errors and errors during streaming response) will be retried according to the configured policy. If an error occurs during streaming response, the streaming response will still be returned through AgentEvent immediately. If the error during streaming response will be retried according to the configured policy, consuming the message stream in AgentEvent will get `WillRetryError`. Users can handle this error for corresponding display processing. Example: ```go iterator := agent.Run(ctx, input) @@ -202,9 +202,9 @@ for { ## ChatModelAgent Transfer -`ChatModelAgent` supports converting other Agents' metadata into its own Tools, enabling dynamic Transfer through ChatModel decisions: +`ChatModelAgent` supports converting other Agents' meta information into its own Tools, achieving dynamic Transfer through ChatModel judgment: -- `ChatModelAgent` implements the `OnSubAgents` interface. After using `SetSubAgents` to set sub-Agents for `ChatModelAgent`, it adds a `Transfer Tool` and instructs the ChatModel in the prompt to call this Tool when transfer is needed, using the target AgentName as input. +- `ChatModelAgent` implements the `OnSubAgents` interface. After using `SetSubAgents` to set sub Agents for `ChatModelAgent`, `ChatModelAgent` will add a `Transfer Tool` and instruct ChatModel in the prompt to call this Tool when transfer is needed, using the transfer target AgentName as Tool input. ```go const ( @@ -228,14 +228,14 @@ func genTransferToAgentInstruction(ctx context.Context, agents []Agent) string { } ``` -- `Transfer Tool` execution sets a Transfer Event, specifying the jump to the target Agent, then ChatModelAgent exits. -- Agent Runner receives the Transfer Event and jumps to the target Agent for execution, completing the Transfer operation. +- `Transfer Tool` running sets a Transfer Event, specifying the jump to the target Agent, and ChatModelAgent exits after completion. +- Agent Runner receives the Transfer Event and jumps to the target Agent for execution, completing the Transfer operation ## ChatModelAgent AgentAsTool -When an Agent to be called doesn't need the full execution context and only requires clear input parameters to run correctly, that Agent can be converted to a Tool for `ChatModelAgent` to decide when to call: +When the Agent being called doesn't need a complete running context but only clear and explicit input parameters to run correctly, the Agent can be converted to a Tool for `ChatModelAgent` to judge and call: -- ADK provides utility methods to conveniently convert an Eino ADK Agent to a Tool for ChatModelAgent to call: +- ADK provides utility methods to conveniently convert Eino ADK Agents to Tools for ChatModelAgent to call: ```go // github.com/cloudwego/eino/adk/agent_tool.go @@ -243,7 +243,7 @@ When an Agent to be called doesn't need the full execution context and only requ func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool ``` -- The converted Agent Tool can be registered directly in ChatModelAgent via `ToolsConfig` +- Agents converted to Tools can be registered directly in ChatModelAgent through `ToolsConfig` ```go bookRecommender := NewBookRecommendAgent() @@ -259,19 +259,250 @@ a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ }) ``` +## ChatModelAgent Middleware + +`ChatModelAgentMiddleware` is an extension mechanism for `ChatModelAgent` that allows developers to inject custom logic at various stages of Agent execution: + + + +`ChatModelAgentMiddleware` is defined as an interface. Developers can implement this interface and configure it in `ChatModelAgentConfig` to make it effective in `ChatModelAgent`: + +```go +type ChatModelAgentMiddleware interface { + // ... +} + +a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Handlers: []adk.ChatModelAgentMiddleware{ + &MyMiddleware{}, + }, +}) +``` + +**Using BaseChatModelAgentMiddleware** + +`BaseChatModelAgentMiddleware` provides default empty implementations for all methods. By embedding it, you can override only the methods you need: + +```go +type MyMiddleware struct { + *adk.BaseChatModelAgentMiddleware + // Custom fields + logger *log.Logger +} + +// Only override the methods you need +func (m *MyMiddleware) BeforeModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + m.logger.Printf("Messages count: %d", len(state.Messages)) + return ctx, state, nil +} +``` + +### BeforeAgent + +Called before each Agent run, can be used to modify instructions and tool configuration. ChatModelAgentContext defines the content that can be read and written in BeforeAgent: + +```go +type ChatModelAgentContext struct { + // Instruction is the current Agent's instruction + Instruction string + // Tools is the current configured original tool list + Tools []tool.BaseTool + // ReturnDirectly configures tool name sets that return directly after being called + ReturnDirectly map[string]bool +} + +type ChatModelAgentMiddleware interface { + // ... + BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) + // ... +} +``` + +Example: + +```go +func (m *MyMiddleware) BeforeAgent( + ctx context.Context, + runCtx *adk.ChatModelAgentContext, +) (context.Context, *adk.ChatModelAgentContext, error) { + // Copy runCtx to avoid modifying input + nRunCtx := *runCtx + + // Modify instruction + nRunCtx.Instruction += "\n\nPlease always reply in Chinese." + + // Add tool + nRunCtx.Tools = append(runCtx.Tools, myCustomTool) + + // Set tool to return directly + nRunCtx.ReturnDirectly["my_tool"] = true + + return ctx, &nRunCtx, nil +} +``` + +### BeforeModelRewriteState / AfterModelRewriteState + +Called before/after each model call, can be used to inspect and modify message history. ModelContext defines read-only content, ChatModelAgentState defines read-write content: + +```go +type ModelContext struct { + // Tools contains the list of tools currently configured for the Agent + // Populated at request time, contains tool info that will be sent to the model + Tools []*schema.ToolInfo + + // ModelRetryConfig contains the retry configuration for the model + // Populated from Agent's ModelRetryConfig + ModelRetryConfig *ModelRetryConfig +} + +type ChatModelAgentState struct { + // Messages contains all messages in the current session + Messages []Message +} + +type ChatModelAgentMiddleware interface { + BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) +} +``` + +Example: + +```go +func (m *MyMiddleware) BeforeModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + // Copy state to avoid modifying input + nState := *state + + // Check message history + if len(state.Messages) > 50 { + // Truncate old messages + nState.Messages = state.Messages[len(state.Messages)-50:] + } + return ctx, &nState, nil +} + +func (m *MyMiddleware) AfterModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + // Model response is the last message + lastMsg := state.Messages[len(state.Messages)-1] + m.logger.Printf("Model response: %s", lastMsg.Content) + return ctx, state, nil +} +``` + +### WrapModel + +Wraps model calls, can be used to intercept and modify model input and output: + +```go +type ChatModelAgentMiddleware interface { + WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) +} +``` + +Example: + +```go +func (m *MyMiddleware) WrapModel( + ctx context.Context, + chatModel model.BaseChatModel, + mc *adk.ModelContext, +) (model.BaseChatModel, error) { + return &loggingModel{ + inner: chatModel, + logger: m.logger, + }, nil +} + +type loggingModel struct { + inner model.BaseChatModel + logger *log.Logger +} + +func (m *loggingModel) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) { + m.logger.Printf("Input messages: %d", len(msgs)) + resp, err := m.inner.Generate(ctx, msgs, opts...) + m.logger.Printf("Output: %v, error: %v", resp != nil, err) + return resp, err +} + +func (m *loggingModel) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { + return m.inner.Stream(ctx, msgs, opts...) +} +``` + +### WrapInvokableToolCall / WrapStreamableToolCall + +Wraps tool calls, can be used to intercept and modify tool input and output: + +```go +// InvokableToolCallEndpoint is the function signature for tool calls. +// Middleware developers add custom logic around this Endpoint. +type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) + +// StreamableToolCallEndpoint is the function signature for streaming tool calls. +// Middleware developers add custom logic around this Endpoint. +type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error) + +type ToolContext struct { + // Name indicates the name of the tool being called + Name string + // CallID indicates the ToolCallID of this tool call + CallID string +} + +type ChatModelAgentMiddleware interface { + WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error) + WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error) +} +``` + +Example: + +```go +func (m *MyMiddleware) WrapInvokableToolCall( + ctx context.Context, + endpoint adk.InvokableToolCallEndpoint, + tCtx *adk.ToolContext, +) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + m.logger.Printf("Calling tool: %s (ID: %s)", tCtx.Name, tCtx.CallID) + start := time.Now() + + result, err := endpoint(ctx, argumentsInJSON, opts...) + + m.logger.Printf("Tool %s completed in %v", tCtx.Name, time.Since(start)) + return result, err + }, nil +} +``` + # ChatModelAgent Usage Example -## Scenario +## Scenario Description Create a book recommendation Agent that can recommend relevant books based on user input. -## Implementation +## Code Implementation -### Step 1: Define the Tool +### Step 1: Define Tools -The book recommendation Agent needs a tool `book_search` that can search for books based on user requirements (genre, rating, etc.). +The book recommendation Agent needs a `book_search` tool that can search for books based on user requirements (genre, rating, etc.). -You can easily create tools using the utility methods provided by Eino (see [How to Create a Tool?](/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool)): +Using utility methods provided by Eino makes it easy to create (see [How to create a tool?](/docs/eino/core_modules/components/tools_node_guide/how_to_create_a_tool)): ```go import ( @@ -307,7 +538,7 @@ func NewBookRecommender() tool.InvokableTool { ### Step 2: Create ChatModel -Eino provides various ChatModel implementations (such as openai, gemini, doubao, etc. See [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide)). Here we use openai ChatModel as an example: +Eino provides various ChatModel wrappers (such as openai, gemini, doubao, etc., see [Eino: ChatModel Usage Guide](/docs/eino/core_modules/components/chat_model_guide) for details). Here we use openai ChatModel as an example: ```go import ( @@ -338,7 +569,7 @@ func NewChatModel() model.ToolCallingChatModel { ### Step 3: Create ChatModelAgent -In addition to configuring ChatModel and tools, you need to configure Name and Description to describe the Agent's functionality, as well as Instruction to guide the ChatModel. The Instruction will ultimately be passed to ChatModel as a system message. +In addition to configuring ChatModel and tools, you need to configure Name and Description describing the Agent's function and purpose, as well as the Instruction that instructs the ChatModel. The Instruction will ultimately be passed to ChatModel as a system message. ```go import ( @@ -411,7 +642,7 @@ func main() { } ``` -## Run Result +## Running Result ```yaml message: @@ -438,13 +669,13 @@ usage: &{185 31 216} ====== ``` -# ChatModelAgent Interrupt & Resume +# ChatModelAgent Interrupt and Resume ## Introduction -`ChatModelAgent` is implemented using Eino Graph, so the agent can reuse Eino Graph's Interrupt & Resume capability. +`ChatModelAgent` is implemented using Eino Graph, so it can reuse Eino Graph's Interrupt&Resume capability in the agent. -- On interrupt, return a special error from the tool to trigger Graph interrupt and emit custom info. On resume, Graph will rerun this tool: +- On Interrupt, return a special error in the tool to make the Graph trigger an interrupt and throw custom information. On resume, the Graph will re-run this tool: ```go // github.com/cloudwego/eino/adk/interrupt.go @@ -452,7 +683,7 @@ usage: &{185 31 216} func NewInterruptAndRerunErr(extra any) error ``` -- On resume, support custom ToolOptions to pass extra information to the Tool: +- On Resume, custom ToolOptions are supported for passing additional information to the Tool during resume: ```go import ( @@ -472,9 +703,9 @@ func WithNewInput(input string) tool.Option { ## Example -Below we will add an `ask_for_clarification` tool to the `BookRecommendAgent` from the [ChatModelAgent Usage Example] section above. When the user provides insufficient information for recommendations, the Agent will call this tool to ask the user for more information. `ask_for_clarification` uses the Interrupt & Resume capability to implement "asking" the user. +Below we will build on the code from the [ChatModelAgent Usage Example] section above to add a tool `ask_for_clarification` to `BookRecommendAgent`. When the user provides insufficient information for recommendations, the Agent will call this tool to ask the user for more information. `ask_for_clarification` uses the Interrupt&Resume capability to implement "asking" the user. -### Step 1: Add Tool with Interrupt Support +### Step 1: Add Tool Supporting Interrupt ```go import ( @@ -529,7 +760,7 @@ func NewBookRecommendAgent() adk.Agent { ToolsNodeConfig: compose.ToolsNodeConfig{ Tools: []tool.BaseTool{NewBookRecommender(), NewAskForClarificationTool()}, }, - // Whether to output AgentEvents when calling SubAgent via AgentTool() internally + // Whether to output AgentEvents from SubAgent when Tool internally calls SubAgent via AgentTool() EmitInternalEvents: true, }, }) @@ -539,7 +770,7 @@ func NewBookRecommendAgent() adk.Agent { ### Step 3: Configure CheckPointStore in Agent Runner -Configure `CheckPointStore` in the Runner (using the simplest InMemoryStore in this example), and pass `CheckPointID` when calling the Agent for use during resume. Also, on interrupt, Graph will place `InterruptInfo` in `Interrupted.Data`: +Configure `CheckPointStore` in Runner (the example uses the simplest InMemoryStore), and pass in `CheckPointID` when calling the Agent for use during resume. Also, on interrupt, Graph places `InterruptInfo` in `Interrupted.Data`: ```go func newInMemoryStore() compose.CheckPointStore { @@ -601,9 +832,9 @@ func main() { } ``` -### Run Result +### Running Result -Running will trigger an interrupt +An interrupt will occur after running ``` message: @@ -620,7 +851,7 @@ interrupt happened, info: &{ToolCalls:[{Index: ID:call_3HAobzkJvW3JsTmSHSBR your input here: ``` -After stdin input, retrieve the previous interrupt state from CheckPointStore, combine with the completed input, and continue running +After stdin input, retrieve the previous interrupt state from CheckPointStore and continue running with the completed input ``` new input is: @@ -661,4 +892,4 @@ usage: &{317 20 337} `ChatModelAgent` is the core Agent implementation in ADK, serving as the "thinking" part of applications. It leverages the powerful capabilities of LLMs for reasoning, understanding natural language, making decisions, generating responses, and interacting with tools. -`ChatModelAgent`'s behavior is non-deterministic, dynamically deciding which tools to use or transferring control to other Agents through the LLM. +`ChatModelAgent`'s behavior is non-deterministic, dynamically deciding which tools to use or transferring control to other Agents through LLM. diff --git a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md index a764dd40160..e54b259d0de 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_implementation/deepagents.md @@ -1,17 +1,25 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: DeepAgents' +title: 'Eino ADK: 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 is an out-of-the-box agent solution built on top of ChatModelAgent (see: [Eino ADK: ChatModelAgent](/docs/eino/core_modules/eino_adk/agent_implementation/chat_model)). You don't need to assemble prompts, tools, or context management yourself - you can immediately get a runnable agent while still using ChatModelAgent's extension capabilities to add business features, such as custom tools and middleware. -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. +**Included Features:** + +- **Planning Capability** — Task decomposition and progress tracking through `write_todos` +- **File System** — Provides `read_file`, `write_file`, `edit_file`, `ls`, `glob`, `grep` for reading and writing context +- **Shell Access** — Use `execute` to run commands +- **Sub-Agents** — Delegate work to sub-agents with independent context windows via `task` +- **Smart Default Configuration** — Built-in prompts that teach the model how to efficiently use these tools +- **Context Management** — Automatic summarization for long conversation history, automatic file saving for large outputs + - SummarizationMiddleware, ReductionMiddleware are under development ### ImportPath @@ -25,126 +33,160 @@ 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. +The core concept of DeepAgents is to use a main agent (MainAgent) to coordinate, plan, delegate, or autonomously execute tasks. The main agent uses its built-in ChatModel and a series of tools to interact with the external world or decompose complex tasks to specialized sub-agents (SubAgents). + + + +The diagram above shows the core components of DeepAgents and their relationships: + +- Main Agent: The entry point and commander of the system, receives initial tasks, calls tools in ReAct mode to complete tasks and is responsible for presenting the final results. +- ChatModel (ToolCallingChatModel): Usually a large language model with tool-calling capabilities, responsible for understanding tasks, reasoning, selecting and calling tools. +- Tools: A collection of capabilities available to MainAgent, including: + - WriteTodos: Built-in planning tool for decomposing complex tasks into structured todo lists. + - TaskTool: A special tool that serves as the unified entry point for calling sub-agents. + - BuiltinTools, CustomTools: General tools built into DeepAgents and various tools customized by users according to business needs. +- SubAgents: Responsible for executing specific, independent subtasks, with context isolated from MainAgent. + - GeneralPurpose: A general-purpose sub-agent with the same tools as MainAgent (except TaskTool), used to execute subtasks in a "clean" context. + - CustomSubAgents: Various sub-agents customized by users according to business needs. - +### Built-in Capabilities -Core components and relationships: +#### Filesystem -- 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 +> 💡 +> Currently in alpha state + +When creating DeepAgents, configure the relevant Backend, and DeepAgents will automatically load the corresponding tools: + +``` +type Config struct { + // ... + Backend filesystem.Backend + Shell filesystem.Shell + StreamingShell filesystem.StreamingShell + // ... +} +``` + +
    ExampleRunPath
    + + + + +
    ConfigurationFunctionAdded Tools
    BackendProvides file system access capability, optionalread_file, write_file, edit_file, glob, grep
    ShellProvides Shell capability, optional, mutually exclusive with StreamShellexecute
    StreamingShellProvides Shell capability with streaming results, optional, mutually exclusive with Shellexecute(streaming)
    + +DeepAgents implements built-in filesystem by referencing filesystem middleware. For more detailed capability description of this middleware, see: [Middleware: FileSystem](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem) ### 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: +The Description of WriteTodos describes the principles of task decomposition and planning. The main agent adds a subtask list to the context by calling the WriteTodos tool to inspire subsequent reasoning and execution processes: -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 +1. The model receives user input. +2. The model calls the WriteTodos tool with a task list generated according to the WriteTodos Description. This tool call is added to the context for future reference. +3. The model calls TaskTool according to the todos in the context to complete the first todo. +4. Calls WriteTodos again to update the Todos execution 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. +> For simple tasks, calling WriteTodos every time may have a negative effect. The WriteTodos Description includes some common positive and negative examples to avoid not calling or over-calling WriteTodos. When using DeepAgents, you can add more prompts according to actual business scenarios to make WriteTodos called at appropriate times. > 💡 -> WriteTodos is enabled by default; set `WithoutWriteTodos=true` to disable it. +> WriteTodos will be added to the Agent by default. Configure `WithoutWriteTodos=true` to disable WriteTodos. -### Delegation and SubAgents +### Task Delegation and SubAgents Invocation **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`. +All sub-agents are bound to TaskTool. When the main agent assigns subtasks to sub-agents for processing, it calls TaskTool and specifies which sub-agent is needed and the task to execute. TaskTool then routes the task to the specified sub-agent and returns the result to the main agent after execution. The default Description of TaskTool explains the general rules for calling sub-agents and concatenates the Description of each sub-agent. Developers can customize the Description of TaskTool by configuring `TaskToolDescriptionGenerator`. + +> When users configure Config.SubAgents, these Agents will be bound to TaskTool based on ChatModelAgent's AgentAsTool capability -**Context isolation** +**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. +Context isolation between Agents: + +- Information Transfer: The main agent and sub-agents do not share context. Sub-agents only receive the subtask goals assigned by the main agent, not the entire task processing; the main agent only receives the processing results from sub-agents, not the processing of sub-agents. +- Avoid Pollution: This isolation ensures that the execution process of sub-agents (such as numerous tool calls and intermediate steps) does not "pollute" the main agent's context. The main agent only receives concise, clear 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. +DeepAgents adds a sub-agent by default: general-purpose. general-purpose has the same system prompt and tools as the main agent (except TaskTool). When there is no specialized sub-agent to handle a task, the main agent can call general-purpose to isolate context. Developers can remove this agent by configuring `WithoutGeneralSubAgent=true`. + +### Comparison with Other Agents + +- Compared to ReAct Agent -### Comparison with Other MultiAgent Patterns + - Advantages: DeepAgents strengthens task decomposition and planning through built-in WriteTodos; it also isolates multi-agent contexts, usually performing better in large-scale, multi-step tasks. + - Disadvantages: Making plans and calling sub-agents bring additional model requests, increasing latency and token costs; if task decomposition is unreasonable, it may have a negative effect. +- Compared to Plan-and-Execute -- 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 + - Advantages: DeepAgents provides Plan/RePlan as tools for the main agent to freely call, allowing unnecessary planning to be skipped during tasks, overall reducing model calls and lowering latency and costs. + - Disadvantages: Task planning and delegation are completed in one model call, requiring higher model capabilities, and prompt tuning is relatively more difficult. ## DeepAgents Usage Example -### Scenario +### Scenario Description -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. +Excel Agent is an "intelligent assistant that understands Excel". It first breaks down the problem into steps, then executes and verifies results step by step. It can understand user questions and uploaded file content, propose feasible solutions, and select appropriate tools (system commands, generate and run Python code, web queries, etc.) to complete tasks. -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: +In real business, you can think of Excel Agent as an "Excel expert + automation engineer". When you provide a raw spreadsheet and target description, it will propose a solution and complete the 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 +- **Data Cleaning and Formatting**: Complete deduplication, null value handling, and date format standardization from an Excel file containing large amounts of data. +- **Data Analysis and Report Generation**: Extract monthly sales totals from sales data, aggregate statistics, pivot, and finally generate and export chart reports. +- **Automated Budget Calculation**: Automatically calculate total budget based on budget applications from different departments and generate department budget allocation tables. +- **Data Matching and Merging**: Match and merge customer information tables from multiple different sources to generate a complete customer information database. -Excel Agent structure with DeepAgents: +The structure of Excel Agent built with DeepAgents is as follows: -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 +1. Add ReadFile tool to the main agent, allowing the main agent to view file content and assist in subtask formulation +2. Add Code and WebSearch sub-agents: Code can write python code to operate excel spreadsheets; WebSearch can search for information and summarize. ### 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 +### Execution Results ```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."} +arguments: {"subagent_type":"CodeAgent","description":"Please help me with a CSV file operation task. Specific requirements: 1. Read the questions.csv file in the current directory; 2. Extract the first column data from the CSV file; 3. Save the extracted first column data to a new CSV file named first_column.csv. Please use the pandas library to complete these operations, ensuring the code can execute correctly and handle possible file 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```"} +arguments: {"code":"```python\nimport pandas as pd\nimport os\n\n# Get current working directory and build file path\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 first column data\n first_column = df.iloc[:, 0]\n # Save as new CSV file (without index)\n first_column.to_csv(output_path, index=False)\n print(f\"Successfully saved first column data to: {output_path}\")\nexcept FileNotFoundError:\n print(f\"Error: File not found {input_path}\")\nexcept Exception as e:\n print(f\"Error 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 +tool response: Successfully saved first column data 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: +answer: Task completed. Successfully read the `questions.csv` file in the current directory, extracted the first column data, and saved the result to `first_column.csv`. The specific output path is: `/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. +The code handles path concatenation and exception catching (such as file not found or format errors) to ensure execution stability. name: ExcelAgent path: [{ExcelAgent}] -tool response: Task completed. Read `questions.csv`, extracted the first column, and saved to `first_column.csv`. Output path: +tool response: Task completed. Successfully read the `questions.csv` file in the current directory, extracted the first column data, and saved the result to `first_column.csv`. The specific output path is: `/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. +The code handles path concatenation and exception catching (such as file not found or format errors) to ensure execution stability. name: ExcelAgent path: [{ExcelAgent}] -answer: Successfully extracted the first column from `questions.csv` into `first_column.csv`. Saved at: +answer: Successfully extracted the first column data from the `questions.csv` spreadsheet to a new file `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. +The process handled path concatenation and exception catching (such as file not found, format errors, etc.) to ensure data extraction completeness and file generation stability. If you need to adjust the file path or have further requirements for data format, please 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 index 553e8d3d767..76988341bc5 100644 --- 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 @@ -1,44 +1,44 @@ --- Description: "" -date: "2025-12-03" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: Plan-Execute Agent' -weight: 3 +title: 'Eino ADK: Plan-Execute Agent' +weight: 4 --- -## Overview +## Plan-Execute Agent Overview ### Import Path -`import "github.com/cloudwego/eino/adk/prebuilt/planexecute"` +`import github.com/cloudwego/eino/adk/prebuilt/planexecute` -### What Is Plan-Execute Agent? +### 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. +Plan-Execute Agent is a multi-agent collaboration framework based on the "plan-execute-reflect" paradigm in Eino ADK, designed to solve the step-by-step decomposition, execution, and dynamic adjustment of complex tasks. Through the collaborative work of three core agents: **Planner**, **Executor**, and **Replanner**, it achieves structured task planning, tool call execution, progress evaluation, and dynamic replanning to ultimately accomplish user goals. -It suits multi-step reasoning, tool integration, and strategy adjustment scenarios (e.g., research, complex problem solving, automated workflows). Key advantages: +Plan-Execute Agent is suitable for scenarios requiring multi-step reasoning, tool integration, or dynamic strategy adjustment (such as research analysis, complex problem solving, automated workflows, etc.). Its core advantages are: -- 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 +- **Structured Planning**: Decomposes complex tasks into clear, executable step sequences +- **Iterative Execution**: Completes single-step tasks based on tool calls, accumulating execution results +- **Dynamic Adjustment**: Evaluates in real-time based on execution progress whether the plan needs adjustment or task termination +- **Model and Tool Agnostic**: Compatible with any model supporting tool calls, flexible integration with external tools -### Architecture +### Plan-Execute Agent Structure -Plan-Execute Agent is built from three core agents plus a coordinator, leveraging ChatModelAgent and Workflow Agents capabilities: +Plan-Execute Agent consists of three core agents and a coordinator, built together using ChatModelAgent and WorkflowAgents capabilities provided in ADK: #### 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 +- **Core Function**: Generates initial task plan (structured step sequence) based on user goals +- **Implementation**: + - Uses a model supporting tool calls (e.g., GPT-4) to generate a step list conforming to JSON Schema via `PlanTool` + - Or directly uses a model supporting structured output to generate `Plan` format results +- **Output**: `Plan` object (containing ordered step list), stored in Session for subsequent process use ```go // PlannerConfig provides configuration options for creating a planner agent. @@ -49,7 +49,7 @@ Plan-Execute Agent is built from three core agents plus a coordinator, leveragin 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 + // Can refer to https://github.com/cloudwego/eino-ext/blob/main/components/model/openai/examples/structured/structured.go. ChatModelWithFormattedOutput model.BaseChatModel // ToolCallingChatModel is a model that supports tool calling capabilities. @@ -73,13 +73,13 @@ type PlannerConfig struct { #### 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 +- **Core Function**: Executes the first step in the plan, calling external tools to complete specific tasks +- **Implementation**: Based on `ChatModelAgent`, configured with tool sets (such as search, calculation, database access, etc.) +- **Workflow**: + - Gets current `Plan` and executed steps from Session + - Extracts the first unexecuted step from the plan as the goal + - Calls tools to execute the step, stores results in Session +- **Key Capability**: Supports multi-round tool calls (controlled via `MaxIterations`) to ensure single-step task completion ```go // ExecutorConfig provides configuration options for creating a executor agent. @@ -103,11 +103,11 @@ type ExecutorConfig struct { #### 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 +- **Core Function**: Evaluates execution progress, decides to continue execution (generate new plan) or terminate task (return result) +- **Implementation**: Based on tool calling model, outputs decisions via `PlanTool` (generate new plan) or `RespondTool` (return result) +- **Decision Logic**: + - **Continue Execution**: If goal not achieved, generates new plan containing remaining steps, updates `Plan` in Session + - **Terminate Task**: If goal achieved, calls `RespondTool` to generate final user response ```go type ReplannerConfig struct { @@ -137,10 +137,10 @@ type ReplannerConfig struct { #### 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 +- **Core Function**: Combines the above three agents to form a "plan → execute → replan" loop workflow +- **Implementation**: Combined via `SequentialAgent` and `LoopAgent`: + - Outer `SequentialAgent`: First executes `Planner` to generate initial plan, then enters execute-replan loop + - Inner `LoopAgent`: Loops execution of `Executor` and `Replanner` until task completion or maximum iterations reached ```go // New creates a new plan execute agent with the given configuration. @@ -155,56 +155,58 @@ type Config struct { } ``` -### Workflow +### Plan-Execute Agent Execution Flow -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` +The complete workflow of Plan-Execute Agent is as follows: -## Usage Example +1. **Initialization**: User inputs target task, starts `PlanExecuteAgent` +2. **Planning Phase**: + - `Planner` receives user goal, generates initial `Plan` (step list) + - `Plan` stored in Session (`PlanSessionKey`) +3. **Execute-Replan Loop** (controlled by `LoopAgent`): + - **Execution Step**: `Executor` extracts first step from `Plan`, calls tools to execute, result stored in Session (`ExecutedStepsSessionKey`) + - **Reflection Step**: `Replanner` evaluates executed steps and results: + - If goal achieved: Calls `RespondTool` to generate final response, exits loop + - If needs to continue: Generates new `Plan` and updates Session, enters next loop iteration +4. **Termination Condition**: Task completed (`Replanner` returns result) or maximum iterations reached (`MaxIterations`) -### Scenario +## Plan-Execute Agent Usage Example -Build a "research" agent: +### Scenario Description -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 +Implement a "research" Agent: -### Code +1. **Planner**: Plans detailed steps for research goals +2. **Executor**: Executes the first step in the plan, uses search tool (duckduckgo) when necessary +3. **Replanner**: Evaluates execution results, adjusts plan if information is insufficient, otherwise generates final summary -#### 1. Initialize model and tools +### Code Implementation + +#### 1. Initialize Model and Tools ```go -// Initialize an OpenAI model that supports tool calling +// Initialize OpenAI model supporting tool calls 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 + Model: "gpt-4o", // Must support tool calls }) if err != nil { - log.Fatalf("failed to init model: %v", err) + log.Fatalf("Failed to initialize model: %v", err) } return cm } -// Initialize search tool (used by Executor) +// Initialize search tool (for Executor to call) func newSearchTool(ctx context.Context) tool.BaseTool { config := &duckduckgo.Config{ - MaxResults: 20, // limit to 20 results - Region: duckduckgo.RegionWT, - Timeout: 10 * time.Second, + MaxResults: 20, // Limit to return 20 results + Region: duckduckgo._RegionWT_, + Timeout: 10 * time._Second_, } tool, err := duckduckgo.NewTextSearchTool(ctx, config) if err != nil { - log.Fatalf("failed to init search tool: %v", err) + log.Fatalf("Failed to initialize search tool: %v", err) } return tool } @@ -215,11 +217,11 @@ func newSearchTool(ctx context.Context) tool.BaseTool { ```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 + ToolCallingChatModel: model, // Use tool calling model to generate plan + ToolInfo: &planexecute.PlanToolInfo, // Default Plan tool schema }) if err != nil { - log.Fatalf("failed to create Planner: %v", err) + log.Fatalf("Failed to create Planner: %v", err) } return planner } @@ -229,19 +231,19 @@ func newPlanner(ctx context.Context, model model.ToolCallingChatModel) adk.Agent ```go func newExecutor(ctx context.Context, model model.ToolCallingChatModel) adk.Agent { - // Configure Executor toolset (only search tool here) + // Configure Executor tool set (only includes search tool) 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 + Model: model, + ToolsConfig: toolsConfig, + MaxIterations: 5, // ChatModel runs at most 5 times }) if err != nil { - log.Fatalf("failed to create Executor: %v", err) + log.Fatalf("Failed to create Executor: %v", err) } return executor } @@ -252,27 +254,27 @@ func newExecutor(ctx context.Context, model model.ToolCallingChatModel) adk.Agen ```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 + ChatModel: model, // Use tool calling model to evaluate progress }) if err != nil { - log.Fatalf("failed to create Replanner: %v", err) + log.Fatalf("Failed to create Replanner: %v", err) } return replanner } ``` -#### 5. Compose into PlanExecuteAgent +#### 5. Combine into PlanExecuteAgent ```go func newPlanExecuteAgent(ctx context.Context) adk.Agent { model := newToolCallingModel(ctx) - // Instantiate the three core agents + // Instantiate three core agents planner := newPlanner(ctx, model) executor := newExecutor(ctx, model) replanner := newReplanner(ctx, model) - // Compose into PlanExecuteAgent (fixed execute–replan max iterations = 10) + // Combine into PlanExecuteAgent (fixed execute-replan max iterations 10) planExecuteAgent, err := planexecute.NewPlanExecuteAgent(ctx, &planexecute.PlanExecuteConfig{ Planner: planner, Executor: executor, @@ -280,7 +282,7 @@ func newPlanExecuteAgent(ctx context.Context) adk.Agent { MaxIterations: 10, }) if err != nil { - log.Fatalf("failed to compose PlanExecuteAgent: %v", err) + log.Fatalf("Failed to combine PlanExecuteAgent: %v", err) } return planExecuteAgent } @@ -312,7 +314,7 @@ func main() { // Create Runner to execute agent runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent, EnableStreaming: true}) - // User input goal + // User inputs target task userInput := []adk.Message{ schema.UserMessage("Research and summarize the latest developments in AI for healthcare in 2024, including key technologies, applications, and industry trends."), } @@ -325,7 +327,7 @@ func main() { break } if event.Err != nil { - log.Printf("error: %v", event.Err) + log.Printf("Execution error: %v", event.Err) break } // Print agent output (plan, execution results, final response, etc.) @@ -336,7 +338,7 @@ func main() { } ``` -### Run Result +### Execution Results ```markdown 2025/09/08 11:47:42 @@ -344,157 +346,38 @@ func main() { {"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 ...] +{"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..."},...]} +... ``` ## 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. +Plan-Execute Agent effectively improves task completion reliability and efficiency through the "plan-execute-reflect" closed-loop workflow, decomposing complex tasks into executable steps, combining tool calls with dynamic adjustment. Its core advantages are: -- 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 +- **Structured Task Decomposition**: Reduces cognitive load of complex problems +- **Tool Integration Capability**: Seamlessly interfaces with external tools (search, calculation, database, etc.) +- **Dynamic Adaptability**: Adjusts strategies in real-time based on execution 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. +Through the `PlanExecuteAgent` provided by Eino ADK, developers can quickly build agent systems capable of handling complex tasks, suitable for various scenarios such as research analysis, office automation, intelligent customer service, etc. ## FAQ ### Error [NodeRunError] no tool call -Planner/Replanner must generate the plan via tool calling. Check: +Planner / Replanner must generate plans through tool calls. When this error occurs, please 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) +1. Whether the model used supports forced tool calls (e.g., openai tool_choice="required") +2. Whether the model's eino-ext wrapper has been upgraded to the latest version (e.g., old version ark sdk does not support forced tool calls) ### Error [NodeRunError] unexpected tool call -ChatModel registered for Replanner should not carry extra tools via `WithTools`. Clear any extra tools if present. +The ChatModel registered with Replanner should not carry additional tools via the WithTools method. If this is the case, please clear the tools. ### Error [NodeRunError] unmarshal plan error -Planner/Replanner config uses both PlanTool and NewPlan to generate the plan: +In Planner / Replanner config, plans are generated based on two fields: PlanTool and NewPlan: -- PlanTool describes the Plan to the model -- NewPlan builds the plan struct used to unmarshal the model's JSON output +- PlanTool serves as the Plan description provided to the model +- NewPlan method serves as the framework's plan builder, used to unmarshal the Plan returned by the model to this struct for subsequent step execution -If this error occurs, ensure PlanTool's field descriptions match the struct returned by NewPlan, then rerun. +When this error occurs, please check whether the field descriptions provided in PlanTool match the struct fields returned in the NewPlan method, align them and re-run. 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 index 5bb55f3b8ae..bca088eb1cf 100644 --- 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 @@ -1,71 +1,71 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: Supervisor Agent' -weight: 4 +title: 'Eino ADK: Supervisor Agent' +weight: 3 --- ## Supervisor Agent Overview ### Import Path -`import "github.com/cloudwego/eino/adk/prebuilt/supervisor"` +`import github.com/cloudwego/eino/adk/prebuilt/supervisor` -### What Is Supervisor Agent? +### 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. +Supervisor Agent is a centralized multi-agent collaboration pattern consisting of one Supervisor Agent and multiple SubAgents. The Supervisor is responsible for task allocation, monitoring the execution process of sub-agents, and summarizing results and making decisions after sub-agents complete; sub-agents focus on executing specific tasks and automatically transfer task control back to the Supervisor via WithDeterministicTransferTo after completion. -This pattern fits scenarios requiring dynamic coordination among specialized agents to complete complex tasks, such as: +This pattern is suitable for scenarios that require dynamic coordination of multiple 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.) +- Research project management (Supervisor assigns research, experiment, report writing tasks to different sub-agents). +- Customer service processes (Supervisor assigns tasks to technical support, after-sales, sales sub-agents based on user question types). ### Supervisor Agent Structure -Core structure of the Supervisor pattern: +The core structure of Supervisor pattern is as follows: -- **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 +- **Supervisor Agent**: As the collaboration core, has task allocation logic (such as rule-based or LLM decision), can include sub-agents under management via `SetSubAgents`. +- **SubAgents**: Each sub-agent is enhanced with WithDeterministicTransferTo, with `ToAgentNames` preset to the Supervisor name, ensuring automatic transfer back to Supervisor after task completion. ### Supervisor Agent Features -1. **Deterministic callback**: when a subagent finishes without interruption, WithDeterministicTransferTo automatically triggers a Transfer event to hand control back to the Supervisor, preventing collaboration flow interruption. -2. **Centralized control**: Supervisor manages subagents and dynamically adjusts task assignments based on subagent results (e.g., assign to another subagent or generate final result directly). -3. **Loose coupling**: subagents can be developed, tested, and replaced independently as long as they implement the `Agent` interface and are bound to the Supervisor. -4. **Interrupt/resume support**: if subagents or the Supervisor implement `ResumableAgent`, the collaboration can resume after interrupts, preserving task context continuity. +1. **Deterministic Callback**: After sub-agent execution completes (not interrupted), WithDeterministicTransferTo automatically triggers Transfer event, transferring task control back to Supervisor, avoiding collaboration flow interruption. +2. **Centralized Control**: Supervisor uniformly manages sub-agents, can dynamically adjust task allocation based on sub-agent execution results (such as assigning to other sub-agents or directly generating final results). +3. **Loosely Coupled Extension**: Sub-agents can be independently developed, tested, and replaced; just ensure they implement the Agent interface and bind to Supervisor to join the collaboration flow. +4. **Support for Interrupt and Resume**: If sub-agent or Supervisor supports `ResumableAgent` interface, collaboration flow can resume after interruption, maintaining task context continuity. -### Supervisor Agent Run Flow +### Supervisor Agent Execution Flow -Typical collaboration flow of the Supervisor pattern: +The typical collaboration flow of Supervisor pattern is as follows: -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 via Transfer event to a designated subagent (e.g., "ResearchAgent") -3. **Execute**: Subagent performs its task (e.g., research LLM key milestones) and generates result events -4. **Auto-callback**: upon completion, WithDeterministicTransferTo triggers a Transfer event back to Supervisor -5. **Process result**: Supervisor receives subagent results and decides next steps (e.g., transfer to "WriterAgent" for further processing, or output final result directly) +1. **Task Start**: Runner triggers Supervisor to run, inputs initial task (e.g., "Complete a report on LLM development history"). +2. **Task Allocation**: Supervisor transfers task to designated sub-agent (e.g., "Research Agent") via Transfer event based on task requirements. +3. **Sub-Agent Execution**: Sub-agent executes specific task (e.g., researches LLM key milestones) and generates execution result events. +4. **Automatic Callback**: After sub-agent completes, WithDeterministicTransferTo triggers Transfer event, transferring task back to Supervisor. +5. **Result Processing**: Supervisor receives sub-agent results, decides next step (e.g., assign to "Report Writing Agent" to continue processing, or directly output final result). ## Supervisor Agent Usage Example -### Scenario +### Scenario Description Create a research report generation system: -- **Supervisor**: given a research topic from user input, assigns tasks to "ResearchAgent" and "WriterAgent", and aggregates the final report -- **ResearchAgent**: generates a research plan (e.g., key stages of LLM development) -- **WriterAgent**: writes a complete report based on the research plan +- **Supervisor**: Based on user input research topic, assigns tasks to "Research Agent" and "Writer Agent", and summarizes final report. +- **Research Agent**: Responsible for generating research plan (e.g., key stages of LLM development). +- **Writer Agent**: Responsible for writing complete report based on research plan. ### Code Implementation -#### Step 1: Implement SubAgents +#### Step 1: Implement Sub-Agents -First create two subagents responsible for research and writing tasks: +First create two sub-agents, responsible for research and writing tasks respectively: ```go -// ResearchAgent: generate a research plan +// Research Agent: Generates research plan func NewResearchAgent(model model.ToolCallingChatModel) adk.Agent { agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{ Name: "ResearchAgent", @@ -78,7 +78,7 @@ Output ONLY the plan, no extra text.`, return agent } -// WriterAgent: write a report based on the plan +// Writer Agent: Writes report based on research plan func NewWriterAgent(model model.ToolCallingChatModel) adk.Agent { agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{ Name: "WriterAgent", @@ -94,10 +94,10 @@ Output ONLY the report, no extra text.`, #### Step 2: Implement Supervisor Agent -Create the Supervisor Agent with task assignment logic (simplified here as rule-based: first assign to ResearchAgent, then to WriterAgent): +Create Supervisor Agent, define task allocation logic (simplified here as rule-based: first assign to Research Agent, then assign to Writer Agent): ```go -// ReportSupervisor: coordinate research and writing tasks +// Supervisor Agent: Coordinates research and writing tasks func NewReportSupervisor(model model.ToolCallingChatModel) adk.Agent { agent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{ Name: "ReportSupervisor", @@ -117,9 +117,9 @@ Workflow: } ``` -#### Step 3: Compose Supervisor and SubAgents +#### Step 3: Combine Supervisor and Sub-Agents -Use `NewSupervisor` to compose the Supervisor and subagents: +Use `NewSupervisor` to combine Supervisor and sub-agents: ```go import ( @@ -141,12 +141,12 @@ func main() { Model: "gpt-4o", }) - // 2. Create subagents and Supervisor + // 2. Create sub-agents and Supervisor researchAgent := NewResearchAgent(model) writerAgent := NewWriterAgent(model) reportSupervisor := NewReportSupervisor(model) - // 3. Compose Supervisor and subagents + // 3. Combine Supervisor and sub-agents supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{ Supervisor: reportSupervisor, SubAgents: []adk.Agent{researchAgent, writerAgent}, @@ -174,7 +174,7 @@ func main() { } ``` -### Run Result +### Execution Results ```markdown Agent[ReportSupervisor]: @@ -191,57 +191,10 @@ Agent[ResearchAgent]: 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] @@ -249,46 +202,20 @@ 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. - +Large Language Models (LLMs) represent one of the most transformative technological innovations of the 21st century... ## 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. - +A **Large Language Model (LLM)** is a type of machine learning model designed to process and generate human language... -## 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]: +## 7. Conclusion: A Revolution in Five Years +The history of LLMs is a story of exponential progress: from the transformer's 2017 invention to ChatGPT's 2022 viral explosion... +## References +- Devlin, J., et al. (2018). *BERT: Pre-training of deep bidirectional transformers for language understanding*. NAACL. +... =========== Agent[WriterAgent]: successfully transferred to agent [ReportSupervisor] @@ -297,46 +224,46 @@ successfully transferred to agent [ReportSupervisor] ## WithDeterministicTransferTo -### What Is 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. +`WithDeterministicTransferTo` is an Agent enhancement tool provided by Eino ADK, used to inject task transfer capability into Agents. It allows developers to preset fixed task transfer paths for target Agents. When the Agent completes its task (not interrupted), it automatically generates a Transfer event to transfer the task flow to the preset target Agent. -This capability is the foundation of the Supervisor Agent collaboration pattern, ensuring subagents reliably hand control back to the Supervisor, forming a "assign-execute-feedback" closed-loop collaboration flow. +This capability is the foundation for building the Supervisor Agent collaboration pattern, ensuring sub-agents can reliably transfer task control back to the Supervisor after execution, forming a "allocate-execute-feedback" closed-loop collaboration flow. ### WithDeterministicTransferTo Core Implementation -#### Config Structure +#### Configuration Structure -Define core parameters for task transfer via `DeterministicTransferConfig`: +Define core task transfer parameters through `DeterministicTransferConfig`: ```go // Wrapper method func AgentWithDeterministicTransferTo(_ context.Context, config *DeterministicTransferConfig) Agent -// Config details +// Configuration details type DeterministicTransferConfig struct { - Agent Agent // Target agent to enhance - ToAgentNames []string // Destination agent names to transfer to after completion + Agent Agent // Target Agent to be enhanced + ToAgentNames []string // List of target Agent names to transfer to after task completion } ``` -- `Agent`: the original agent to add transfer capability to -- `ToAgentNames`: destination agent names (in order) to automatically transfer to when the agent completes without interruption +- `Agent`: The original Agent that needs transfer capability added. +- `ToAgentNames`: When `Agent` completes task and is not interrupted, automatically transfers task to target Agent name list (transfers in order). #### Agent Wrapping -WithDeterministicTransferTo wraps the original agent. Depending on whether it implements `ResumableAgent` (supports interrupt/resume), it returns `agentWithDeterministicTransferTo` or `resumableAgentWithDeterministicTransferTo`, ensuring enhanced capability is compatible with the agent's original features (like `Resume` method). +WithDeterministicTransferTo wraps the original Agent. Based on whether it implements the `ResumableAgent` interface (supports interrupt and resume), it returns `agentWithDeterministicTransferTo` or `resumableAgentWithDeterministicTransferTo` instance respectively, ensuring enhanced capability is compatible with Agent's original functions (such as `Resume` method). -The wrapped agent overrides `Run` method (and `Resume` for `ResumableAgent`), appending Transfer events after the original agent's event stream: +The wrapped Agent overrides the `Run` method (for `ResumableAgent`, also overrides `Resume` method), appending Transfer events to the original Agent's event stream: ```go -// Wrapper for a regular Agent +// Wrapper for regular Agent type agentWithDeterministicTransferTo struct { - agent Agent // Original agent - toAgentNames []string // Destination agent names + agent Agent // Original Agent + toAgentNames []string // Target Agent name list } -// Run: execute original agent and append Transfer event after completion +// Run method: Executes original Agent task, appends Transfer event after task completion func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] { aIter := a.agent.Run(ctx, input, options...) @@ -349,15 +276,15 @@ func (a *agentWithDeterministicTransferTo) Run(ctx context.Context, input *Agent } ``` -For `ResumableAgent`, additionally implement `Resume` method to ensure deterministic transfer on resume completion: +For `ResumableAgent`, additionally implements `Resume` method, ensuring deterministic transfer still triggers after resume execution: ```go type resumableAgentWithDeterministicTransferTo struct { - agent ResumableAgent // Resumable original agent - toAgentNames []string // Destination agent names + agent ResumableAgent // Original Agent supporting resume + toAgentNames []string // Target Agent name list } -// Resume: resume original agent and append Transfer event after completion +// Resume method: Resumes execution of original Agent task, appends Transfer event 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]() @@ -366,23 +293,23 @@ func (a *resumableAgentWithDeterministicTransferTo) Resume(ctx context.Context, } ``` -#### Append Transfer Event to Event Stream +#### Event Stream Append Transfer Event -`appendTransferAction` is the core logic for deterministic transfer. It consumes the original agent's event stream and, when the agent task finishes normally (not interrupted), automatically generates and sends Transfer events to destination agents: +`appendTransferAction` is the core logic implementing deterministic transfer. It consumes the original Agent's event stream and automatically generates and sends Transfer events to target Agents after the Agent task ends normally (not interrupted): ```go func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent], generator *AsyncGenerator[*AgentEvent], toAgentNames []string) { defer func() { - // Exception handling: capture panic and pass error via event + // Exception handling: Capture panic and pass error via event if panicErr := recover(); panicErr != nil { generator.Send(&AgentEvent{Err: safe.NewPanicErr(panicErr, debug.Stack())}) } - generator.Close() // Close generator when event stream ends + generator.Close() // Event stream ends, close generator }() interrupted := false - // 1. Forward all original agent events + // 1. Forward all events from original Agent for { event, ok := aIter.Next() if !ok { // Original event stream ended @@ -390,7 +317,7 @@ func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent] } generator.Send(event) // Forward event to caller - // Check for interrupt (e.g., InterruptAction) + // Check if interruption occurred (e.g., InterruptAction) if event.Action != nil && event.Action.Interrupted != nil { interrupted = true } else { @@ -398,19 +325,19 @@ func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent] } } - // 2. If not interrupted and destination agents exist, generate Transfer events + // 2. If not interrupted and target Agent exists, generate Transfer event if !interrupted && len(toAgentNames) > 0 { for _, toAgentName := range toAgentNames { - // Generate transfer messages (system tip + Transfer action) + // Generate transfer message (system prompt + Transfer action) aMsg, tMsg := GenTransferMessages(ctx, toAgentName) - // Send system tip event (notify user of task transfer) + // Send system prompt event (notify user of task transfer) aEvent := EventFromMessage(aMsg, nil, schema.Assistant, "") generator.Send(aEvent) // Send Transfer action event (trigger task transfer) tEvent := EventFromMessage(tMsg, nil, schema.Tool, tMsg.ToolName) tEvent.Action = &AgentAction{ TransferToAgent: &TransferToAgentAction{ - DestAgentName: toAgentName, // Destination agent name + DestAgentName: toAgentName, // Target Agent name }, } generator.Send(tEvent) @@ -421,12 +348,12 @@ func appendTransferAction(ctx context.Context, aIter *AsyncIterator[*AgentEvent] **Key Logic**: -- **Event forwarding**: all events from the original agent (thinking, tool calls, output results) are forwarded intact, ensuring business logic is unaffected -- **Interrupt check**: if the agent is interrupted during execution (e.g., `InterruptAction`), Transfer is not triggered (interrupt means task did not complete normally) -- **Transfer event generation**: after task completes normally, for each `ToAgentNames`, generate two events: - 1. System tip event (`schema.Assistant` role): notify user that task will transfer to destination agent - 2. Transfer action event (`schema.Tool` role): carry `TransferToAgentAction` to trigger ADK runtime to transfer task to the agent specified by `DestAgentName` +- **Event Forwarding**: All events generated by the original Agent (such as thinking, tool calls, output results) are fully forwarded, ensuring business logic is unaffected. +- **Interruption Check**: If Agent is interrupted during execution (e.g., `InterruptAction`), Transfer is not triggered (interruption is considered task not completed normally). +- **Transfer Event Generation**: After task ends normally, two events are generated for each `ToAgentNames`: + 1. System prompt event (`schema.Assistant` role): Notifies user that task will be transferred to target Agent. + 2. Transfer action event (`schema.Tool` role): Carries `TransferToAgentAction`, triggers ADK runtime to transfer task to the Agent corresponding to `DestAgentName`. ## Summary -WithDeterministicTransferTo provides reliable task transfer capability for agents, serving as the core foundation for building the Supervisor pattern; the Supervisor pattern achieves efficient multi-agent collaboration through centralized coordination and deterministic callback, significantly reducing development and maintenance costs for complex tasks. Combining both, developers can quickly build flexible, extensible multi-agent systems. +WithDeterministicTransferTo provides reliable task transfer capability for Agents, which is the core foundation for building Supervisor pattern; Supervisor pattern achieves efficient collaboration between multiple Agents through centralized coordination and deterministic callbacks, significantly reducing development and maintenance costs for complex tasks. By combining both, developers can quickly build flexible, scalable multi-Agent systems. 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 index 619d2ad330a..0c57ea1982d 100644 --- a/content/en/docs/eino/core_modules/eino_adk/agent_interface.md +++ b/content/en/docs/eino/core_modules/eino_adk/agent_interface.md @@ -1,9 +1,9 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK: Agent Interface' +title: 'Eino ADK: Agent Abstraction' weight: 3 --- @@ -23,14 +23,14 @@ type Agent interface { - - - + + +
    MethodDescription
    NameThe name of the Agent, serving as its identifier
    DescriptionDescription of the Agent's capabilities, mainly used to help other Agents understand and determine this Agent's responsibilities or functions
    RunThe core execution method of the Agent, returns an iterator through which the caller can continuously receive events produced by the Agent
    NameThe name of the Agent, serving as the Agent's identifier
    DescriptionDescription of the Agent's capabilities, mainly used for other Agents to understand and determine this Agent's responsibilities or functions
    RunThe core execution method of the Agent, returns an iterator through which callers can continuously receive events produced by the Agent
    ## AgentInput -The Run method accepts AgentInput as the Agent's input: +The Run method receives AgentInput as the Agent's input: ```go type AgentInput struct { @@ -41,7 +41,7 @@ type AgentInput struct { type Message = *schema.Message ``` -Agents typically center around a ChatModel, so the Agent's input is defined as `Messages`, which is the same type used when calling Eino ChatModel. `Messages` can include user instructions, dialogue history, background knowledge, example data, and any other data you wish to pass to the Agent. For example: +Agents typically have ChatModel as their core, so the Agent's input is defined as `Messages`, which is the same type as when calling Eino ChatModel. `Messages` can include user instructions, conversation history, background knowledge, sample data, or any other data you want to pass to the Agent. For example: ```go import ( @@ -58,15 +58,15 @@ input := &adk.AgentInput{ } ``` -`EnableStreaming` is used to **suggest** the output mode to the Agent, but it is not a mandatory constraint. Its core idea is to control the behavior of components that support both streaming and non-streaming output, such as ChatModel, while components that only support one output method will not be affected by `EnableStreaming`. Additionally, the `AgentOutput.IsStreaming` field indicates the actual output type. The runtime behavior is: +`EnableStreaming` is used to **suggest** the output mode to the Agent, but it is not a mandatory constraint. Its core idea is to control the behavior of components that support both streaming and non-streaming output, such as ChatModel, while `EnableStreaming` does not affect components that only support one output method. Additionally, the `AgentOutput.IsStreaming` field indicates the actual output type. The runtime behavior is: -- When `EnableStreaming=false`, for components that can output both streaming and non-streaming, the non-streaming mode that returns the complete result at once will be used. -- When `EnableStreaming=true`, for components inside the Agent that can output streaming (such as ChatModel calls), results should be returned incrementally as a stream. If a component does not naturally support streaming, it can still work in its original non-streaming manner. +- When `EnableStreaming=false`, components that can output both streaming and non-streaming will use the non-streaming mode that returns complete results at once. +- When `EnableStreaming=true`, components within the Agent that can output streaming (such as ChatModel calls) should return results progressively as a stream. If a component naturally doesn't support streaming, it can still work in its original non-streaming way. -As shown in the diagram below, ChatModel can output either streaming or non-streaming, while Tool can only output non-streaming: +As shown in the diagram below, ChatModel can output both streaming and non-streaming, while Tool can only output non-streaming: - When `EnableStream=false`, both output non-streaming -- When `EnableStream=true`, ChatModel outputs streaming, while Tool still outputs non-streaming since it doesn't have streaming capability. +- When `EnableStream=true`, ChatModel outputs streaming, Tool still outputs non-streaming because it doesn't have streaming capability. @@ -77,9 +77,9 @@ As shown in the diagram below, ChatModel can output either streaming or non-stre Eino ADK provides some commonly defined Options for users: - `WithSessionValues`: Set cross-Agent read/write data -- `WithSkipTransferMessages`: When configured, when the Event is Transfer SubAgent, the messages in the Event will not be appended to History +- `WithSkipTransferMessages`: When configured, if the Event is a Transfer to SubAgent, the messages in the Event will not be appended to History -Eino ADK provides two methods `WrapImplSpecificOptFn` and `GetImplSpecificOptions` for Agents to wrap and read custom `AgentRunOption`. +Eino ADK provides `WrapImplSpecificOptFn` and `GetImplSpecificOptions` methods for Agents to wrap and read custom `AgentRunOptions`. When using the `GetImplSpecificOptions` method to read `AgentRunOptions`, AgentRunOptions that don't match the required type (like options in the example) will be ignored. @@ -109,7 +109,7 @@ func (m *MyAgent) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.Ag } ``` -Additionally, AgentRunOption has a `DesignateAgent` method. Calling this method allows you to specify which Agents the Option takes effect on when calling a multi-Agent system: +Additionally, AgentRunOption has a `DesignateAgent` method. Calling this method allows you to specify which Agent the Option takes effect on when calling a multi-Agent system: ```go func genOpt() { @@ -134,12 +134,12 @@ func (ai *AsyncIterator[T]) Next() (T, bool) { } ``` -It represents an asynchronous iterator (asynchronous means there is no synchronization control between production and consumption), allowing the caller to consume a series of events produced by the Agent during execution in an ordered, blocking manner. +It represents an asynchronous iterator (asynchronous means there is no synchronization control between production and consumption), allowing callers to consume a series of events produced by the Agent in an ordered, blocking manner. - `AsyncIterator` is a generic struct that can be used to iterate over any type of data. Currently in the Agent interface, the iterator type returned by the Run method is fixed as `AsyncIterator[*AgentEvent]`. This means that every element you get from this iterator will be a pointer to an `AgentEvent` object. `AgentEvent` will be explained in detail in the following sections. -- The main way to interact with the iterator is by calling its `Next()` method. This method's behavior is blocking. Each time you call `Next()`, the program pauses execution until one of the following two situations occurs: - - The Agent produces a new `AgentEvent`: The `Next()` method returns this event, and the caller can process it immediately. - - The Agent actively closes the iterator: When the Agent will no longer produce any new events (usually when the Agent finishes running), it closes this iterator. At this point, the `Next()` call ends blocking and returns false in the second return value, informing the caller that iteration has ended. +- The main way to interact with the iterator is by calling its `Next()` method. This method is **blocking** - each call to `Next()` will pause execution until one of the following two situations occurs: + - Agent produces a new `AgentEvent`: The `Next()` method returns this event, and the caller can immediately process it. + - Agent actively closes the iterator: When the Agent will no longer produce any new events (usually when the Agent finishes running), it closes the iterator. At this point, the `Next()` call will end blocking and return false in the second return value, telling the caller that iteration has ended. Typically, you need to use a for loop to process `AsyncIterator`: @@ -155,7 +155,7 @@ for { } ``` -`AsyncIterator` can be created by `NewAsyncIteratorPair`. The other parameter returned by this function, `AsyncGenerator`, is used to produce data: +`AsyncIterator` can be created by `NewAsyncIteratorPair`, which returns another parameter `AsyncGenerator` for producing data: ```go // github.com/cloudwego/eino/adk/utils.go @@ -163,7 +163,7 @@ for { func NewAsyncIteratorPair[T any]() (*AsyncIterator[T], *AsyncGenerator[T]) ``` -Agent.Run returns AsyncIterator to allow the caller to receive a series of AgentEvents produced by the Agent in real-time. Therefore, Agent.Run usually runs the Agent in a Goroutine to immediately return the AsyncIterator for the caller to listen to: +Agent.Run returns AsyncIterator to let callers receive a series of AgentEvents produced by the Agent in real-time. Therefore, Agent.Run typically runs the Agent in a Goroutine to immediately return the AsyncIterator for the caller to listen to: ```go import "github.com/cloudwego/eino/adk" @@ -185,7 +185,7 @@ func (m *MyAgent) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.Ag ## AgentWithOptions -Using the `AgentWithOptions` method allows you to perform some common configurations in Eino ADK Agent. +Using the `AgentWithOptions` method allows you to make some general configurations in Eino ADK Agents. Unlike `AgentRunOption`, `AgentWithOptions` takes effect before running and does not support custom options. @@ -194,14 +194,14 @@ Unlike `AgentRunOption`, `AgentWithOptions` takes effect before running and does func AgentWithOptions(ctx context.Context, agent Agent, opts ...AgentOption) Agent ``` -Currently built-in configurations supported by Eino ADK: +Currently built-in supported configurations in Eino ADK include: -- `WithDisallowTransferToParent`: Configure that this SubAgent is not allowed to Transfer to ParentAgent, which will trigger the SubAgent's `OnDisallowTransferToParent` callback method -- `WithHistoryRewriter`: When configured, this Agent will rewrite the received context information through this method before execution +- `WithDisallowTransferToParent`: Configures that this SubAgent is not allowed to Transfer to ParentAgent, which will trigger the SubAgent's `OnDisallowTransferToParent` callback method +- `WithHistoryRewriter`: When configured, the Agent will rewrite the received context information through this method before execution # AgentEvent -AgentEvent is the core event data structure produced by the Agent during its execution. It contains the Agent's meta information, output, behavior, and errors: +AgentEvent is the core event data structure produced by the Agent during its run. It contains the Agent's metadata, output, actions, and errors: ```go // github.com/cloudwego/eino/adk/interface.go @@ -218,7 +218,7 @@ type AgentEvent struct { Err error } -// EventFromMessage builds a standard event +// EventFromMessage constructs a regular event func EventFromMessage(msg Message, msgStream MessageStream, role schema.RoleType, toolName string) *AgentEvent ``` @@ -233,7 +233,7 @@ type RunStep struct { ``` - `AgentName` indicates which Agent instance produced the current AgentEvent. -- `RunPath` records the complete call chain to reach the current Agent. `RunPath` is a slice of `RunStep` that records all `AgentName`s in order from the initial entry Agent to the Agent that produced the current event. +- `RunPath` records the complete call chain to reach the current Agent. `RunPath` is a slice of `RunStep` that sequentially records all `AgentNames` from the initial entry Agent to the current Agent producing the event. ## AgentOutput @@ -262,22 +262,22 @@ type MessageVariant struct { } ``` -The type `MessageVariant` of the `MessageOutput` field is a core data structure with the main functions of: +The `MessageVariant` type of the `MessageOutput` field is a core data structure with main functions: -1. Unified handling of streaming and non-streaming messages: `IsStreaming` is a flag. A value of true indicates that the current `MessageVariant` contains a streaming message (read from MessageStream), while false indicates it contains a non-streaming message (read from Message): +1. Unified handling of streaming and non-streaming messages: `IsStreaming` is a flag. When true, it indicates the current `MessageVariant` contains a streaming message (read from MessageStream). When false, it indicates a non-streaming message (read from Message): - - Streaming: Returns a series of message fragments over time that eventually form a complete message (MessageStream). + - Streaming: Returns a series of message fragments progressively over time, eventually forming a complete message (MessageStream). - Non-streaming: Returns a complete message at once (Message). -2. Providing convenient metadata access: The Message struct contains some important meta information internally, such as the message's Role (Assistant or Tool). To quickly identify message types and sources, MessageVariant elevates these commonly used metadata to the top level: +2. Convenient metadata access: The Message struct contains some important metadata, such as the message's Role (Assistant or Tool). To quickly identify message type and source, MessageVariant elevates these commonly used metadata to the top level: - `Role`: The role of the message, Assistant / Tool - `ToolName`: If the message role is Tool, this field directly provides the tool's name. -The benefit of this is that when code needs to route or make decisions based on message type, it doesn't need to deeply parse the specific content of the Message object. It can directly get the required information from the top-level fields of MessageVariant, thus simplifying the logic and improving code readability and efficiency. +The benefit of this is that when code needs to route or make decisions based on message type, it doesn't need to deeply parse the specific content of the Message object - it can directly get the needed information from MessageVariant's top-level fields, simplifying logic and improving code readability and efficiency. ## AgentAction -An Agent producing an Event containing AgentAction can control multi-Agent collaboration, such as immediate exit, interruption, transfer, etc.: +When an Agent produces an Event containing AgentAction, it can control multi-Agent collaboration, such as immediate exit, interrupt, jump, etc.: ```go // github.com/cloudwego/eino/adk/interface.go @@ -313,7 +313,7 @@ func NewExitAction() *AgentAction { } ``` -2. Transfer: When an Agent produces a Transfer Action, it will transfer to the target Agent to run +2. Transfer: When an Agent produces a Transfer Action, it will jump to the target Agent to run ```go func NewTransferToAgentAction(destAgentName string) *AgentAction { @@ -321,7 +321,7 @@ func NewTransferToAgentAction(destAgentName string) *AgentAction { } ``` -3. Interrupt: When an Agent produces an Interrupt Action, it will interrupt the Runner's execution. Since interruption can occur at any position and unique information needs to be passed out during interruption, the Action provides an `Interrupted` field for the Agent to set custom data. When the Runner receives an Action with non-empty Interrupted, it considers an interruption has occurred. The internal mechanism of Interrupt & Resume is relatively complex and will be detailed in the [Eino ADK: Agent Runner] - [Eino ADK: Interrupt & Resume] section. +3. Interrupt: When an Agent produces an Interrupt Action, it will interrupt the Runner's execution. Since interrupts can occur at any position and need to pass unique information when interrupting, the Action provides an `Interrupted` field for Agents to set custom data. When the Runner receives an Action with non-empty Interrupted, it considers an interrupt has occurred. The internal mechanism of Interrupt & Resume is relatively complex and will be elaborated in the [Eino ADK: Agent Runner] - [Eino ADK: Interrupt & Resume] section. ```go // For example, when ChatModelAgent interrupts, it sends the following AgentEvent: @@ -332,4 +332,59 @@ h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{ }}) ``` -4. Break Loop: When a sub-Agent of LoopAgent emits a BreakLoopAction, the corresponding LoopAgent will stop looping and exit normally. +4. Break Loop: When a child Agent of LoopAgent emits a BreakLoopAction, the corresponding LoopAgent will stop looping and exit normally. + +# Language Settings + +ADK provides a `SetLanguage` function to set the language for built-in prompts. This affects the language of prompts generated by all ADK built-in components and middleware. This capability was introduced in [alpha/08](https://github.com/cloudwego/eino/releases/tag/v0.8.0-alpha.13) version. + +## API + +```go +// Language represents the language setting for ADK built-in prompts +type Language uint8 + +const ( + // LanguageEnglish represents English (default) + LanguageEnglish Language = iota + // LanguageChinese represents Chinese + LanguageChinese +) + +// SetLanguage sets the language for ADK built-in prompts +// The default language is English (if not explicitly set) +func SetLanguage(lang Language) error +``` + +## Usage Example + +```go +import "github.com/cloudwego/eino/adk" + +// Set to Chinese +err := adk.SetLanguage(adk.LanguageChinese) +if err != nil { + // Handle error +} + +// Set to English (default) +err = adk.SetLanguage(adk.LanguageEnglish) +``` + +## Scope of Effect + +Language settings affect the built-in prompts of the following components: + + + + + + + +
    Component/MiddlewareAffected Prompts
    FileSystem MiddlewareFile system tool descriptions, system prompts, execution tool prompts
    Reduction MiddlewareTool result truncation/cleanup prompt text
    Skill MiddlewareSkill system prompts, skill tool descriptions
    ChatModelAgentBuilt-in system prompts
    + +> 💡 +> It is recommended to set the language during program initialization because the language setting takes effect globally. Changing the language at runtime may result in mixed-language prompts within the same session. + +> 💡 +> The language setting only affects ADK built-in prompts. Your custom prompts (such as Agent's Instruction) need to handle internationalization on your own. diff --git a/content/en/docs/eino/ecosystem_integration/_index.md b/content/en/docs/eino/ecosystem_integration/_index.md index 9f8bda16c56..27b0baf8f62 100644 --- a/content/en/docs/eino/ecosystem_integration/_index.md +++ b/content/en/docs/eino/ecosystem_integration/_index.md @@ -3,17 +3,17 @@ Description: "" date: "2026-01-20" lastmod: "" tags: [] -title: 'Eino: Ecosystem Integration' +title: 'Eino: Component Integration' weight: 6 --- -## Ecosystem Integration +## Component 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) -- ollama: [ChatModel - Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) +- openai: [ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) +- ark: [ChatModel - ARK](https://bytedance.larkoffice.com/wiki/WUzzwaX8ricGwZk1i1mcJHHNnEl) +- ollama: [ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) ### Document diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/_index.md b/content/en/docs/eino/ecosystem_integration/callbacks/_index.md index 91730eec94d..7d87afed10d 100644 --- a/content/en/docs/eino/ecosystem_integration/callbacks/_index.md +++ b/content/en/docs/eino/ecosystem_integration/callbacks/_index.md @@ -1,9 +1,26 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Callbacks -weight: 0 +weight: 5 --- +# Callbacks Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + +
    Component NameChinese DocsEnglish Docs
    APMPlusREADME_zh.mdREADME.md
    CozeLoopREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md b/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md deleted file mode 100644 index 0376afeb9cf..00000000000 --- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -Description: "" -date: "2025-03-04" -lastmod: "" -tags: [] -title: Callback - APMPlus -weight: 0 ---- - -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 - -import ( - "context" - "log" - - "github.com/cloudwego/eino-ext/callbacks/apmplus" - "github.com/cloudwego/eino/callbacks" -) - -func main() { - ctx := context.Background() - // create apmplus handler - cbh, showdown, err := apmplus.NewApmplusHandler(&apmplus.Config{ - Host: "apmplus-cn-beijing.volces.com:4317", - AppKey: "xxx", - ServiceName: "eino-app", - Release: "release/v0.0.1", - }) - if err != nil { - log.Fatal(err) - } - - // set apmplus as global callback - callbacks.AppendGlobalHandlers(cbh) - - g := NewGraph[string,string]() - /* - * compose and run graph - */ - - // wait until all traces and metrics are flushed before exit - showdown(ctx) -} -``` - -You can view traces and metrics in [APMPlus](https://console.volcengine.com/apmplus-server): - - diff --git a/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md b/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md deleted file mode 100644 index 960bd16fa76..00000000000 --- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Callback - CozeLoop -weight: 0 ---- - -# **CozeLoop Callback** - -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** - -- Implements `github.com/cloudwego/eino/internel/callbacks.Handler` interface -- Easy integration with Eino applications - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/callbacks/cozeloop -``` - -## **Quick Start** - -```go -package main -import ( - "context" - "log" - - ccb "github.com/cloudwego/eino-ext/callbacks/cozeloop" - "github.com/cloudwego/eino/callbacks" - "github.com/coze-dev/cozeloop-go" -) - -func main() { - // 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) -} -``` - -## **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 deleted file mode 100644 index 6e0e5a33d9e..00000000000 --- a/content/en/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -Description: "" -date: "2025-03-18" -lastmod: "" -tags: [] -title: Callback - Langfuse -weight: 0 ---- - -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 - -import ( - "github.com/cloudwego/eino-ext/callbacks/langfuse" - "github.com/cloudwego/eino/callbacks" -) - -func main() { - cbh, flusher := NewLangfuseHandler(&Config{ - Host: "https://cloud.langfuse.com", - PublicKey: "pk-xxx", - SecretKey: "sk-xxx", - }) - - 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 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 ece88e1fd03..869e343e0f9 100644 --- a/content/en/docs/eino/ecosystem_integration/chat_model/_index.md +++ b/content/en/docs/eino/ecosystem_integration/chat_model/_index.md @@ -1,8 +1,33 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: ChatModel -weight: 0 +weight: 1 --- + +# ChatModel Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    ARKREADME_zh.mdREADME.md
    ARKBotREADME_zh.mdREADME.md
    ClaudeREADME_zh.mdREADME.md
    DeepSeekREADME_zh.mdREADME.md
    GeminiREADME_zh.mdREADME.md
    OllamaREADME_zh.mdREADME.md
    OpenAIREADME_zh.mdREADME.md
    QianfanREADME_zh.mdREADME.md
    QwenREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md b/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md index 556c5694a44..9c86caefe77 100644 --- a/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md +++ b/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md @@ -1,13 +1,16 @@ --- Description: "" -date: "2026-01-22" +date: "2026-03-02" lastmod: "" tags: [] title: AgenticModel - ARK -weight: 0 +weight: 2 --- -A Volcengine Ark model implementation based on [Eino](https://github.com/cloudwego/eino), implementing the `AgenticModel` component interface. This enables the model to seamlessly integrate into Eino's Agent capabilities, providing enhanced natural language processing and generation features. +An implementation of the Volcengine Ark model based on [Eino](https://github.com/cloudwego/eino), implementing the `AgenticModel` component interface. This enables the model to seamlessly integrate into Eino's Agent capabilities, providing enhanced natural language processing and generation features. + +> 💡 +> This component was introduced in version alpha/09 ## Features @@ -27,7 +30,7 @@ go get github.com/cloudwego/eino-ext/components/model/agenticark@latest ## Quick Start -Here is a quick example of how to use `AgenticModel`: +Here's a quick example of how to use `AgenticModel`: ```go package main @@ -84,7 +87,7 @@ type Config struct { // Optional. Default: 10 minutes Timeout *time.Duration - // HTTPClient specifies the client used to send HTTP requests. + // HTTPClient specifies the client to use for sending HTTP requests. // If HTTPClient is set, Timeout will not be used. // Optional. Default: &http.Client{Timeout: Timeout} HTTPClient *http.Client @@ -93,7 +96,7 @@ type Config struct { // Optional. Default: 2 RetryTimes *int - // BaseURL specifies the base URL for the Ark service + // BaseURL specifies the base URL of the Ark service // Optional. Default: "https://agenticark.cn-beijing.volces.com/api/v3" BaseURL string @@ -101,8 +104,8 @@ type Config struct { // Optional. Default: "cn-beijing" Region string - // The following three fields are related to authentication - either APIKey or AccessKey/SecretKey pair is required - // For details on authentication, see: https://www.volcengine.com/docs/82379/1298459 + // The following three fields are authentication related - either APIKey or AccessKey/SecretKey pair is required + // For authentication details, see: https://www.volcengine.com/docs/82379/1298459 // If both are provided, APIKey takes precedence APIKey string @@ -122,14 +125,14 @@ type Config struct { MaxTokens *int // Temperature specifies the sampling temperature to use - // It is generally recommended to modify this or TopP, but not both - // Range: 0.0 to 1.0. Higher values make output more random + // It's generally recommended to modify this or TopP, but not both + // Range: 0.0 to 1.0. Higher values make the output more random // Optional. Default: 1.0 Temperature *float64 - // TopP controls diversity via nucleus sampling - // It is generally recommended to modify this or Temperature, but not both - // Range: 0.0 to 1.0. Lower values make output more focused + // TopP controls diversity through nucleus sampling + // It's generally recommended to modify this or Temperature, but not both + // Range: 0.0 to 1.0. Lower values make the output more focused // Optional. Default: 0.7 TopP *float64 @@ -139,7 +142,7 @@ type Config struct { Stop []string // FrequencyPenalty penalizes tokens based on frequency to prevent repetition - // Range: -2.0 to 2.0. Positive values reduce likelihood of repetition + // Range: -2.0 to 2.0. Positive values reduce the likelihood of repetition // Optional. Default: 0 FrequencyPenalty *float64 @@ -148,11 +151,11 @@ type Config struct { LogitBias map[string]int32 // PresencePenalty penalizes tokens based on presence to prevent repetition - // Range: -2.0 to 2.0. Positive values increase likelihood of new topics + // Range: -2.0 to 2.0. Positive values increase the likelihood of new topics // Optional. Default: 0 PresencePenalty *float64 - // LogProbs specifies whether to return log probabilities of output tokens. + // LogProbs specifies whether to return log probabilities of the output tokens. LogProbs *bool // TopLogProbs specifies the number of most likely tokens to return at each token position, each with an associated log probability. @@ -166,7 +169,7 @@ type Config struct { // Enabled by default. Thinking *responses.ResponsesThinking - // Reasoning specifies the reasoning effort of the model. + // Reasoning specifies the reasoning effort for the model. // Optional. // EnablePassBackReasoning controls whether the model passes back reasoning items in the next request. @@ -182,11 +185,11 @@ type Config struct { // Optional. ParallelToolCalls *bool - // ServerTools specifies the server-side tools available to the model. + // ServerTools specifies server-side tools available to the model. // Optional. ServerTools []*ServerToolConfig - // MCPTools specifies the MCP tools available to the model. + // MCPTools specifies MCP tools available to the model. // Optional. MCPTools []*responses.ToolMcp @@ -194,7 +197,7 @@ type Config struct { // Optional. Cache *CacheConfig - // CustomHeader HTTP headers passed when requesting the model + // CustomHeader HTTP headers to pass when requesting the model CustomHeader map[string]string } ``` @@ -203,7 +206,7 @@ type Config struct { ### Tool Calling -`AgenticModel` supports tool calling, including function tools, MCP tools, and server tools. +`AgenticModel` supports tool calling, including Function Tools, MCP Tools, and Server Tools. #### Function Tool Example diff --git a/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md b/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md index 7ed9bb5d4cc..dad4026503f 100644 --- a/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md +++ b/content/en/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md @@ -1,13 +1,16 @@ --- Description: "" -date: "2026-01-22" +date: "2026-03-02" lastmod: "" tags: [] title: AgenticModel - OpenAI -weight: 0 +weight: 1 --- -An OpenAI model implementation based on [Eino](https://github.com/cloudwego/eino), implementing the `AgenticModel` component interface. This enables seamless integration of the model into Eino's Agent capabilities, providing enhanced natural language processing and generation features. +An implementation of the OpenAI model based on [Eino](https://github.com/cloudwego/eino), implementing the `AgenticModel` component interface. This enables the model to seamlessly integrate into Eino's Agent capabilities, providing enhanced natural language processing and generation features. + +> 💡 +> This component was introduced in version alpha/09. ## Features @@ -27,7 +30,7 @@ go get github.com/cloudwego/eino-ext/components/model/agenticopenai@latest ## Quick Start -Here is a quick example of how to use `AgenticModel`: +Here's a quick example of how to use `AgenticModel`: ```go package main @@ -128,7 +131,7 @@ type Config struct { // Optional. Timeout *time.Duration - // HTTPClient specifies the client used to send HTTP requests. + // HTTPClient specifies the client to use for sending HTTP requests. // Optional. HTTPClient *http.Client @@ -145,14 +148,14 @@ type Config struct { MaxTokens *int // Temperature controls the randomness of the model output. - // Higher values (e.g., 0.8) make the output more random, while lower values (e.g., 0.2) make the output more focused and deterministic. + // Higher values (e.g., 0.8) make output more random, while lower values (e.g., 0.2) make it more focused and deterministic. // Range: 0.0 to 2.0. // Optional. Temperature *float32 // TopP controls diversity through nucleus sampling. // It specifies the cumulative probability threshold for token selection. - // It is recommended to modify this or Temperature, but not both. + // It's recommended to modify this or Temperature, but not both. // Range: 0.0 to 1.0. // Optional. TopP *float32 @@ -169,7 +172,7 @@ type Config struct { // Optional. Reasoning *responses.ReasoningParam - // Store specifies whether to store the response on the server. + // Store specifies whether to store responses on the server. // Optional. Store *bool @@ -177,19 +180,19 @@ type Config struct { // Optional. MaxToolCalls *int - // ParallelToolCalls specifies whether multiple tool calls are allowed in a single turn. + // ParallelToolCalls specifies whether to allow multiple tool calls in a single turn. // Optional. ParallelToolCalls *bool - // Include specifies the list of additional fields to include in the response. + // Include specifies a list of additional fields to include in the response. // Optional. Include []responses.ResponseIncludable - // ServerTools specifies the server-side tools available to the model. + // ServerTools specifies server-side tools available to the model. // Optional. ServerTools []*ServerToolConfig - // MCPTools specifies the Model Context Protocol tools available to the model. + // MCPTools specifies Model Context Protocol tools available to the model. // Optional. MCPTools []*responses.ToolMcpParam @@ -198,8 +201,8 @@ type Config struct { // Optional. CustomHeader map[string]string - // ExtraFields specifies extra fields to be added directly to the HTTP request body. - // This allows support for vendor-specific or future parameters not yet explicitly supported. + // ExtraFields specifies additional fields that will be added directly to the HTTP request body. + // This allows supporting vendor-specific or future parameters not yet explicitly supported. // Optional. ExtraFields map[string]any } @@ -211,7 +214,7 @@ type Config struct { `AgenticModel` supports tool calling, including Function Tools, MCP Tools, and Server Tools. -#### Function Tools Example +#### Function Tool Example ```go package main @@ -336,7 +339,7 @@ func main() { } ``` -#### Server Tools Example +#### Server Tool Example ```go package main 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 deleted file mode 100644 index ce6739c017b..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md +++ /dev/null @@ -1,1634 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: ChatModel - ARK -weight: 0 ---- - -## **ARK Model** - -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 different models: - -- **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 -- Supports chat completion, image generation, and Response API -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration - -## Quick Install - -```bash -go get github.com/cloudwego/eino-ext/components/model/ark@latest -``` - -### Quick Start - -Here's a quick example of how to use `ChatModel`: - -```go -package main - -import ( - "context" - "encoding/json" - "errors" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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", ark.GetArkRequestID(msg)) - respBody, _ = json.MarshalIndent(msg, " ", " ") - log.Printf("body: %s \n", string(respBody)) -} -``` - -### Configuration - -`ChatModel` can be configured using the `ark.ChatModelConfig` struct: - -```go -type ChatModelConfig struct { - // Timeout specifies the maximum duration to wait for API responses - // Optional. Default: 10 minutes - Timeout *time.Duration `json:"timeout"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following three fields are about authentication - either APIKey or AccessKey/SecretKey pair is required - // For authentication details, see: https://www.volcengine.com/docs/82379/1298459 - // APIKey takes precedence if both are provided - APIKey string `json:"api_key"` - AccessKey string `json:"access_key"` - SecretKey string `json:"secret_key"` - - // The following fields correspond to Ark's chat completion API parameters - // Ref: https://www.volcengine.com/docs/82379/1298454 - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // MaxTokens limits the maximum number of tokens that can be generated in the chat completion and the range of values is [0, 4096] - // 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 to 1.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: 0.7 - 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"` - - // 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"` - - // 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"` - - // CustomHeader the http header passed to model when requesting model - CustomHeader map[string]string `json:"custom_header"` -} -``` - -## Image Generation - -### Quick Start - -Here's a quick example of how to use `ImageGenerationModel`: - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "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++ - } -} -``` - -### Configuration - -`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 - // Required - APIKey string `json:"api_key"` - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // Timeout specifies the maximum duration to wait for API responses - // If HTTPClient is set, Timeout will not be used. - // Optional. Default: 10 minutes - 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"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following fields correspond to Ark's image generation API parameters - // Ref: https://www.volcengine.com/docs/82379/1541523 - - // Size specifies the dimensions of the generated image. - // It can be a resolution keyword (e.g., "1K", "2K", "4K") or a custom resolution - // in "{width}x{height}" format (e.g., "1920x1080"). - // When using custom resolutions, the total pixels must be between 1280x720 and 4096x4096, - // and the aspect ratio (width/height) must be between 1/16 and 16. - // Optional. Defaults to "2048x2048". - Size string `json:"size"` - - // SequentialImageGeneration determines if the model should generate a sequence of images. - // Possible values: - // - "auto": The model decides whether to generate multiple images based on the prompt. - // - "disabled": Only a single image is generated. - // Optional. Defaults to "disabled". - SequentialImageGeneration SequentialImageGeneration `json:"sequential_image_generation"` - - // SequentialImageGenerationOption sets the maximum number of images to generate when - // SequentialImageGeneration is set to "auto". - // The value must be between 1 and 15. - // Optional. Defaults to 15. - SequentialImageGenerationOption *model.SequentialImageGenerationOptions `json:"sequential_image_generation_option"` - - // ResponseFormat specifies how the generated image data is returned. - // Possible values: - // - "url": A temporary URL to download the image (valid for 24 hours). - // - "b64_json": The image data encoded as a Base64 string in the response. - // Optional. Defaults to "url". - ResponseFormat ImageResponseFormat `json:"response_format"` - - // DisableWatermark, if set to true, removes the "AI Generated" watermark - // from the bottom-right corner of the image. - // Optional. Defaults to false. - DisableWatermark bool `json:"disable_watermark"` -} -``` - -## Examples - -### Text Generation - -```go -package main - -import ( - "context" - "encoding/json" - "errors" - "io" - "log" - "os" - - "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) - } - - 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)) -} -``` - -### Multimodal Support (Image Understanding) - -```go -package main - -import ( - "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-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) -} -``` - -### 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" -) - -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) -} -``` - -### Tool Calling - -```go -package main - -import ( - "context" - "log" - "os" - - "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) -} -``` - -### Image Generation - -```go -package main - -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() - - // 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 Prefix Cache - -```go -package main - -import ( - "context" - "encoding/json" - "io" - "log" - "os" - - "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) - } - - 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 Prefix Cache - -```go -package main - -import ( - "context" - "encoding/json" - "errors" - "io" - "log" - "os" - - "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"), - 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 - -} -``` - -When you don't want to use cache for messages that have already been cached, you can call `InvalidateMessageCaches(messages []*schema.Message) error` to clear the cache markers in the messages. This way, when the ARK SDK constructs the Responses API request, it cannot find the corresponding ResponseID based on the cache marker, and thus will not assign a value to PreviousResponseID in the Responses API. - -### 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" -) - -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 Cache - -```go -package main - -import ( - "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"), - 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) - } -} -``` - -When you don't want to use cache for messages that have already been cached, you can call `InvalidateMessageCaches(messages []*schema.Message) error` to clear the cache markers in the messages. This way, when the ARK SDK constructs the Responses API request, it cannot find the corresponding ResponseID based on the cache marker, and thus will not assign a value to PreviousResponseID in the Responses API. - -## **Usage** - -### ChatModel - -#### **Component Initialization** - -The Ark model is initialized through the `NewChatModel` function with the following main configuration parameters: - -```go -import "github.com/cloudwego/eino-ext/components/model/ark" - -model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{ - // Service configuration - BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // Service URL - Region: "cn-beijing", // Region - HTTPClient: httpClient, // Custom HTTP client - Timeout: &timeout, // Timeout - RetryTimes: &retries, // Retry times - - // Authentication configuration (choose one) - APIKey: "your-api-key", // API Key authentication - AccessKey: "your-ak", // AK/SK authentication - SecretKey: "your-sk", - - // Model configuration - Model: "endpoint-id", // Model endpoint ID - - // Generation parameters - MaxTokens: &maxTokens, // Maximum generation length - Temperature: &temp, // Temperature - TopP: &topP, // Top-P sampling - Stop: []string{}, // Stop words - FrequencyPenalty: &fp, // Frequency penalty - PresencePenalty: &pp, // Presence penalty - - // Advanced parameters - LogitBias: map[string]int{}, // Token bias - CustomHeader: map[string]string{}, // HTTP custom header -}) -``` - -#### **Generate Conversation** - -Conversation generation supports both normal mode and streaming mode: - -```go -func main() { - // Normal mode - response, err := model.Generate(ctx, messages) - - // Streaming mode - stream, err := model.Stream(ctx, messages) -} -``` - -Message format example: - -> Note: Whether multimodal images are supported depends on the specific model - -```go -func main() { - imgUrl := "https://example.com/image.jpg" - messages := []*schema.Message{ - // System message - schema.SystemMessage("You are an assistant"), - - // Multimodal message (with image) - { - Role: schema.User, - UserInputMultiContent: []schema.MessageInputPart{ - { - Type: schema.ChatMessagePartTypeText, - Text: "What is this image?", - }, - { - Type: schema.ChatMessagePartTypeImageURL, - Image: &schema.MessageInputImage{ - MessagePartCommon: schema.MessagePartCommon{ - URL: &imgUrl, - }, - Detail: schema.ImageURLDetailAuto, - }, - }, - }, - }, - } -} -``` - -#### **Tool Calling** - -Supports binding tools: - -```go -// Define tools -tools := []*schema.ToolInfo{ - { - Name: "search", - Desc: "Search for information", - ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ - "query": { - Type: schema.String, - Desc: "Search keywords", - Required: true, - }, - }), - }, -} - -// Bind tools -err := model.BindTools(tools) -``` - -> For tool-related information, please refer to [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide) - -#### **Complete Usage Examples** - -##### **Direct Conversation** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/model/ark" - "github.com/cloudwego/eino/schema" -) - -func main() { - ctx := context.Background() - - timeout := 30 * time.Second - // Initialize model - model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{ - APIKey: "your-api-key", - Region: "cn-beijing", - Model: "endpoint-id", - Timeout: &timeout, - }) - if err != nil { - panic(err) - } - - // Prepare messages - messages := []*schema.Message{ - schema.SystemMessage("You are an assistant"), - schema.UserMessage("Introduce Volcengine"), - } - - // Generate response - response, err := model.Generate(ctx, messages) - if err != nil { - panic(err) - } - - // Process response - println(response.Content) - - // Get token usage - if usage := response.ResponseMeta.Usage; usage != nil { - println("Prompt Tokens:", usage.PromptTokens) - println("Completion Tokens:", usage.CompletionTokens) - println("Total Tokens:", usage.TotalTokens) - } -} -``` - -##### **Streaming Conversation** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/model/ark" - "github.com/cloudwego/eino/schema" -) - -func main() { - ctx := context.Background() - - // Initialize model - model, err := ark.NewChatModel(ctx, &ark.ChatModelConfig{ - APIKey: "your-api-key", - Model: "ep-xxx", - }) - if err != nil { - panic(err) - } - - // Prepare messages - messages := []*schema.Message{ - schema.SystemMessage("You are an assistant"), - schema.UserMessage("Introduce Eino"), - } - - // Get streaming response - reader, err := model.Stream(ctx, messages) - if err != nil { - panic(err) - } - defer reader.Close() // Remember to close - - // Process streaming content - for { - chunk, err := reader.Recv() - if err != nil { - break - } - print(chunk.Content) - } -} -``` - -### ImageGeneration Model - -#### Component Initialization - -The Seedream (seedream4.0) image generation model is initialized through the `NewImageGenerationModel` function: - -```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{ - // Service configuration - BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // Service URL - Region: "cn-beijing", // Region - HTTPClient: httpClient, // Custom HTTP client - Timeout: &timeout, // Timeout - RetryTimes: &retries, // Retry times - - // Model configuration - APIKey: os.Getenv("ARK_API_KEY"), - Model: os.Getenv("ARK_MODEL_ID"), - - // Generation configuration - Size: "1K", // Specify generated image size - SequentialImageGeneration: ark.SequentialImageGenerationAuto, // Determine whether to generate a set of images - SequentialImageGenerationOption: &model.SequentialImageGenerationOptions{ // Maximum number of images when generating a set - MaxImages: ptr(2), - }, - ResponseFormat: ark.ImageResponseFormatURL, // Image data return method, URL or Base64 - DisableWatermark: false, // Whether to include "AI Generated" watermark -}) -``` - -#### Generate Conversation - -The image generation model also supports both normal mode and streaming mode: - -```go -func main() { - // Normal mode - response, err := model.Generate(ctx, messages) - - // Streaming mode - stream, err := model.Stream(ctx, messages) -} -``` - -#### Complete Usage Examples - -##### Direct Conversation - -```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" -) - -// Pointer helper function -func ptr[T any](v T) *T { - return &v -} - -func main() { - // Initialize model - 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) - } - - // Prepare messages - inMsgs := []*schema.Message{ - { - Role: schema.User, - Content: "generate two images of a cat", - }, - } - - // Generate image - msg, err := imageGenerationModel.Generate(ctx, inMsgs) - if err != nil { - log.Fatalf("Generate failed, err=%v", err) - } - - // Print image information - log.Printf("\ngenerate output: \n") - respBody, _ := json.MarshalIndent(msg, " ", " ") - log.Printf(" body: %s\n", string(respBody)) -} -``` - -##### Streaming Conversation - -```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" -) - -// Pointer helper function -func ptr[T any](v T) *T { - return &v -} - -func main() { - // Initialize model - 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) - } - - // Prepare messages - inMsgs := []*schema.Message{ - { - Role: schema.User, - Content: "generate two images of a cat", - }, - } - - // Stream generate images - sr, err := imageGenerationModel.Stream(ctx, inMsgs) - if err != nil { - log.Fatalf("Stream failed, err=%v", err) - } - - // Process streaming information - 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++ - } -} -``` - -### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/ark/examples) - -## **Related Documentation** - -- [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide) -- [ChatModel - OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai) -- [ChatModel - Ollama](/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 deleted file mode 100644 index b02d5c9f68a..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md +++ /dev/null @@ -1,333 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -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 to enhance natural language processing and generation. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/model.ToolCallingChatModel` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/arkbot@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Ark Bot: - -```go - -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} - -``` - -## **Configuration** - -You can configure the model using the `arkbot.Config` struct: - -```go - -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 - 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"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following three fields are about authentication - either APIKey or AccessKey/SecretKey pair is required - // For authentication details, see: https://www.volcengine.com/docs/82379/1298459 - // APIKey takes precedence if both are provided - APIKey string `json:"api_key"` - AccessKey string `json:"access_key"` - SecretKey string `json:"secret_key"` - - // The following fields correspond to Ark's chat completion API parameters - // Ref: https://www.volcengine.com/docs/82379/1298454 - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // MaxTokens limits the maximum number of tokens that can be generated in the chat completion. - // 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 to 1.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: 0.7 - 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"` - - // 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"` - - // 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"` - - // CustomHeader the http header passed to model when requesting model - CustomHeader map[string]string `json:"custom_header"` - - // 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"` - - // ResponseFormat specifies the format that the model must output. - ResponseFormat *ResponseFormat `json:"response_format,omitempty"` -} -``` - -## **Request 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 {} -``` - -## **Examples** - -### **Text Generation** - -```go - -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} - -``` - -### **Streaming Generation** - -```go - -package main - -import ( - "context" - "encoding/json" - "fmt" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} - -``` - -## **More Information** - -- [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 deleted file mode 100644 index 032b27454a6..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md +++ /dev/null @@ -1,833 +0,0 @@ ---- -Description: "" -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 to enhance natural language processing and generation. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/model.Model` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/claude@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Claude model: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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 - } - - // 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** - -You can configure the model using the `claude.ChatModelConfig` struct: - -```go -type Config struct { - // ByBedrock indicates whether to use Bedrock Service - // Required for Bedrock - ByBedrock bool - - // AccessKey is your Bedrock API Access key - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - AccessKey string - - // SecretAccessKey is your Bedrock API Secret Access key - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - SecretAccessKey string - - // SessionToken is your Bedrock API Session Token - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - SessionToken string - - // Profile is your Bedrock API AWS profile - // This parameter is ignored if AccessKey and SecretAccessKey are provided - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - Profile string - - // Region is your Bedrock API region - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - Region string - - // BaseURL is the custom API endpoint URL - // Use this to specify a different API endpoint, e.g., for proxies or enterprise setups - // Optional. Example: "https://custom-claude-api.example.com" - BaseURL *string - - // APIKey is your Anthropic API key - // Obtain from: https://console.anthropic.com/account/keys - // Required - APIKey string - - // Model specifies which Claude model to use - // Required - Model string - - // MaxTokens limits the maximum number of tokens in the response - // Range: 1 to model's context length - // Required. Example: 2000 for a medium-length response - 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: float32(0.7) - Temperature *float32 - - // TopP controls diversity via nucleus sampling - // Range: [0.0, 1.0], where 1.0 disables nucleus sampling - // Optional. Example: float32(0.95) - TopP *float32 - - // TopK controls diversity by limiting the top K tokens to sample from - // Optional. Example: int32(40) - TopK *int32 - - // StopSequences specifies custom stop sequences - // The model will stop generating when it encounters any of these sequences - // Optional. Example: []string{"\n\nHuman:", "\n\nAssistant:"} - StopSequences []string - - Thinking *Thinking - - // HTTPClient specifies the client to send HTTP requests. - HTTPClient *http.Client `json:"http_client"` - - DisableParallelToolUse *bool `json:"disable_parallel_tool_use"` -} -``` - -## **Examples** - -### **Text Generation** - -```go - -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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 - } - - // 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) - } -} - -``` - -### **Multimodal Support (Image Understanding)** - -```go - -package main - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - - "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 - } - - // 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 -} - -``` - -### **Streaming Generation** - -```go - -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "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 - } - - // 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----------") -} - -``` - -### **Claude Prompt Cache** - -```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" -) - -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.", - }) - - 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) -} - -``` - -### **function_call** - -```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" -) - -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** - -- `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 deleted file mode 100644 index 4a5c209ca41..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md +++ /dev/null @@ -1,468 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -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 to enhance natural language processing and generation. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/model.Model` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/deepseek@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the DeepSeek model: - -```go -package main - -import ( - "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) - } - -} -``` - -## **Configuration** - -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 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** - -### **Text Generation** - -```go -package main - -import ( - "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) - } - -} -``` - -### **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" -) - -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) - -} -``` - -### **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" -) - -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) - } - } -} -``` - -### **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" -) - -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) - -} -``` - -## **More Information** - -- [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 deleted file mode 100644 index 9eb6328a44e..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md +++ /dev/null @@ -1,840 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: ChatModel - gemini -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** - -- Implements `github.com/cloudwego/eino/components/model.Model` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration -- Supports caching of generated responses - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/gemini@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Gemini model: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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), - // 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** - -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-2.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 []gemini.GeminiResponseModality - - 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"` -} -``` - -## **Cache** - -This component supports two caching strategies to improve latency and reduce API calls: - -- 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{}), - }, -} -cacheInfo, _ := cm.CreatePrefixCache(ctx, []*schema.Message{ - { - 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)) -``` - -## **Examples** - -### **Text Generation** - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) - } -} - -``` - -### **Multimodal Support (Image Understanding)** - -```go -package main - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) -} - -``` - -### **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" -) - -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. -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)) -} - -``` - -### **Streaming Generation** - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "google.golang.org/genai" - - "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() -} - -``` - -### **Tool Calling** - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) -} - -``` - -### **Image Generation** - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "google.golang.org/genai" - - "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)) -} - -``` - -### **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" -) - -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)) - } -} - -``` - -### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini/examples) - -## **Related Documentation** - -- `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 deleted file mode 100644 index abb0b52172a..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md +++ /dev/null @@ -1,450 +0,0 @@ ---- -Description: "" -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 to enhance natural language processing and generation. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/model.Model` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Supports custom response parsing -- Flexible model configuration - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/ollama@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Ollama model: - -```go -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} - -``` - -## **Configuration** - -The model can be configured using the `ollama.ChatModelConfig` struct: - - -```go -type ChatModelConfig struct { - BaseURL string `json:"base_url"` - Timeout time.Duration `json:"timeout"` // request timeout for http client - - // 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"` - - Model string `json:"model"` - Format json.RawMessage `json:"format"` - KeepAlive *time.Duration `json:"keep_alive"` - - Options *Options `json:"options"` - - Thinking *ThinkValue `json:"thinking"` -} - - -type Options struct { - Runner - - // NumKeep specifies the number of tokens from the prompt to retain when the context size is exceeded and tokens need to be trimmed. - NumKeep int `json:"num_keep,omitempty"` - // Seed sets the random number seed for the model. Using the same seed with the same parameters will produce the same output. - Seed int `json:"seed,omitempty"` - // NumPredict sets the maximum number of tokens to generate. - NumPredict int `json:"num_predict,omitempty"` - // TopK controls the diversity of the generated text by limiting the selection of tokens to the top k most likely tokens. - TopK int `json:"top_k,omitempty"` - // TopP, also known as nucleus sampling, is another way to control the diversity of the generated text. It filters out the least likely tokens whose cumulative probability is below a certain threshold. - TopP float32 `json:"top_p,omitempty"` - // MinP is a parameter that works with TopP to ensure that the generated text is not too constrained. It sets a minimum probability for a token to be considered. - MinP float32 `json:"min_p,omitempty"` - // TypicalP is a parameter that helps to generate more "typical" or expected text by sampling from a reduced set of tokens that are considered typical. - TypicalP float32 `json:"typical_p,omitempty"` - // RepeatLastN specifies how many of the last N tokens to consider for penalizing repetition. - RepeatLastN int `json:"repeat_last_n,omitempty"` - // Temperature controls the randomness of the generated text. A higher temperature results in more random and creative output, while a lower temperature produces more predictable and conservative text. - Temperature float32 `json:"temperature,omitempty"` - // RepeatPenalty is used to penalize the model for repeating tokens that have already appeared in the generated text. - RepeatPenalty float32 `json:"repeat_penalty,omitempty"` - // PresencePenalty is used to penalize the model for introducing new tokens that were not present in the prompt. - PresencePenalty float32 `json:"presence_penalty,omitempty"` - // FrequencyPenalty is used to penalize the model for using tokens that appear frequently in the training data. - FrequencyPenalty float32 `json:"frequency_penalty,omitempty"` - // Stop is a list of strings that will cause the generation to stop if they are encountered. - Stop []string `json:"stop,omitempty"` -} - -type ThinkValue struct { - // Value can be a bool or string - Value interface{} -} - -``` - -## **Examples** - -### **Text Generation** - -```go - -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} - -``` - -### **Multimodal Support (Image Understanding)** - -```go - -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} - -func of[T any](a T) *T { - return &a -} - -``` - -### **Streaming Generation** - -```go - -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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") -} - -``` - -### **Tool Calling** - -```go - -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} - -``` - -### **Enable Thinking Mode** - -```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) -} - -``` - -### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/ollama/examples) - -## **Related Documentation** - -- `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 deleted file mode 100644 index 9a1a75ef121..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md +++ /dev/null @@ -1,940 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: ChatModel - OpenAI -weight: 0 ---- - -## **OpenAI Model** - -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** - -- 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 - -## Installation - -```go -go get github.com/cloudwego/eino-ext/components/model/openai@latest -``` - -## Quick Start - -Here's a quick example of how to use the OpenAI model: - -```go -package main - -import ( - "context" - "fmt" - "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 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) - -} -``` - -## Configuration - -You can configure the model using the `openai.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"` - -// The following three fields are only required when using Azure OpenAI Service, otherwise they can be ignored. -// For more details, see: https://learn.microsoft.com/en-us/azure/ai-services/openai/ - -// ByAzure indicates whether to use Azure OpenAI Service -// Required for Azure -ByAzure bool `json:"by_azure"` - -// AzureModelMapperFunc is used to map the model name to the deployment name for Azure OpenAI Service. -// This is useful when the model name is different from the deployment name. -// Optional for Azure, remove [,:] from the model name by default. -AzureModelMapperFunc func(model string) string - -// BaseURL is the Azure OpenAI endpoint URL -// Format: https://{YOUR_RESOURCE_NAME}.openai.azure.com. YOUR_RESOURCE_NAME is the name of your resource that you have created on Azure. -// Required for Azure -BaseURL string `json:"base_url"` - -// APIVersion specifies the Azure OpenAI API version -// Required for Azure -APIVersion string `json:"api_version"` - -// 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 -// Deprecated: use MaxCompletionTokens. Not compatible with o1-series models. -// refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens -MaxTokens *int `json:"max_tokens,omitempty"` - -// MaxCompletionTokens specifies an upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. -MaxCompletionTokens *int `json:"max_completion_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 *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"` - -// ExtraFields will override any existing fields with the same key. -// Optional. Useful for experimental features not yet officially supported. -ExtraFields map[string]any `json:"extra_fields,omitempty"` - -// ReasoningEffort will override the default reasoning level of "medium" -// Optional. Useful for fine tuning response latency vs. accuracy -ReasoningEffort ReasoningEffortLevel - -// Modalities are output types that you would like the model to generate. Most models are capable of generating text, which is the default: ["text"] -// The gpt-4o-audio-preview model can also be used to generate audio. To request that this model generate both text and audio responses, you can use: ["text", "audio"] -Modalities []Modality `json:"modalities,omitempty"` - -// Audio parameters for audio output. Required when audio output is requested with modalities: ["audio"] -Audio *Audio `json:"audio,omitempty"` -} -``` - -## Examples - -### Text Generation - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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{ - // If you want to use Azure OpenAI Service, set these two 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) - -} -``` - -### Multimodal Support (Image Understanding) - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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 -} -``` - -### Streaming Generation - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "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 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") -} -``` - -### Tool Calling - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "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 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) -} -``` - -### Audio Generation - -```go -package main - -import ( - "context" - - "log" - "os" - - "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{ - // If you want to use Azure OpenAI Service, set these two 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, - 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)) - -} -``` - -### 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" -) - -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) -} -``` - -## **Usage** - -### **Component Initialization** - -The OpenAI model is initialized through the `NewChatModel` function with the following main configuration parameters: - -```go -import "github.com/cloudwego/eino-ext/components/model/openai" - -model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ - // Azure OpenAI Service configuration (optional) - ByAzure: false, // Whether to use Azure OpenAI - BaseURL: "your-url", // Azure API base URL - APIVersion: "2023-05-15", // Azure API version - - // Basic configuration - APIKey: "your-key", // API key - Timeout: 30 * time.Second, // Timeout - - // Model parameters - Model: "gpt-4", // Model name - MaxTokens: &maxTokens,// Maximum generation length - Temperature: &temp, // Temperature - TopP: &topP, // Top-P sampling - Stop: []string{},// Stop words - PresencePenalty: &pp, // Presence penalty - FrequencyPenalty: &fp, // Frequency penalty - - // Advanced parameters - ResponseFormat: &format, // Response format - Seed: &seed, // Random seed - LogitBias: map[string]int{}, // Token bias - User: &user, // User identifier - - ReasoningEffort:openai.ReasoningEffortLevelHigh, // Reasoning level, default "medium" - - Modalities: make([]openai.Modality, 0), // Model response modality types: ["text","audio"] default text - - Audio: &openai.Audio{ // Audio output parameters, required when modality includes audio - Format: openai.AudioFormatMp3, - Voice: openai.AudioVoiceAlloy, - }, - - ExtraFields: map[string]any{}, // Extra fields, will add or override request fields, used for experimental validation - -}) -``` - -> - For detailed parameter meanings, refer to: [https://platform.openai.com/docs/api-reference/chat/create](https://platform.openai.com/docs/api-reference/chat/create) -> - For Azure-related services, refer to: [https://learn.microsoft.com/en-us/azure/ai-services/openai/](https://learn.microsoft.com/en-us/azure/ai-services/openai/) - -### **Generate Conversation** - -Conversation generation supports both normal mode and streaming mode: - -```go -// Invoke mode -response, err := model.Generate(ctx, messages) - -// Streaming mode -stream, err := model.Stream(ctx, messages) -``` - -Message format example: - -```go -import ( - "os" - "encoding/base64" - - "github.com/cloudwego/eino/schema" -) - -// Base64 format image data -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{ - // System message - schema.SystemMessage("You are an assistant"), - - // Multimodal message (with image) - { - 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: "What is this image?", - }, - }, - }, -} -``` - -### **Tool Calling** - -Supports binding tools and forced tool calling: - -```go -import "github.com/cloudwego/eino/schema" - -// Define tools -tools := []*schema.ToolInfo{ - { - Name: "search", - Desc: "Search for information", - ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ - "query": { - Type: schema.String, - Desc: "Search keywords", - Required: true, - }, - }), - }, -} -// Bind optional tools -err := model.BindTools(tools) - -// Bind forced tools -err := model.BindForcedTools(tools) -``` - -> For tool-related information, refer to [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide) - -### **Complete Usage Examples** - -#### **Direct Conversation** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/model/openai" - "github.com/cloudwego/eino/schema" -) - -func main() { - ctx := context.Background() - - // Initialize model - model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ - APIKey: "your-api-key", // required - Timeout: 30 * time.Second, - Model: "gpt-4", // required - - // If the model supports audio generation and you need to generate audio, configure as follows - // Modalities: []openai.Modality{openai.AudioModality, openai.TextModality}, - //Audio: &openai.Audio{ - // Format: openai.AudioFormatMp3, - // Voice: openai.AudioVoiceAlloy, - //}, -}, - - }) - if err != nil { - panic(err) - } - - // Base64 format image data - 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) - - // Request messages - messages := []*schema.Message{ - schema.SystemMessage("You are an image generation assistant that can generate images similar in style to the user's provided image"), - { - 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", - }, - }, - }, - } - - // Generate response - response, err := model.Generate(ctx, messages) - if err != nil { - panic(err) - } - - // Process response - /* - The generated multimodal content is stored in the response.AssistantGenMultiContent field - In this example, the final generated message looks like: - 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) -} -``` - -#### **Streaming Conversation** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/model/openai" - "github.com/cloudwego/eino/schema" -) - -func main() { - ctx := context.Background() - - // Initialize model - model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ - APIKey: "your-api-key", - Timeout: 30 * time.Second, - Model: "gpt-4", - }) - if err != nil { - panic(err) - } - - // Prepare messages - messages := []*schema.Message{ - schema.SystemMessage("You are an assistant"), - schema.UserMessage("Write a story"), - } - - // Get streaming response - reader, err := model.Stream(ctx, messages) - if err != nil { - panic(err) - } - defer reader.Close() // Remember to close - - // Process streaming content - for { - chunk, err := reader.Recv() - if err != nil { - break - } - print(chunk.Content) - } -} -``` - -### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/openai/examples) - -## **Related Documentation** - -- [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide) -- [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide) -- [ChatModel - ARK](/docs/eino/ecosystem_integration/chat_model/chat_model_ark) -- [ChatModel - Ollama](/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 deleted file mode 100644 index 5a8e9390e01..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md +++ /dev/null @@ -1,441 +0,0 @@ ---- -Description: "" -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 to enhance natural language processing and generation. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/model.Model` -- Easy integration with Eino's model system -- Configurable model parameters -- Supports chat completion -- Supports streaming responses -- Custom response parsing support -- Flexible model configuration - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/qianfan@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Qianfan model: - - -```go - -package main - -import ( - "context" - "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") - 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 -} -``` - -## **Configuration** - -The model can be configured using the `qianfan.ChatModelConfig` struct: - - -```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 - - // LLMRetryCount is the number of times to retry a failed request. - LLMRetryCount *int - - // LLMRetryTimeout is the timeout for each retry attempt. - LLMRetryTimeout *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 - - // 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 - - // 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 - - // 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 - - // 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 - - // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true. - ParallelToolCalls *bool - - // ResponseFormat specifies the format of the response. - ResponseFormat *ResponseFormat -} - -``` - -## **Examples** - -### **Text Generation** - -```go - -package main - -import ( - "context" - "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") - 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 -} - -``` - -### **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" -) - -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) -} - -func of[T any](t T) *T { - return &t -} - -``` - -### **Streaming Generation** - -```go - -package main - -import ( - "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) -} - -func of[T any](t T) *T { - return &t -} - -``` - -### **Tool Calling** - -```go - -package main - -import ( - "context" - "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") - 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 -} - -``` - -### [More Examples](https://github.com/cloudwego/eino-ext/tree/main/components/model/qianfan/examples) - -## **Related Documentation** - -- `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 deleted file mode 100644 index 5da8845649c..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md +++ /dev/null @@ -1,310 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: ChatModel - qwen -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** - -- 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 - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/model/qwen@latest -``` - -## **Quick Start** - -Here's a quick example of how to use the Qwen model: - -```go -package main - -import ( - "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) - -} - -func of[T any](t T) *T { - return &t -} -``` - -## **Configuration** - -The model can be configured using the `qwen.ChatModelConfig` struct: - -```go -type ChatModelConfig struct { - 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** - -### **Text Generation** - -```go -package main - -import ( - "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) - -} - -func of[T any](t T) *T { return &t } -``` - -### **Multimodal Understanding (Image Understanding)** - -```go -package main - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - "github.com/cloudwego/eino-ext/components/model/qwen" -) - -func main() { - 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 } -``` - -### **Streaming Generation** - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - "github.com/cloudwego/eino-ext/components/model/qwen" -) - -func main() { - 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 Calling** - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - "github.com/cloudwego/eino-ext/components/model/qwen" -) - -func main() { - 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** - -- `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 bc7ff0d33bf..c32aa55bcbb 100644 --- a/content/en/docs/eino/ecosystem_integration/chat_template/_index.md +++ b/content/en/docs/eino/ecosystem_integration/chat_template/_index.md @@ -4,6 +4,22 @@ date: "2025-01-20" lastmod: "" tags: [] title: ChatTemplate -weight: 0 +weight: 8 --- +# ChatTemplate Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + +
    Component NameChinese DocsEnglish Docs
    MCPREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository 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 deleted file mode 100644 index 09718287f97..00000000000 --- a/content/en/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -Description: "" -date: "2025-11-20" -lastmod: "" -tags: [] -title: ChatTemplate - MCP -weight: 0 ---- - -## Overview - -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 MCPPrompt wrapper, which implements Eino’s ChatTemplate interface ([Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide)). - - - -Also see: [Tool - MCP](/docs/eino/ecosystem_integration/tool/tool_mcp) - -## Usage - -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" - -// stdio client -cli, err := client.NewStdioMCPClient(myCommand, myEnvs, myArgs...) - -// sse client -cli, err := client.NewSSEMCPClient(myBaseURL) -// sse client needs to manually start asynchronous communication -// while stdio does not require it. -err = cli.Start(ctx) -``` - -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" - -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) -``` - -Then create the Eino ChatTemplate using the Client: - -```go -import "github.com/cloudwego/eino-ext/components/prompt/mcp" - -tpl, err := mcp.NewPromptTemplate(ctx, &mcp.Config{ - Cli: cli, - Name: "your prompt name", -}) -``` - -The template can be invoked directly: - -``` -result, err := tpl.Format(ctx, map[string]interface{}{/* input k-v */}) -``` - -Or used within any Eino Agent; for a simple LLM chain: - -``` -import ( - "github.com/cloudwego/eino/compose" - "github.com/cloudwego/eino-ext/components/prompt/mcp" -) - -llm, err := /*create a chat model*/ -tpl, err := mcp.NewPromptTemplate(ctx, &mcp.Config{ - Cli: cli, - Name: "your prompt name", -}) - -runner, err := compose.NewChain[map[string]any, *schema.Message](). - AppendChatTemplate(tpl). - AppendChatModel(llm). - Compile(ctx) -``` - -## More Information - -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 0345debe7e3..49c79246fb0 100644 --- a/content/en/docs/eino/ecosystem_integration/document/_index.md +++ b/content/en/docs/eino/ecosystem_integration/document/_index.md @@ -1,9 +1,32 @@ --- Description: "" -date: "2025-01-06" +date: "2026-03-02" lastmod: "" tags: [] title: Document -weight: 0 +weight: 2 --- +# Document Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    amazon s3README_zh.mdREADME.md
    local fileREADME_zh.mdREADME.md
    web urlREADME_zh.mdREADME.md
    htmlREADME_zh.mdREADME.md
    pdfREADME_zh.mdREADME.md
    markdownREADME_zh.mdREADME.md
    recursiveREADME_zh.mdREADME.md
    semanticREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository 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 deleted file mode 100644 index c27c9df8292..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/loader_amazon_s3.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Loader - amazon s3 -weight: 0 ---- - -## **Overview** - -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). - -### **AWS S3 Service Overview** - -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** - -### **Initialization** - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/loader/s3" -) - -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 key - UseObjectKeyAsID: true, // use object key as document ID - Parser: &parser.TextParser{}, // document parser (default TextParser) - }) -} -``` - -Parameters: - -- `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 - -### **Load Documents** - -```go -docs, err := loader.Load(ctx, document.Source{ URI: "s3://bucket-name/path/to/document.txt" }) -``` - -URI format: - -- Must start with `s3://` -- Followed by bucket name and object key -- Example: `s3://my-bucket/folder/document.pdf` - -Notes: - -- 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 - -### **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" -) - -func main() { - ctx := context.Background() - - loader, err := s3.NewS3Loader(ctx, &s3.LoaderConfig{ - Region: aws.String("us-east-1"), - AWSAccessKey: aws.String("your-access-key"), - AWSSecretKey: aws.String("your-secret-key"), - UseObjectKeyAsID: true, - }) - 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) } -} -``` - -## **References** - -- [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 deleted file mode 100644 index 1ea666185de..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/loader_local_file.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Loader - local file -weight: 0 ---- - -## **Overview** - -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** - -- 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** - -### **Initialization** - -```go -import ( - "github.com/cloudwego/eino/components/document/loader/file" -) - -func main() { - loader, err := file.NewFileLoader(ctx, &FileLoaderConfig{ - UseNameAsID: true, // whether to use filename as document ID - Parser: &parser.TextParser{}, // optional: custom parser - }) -} -``` - -Parameters: - -- `UseNameAsID`: whether to use filename as document ID -- `Parser`: document parser; if not set, default extension parser (ExtParser, currently TextParser) is used - -### **Load Documents** - -```go -docs, err := loader.Load(ctx, document.Source{ URI: "./path/to/document.txt" }) -``` - -After load, the following metadata is added: - -- `_file_name`: filename -- `_extension`: file extension -- `_source`: full file path - -Notes: - -- 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` - -### **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() - - 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) - fileName := doc.MetaData[file.MetaKeyFileName] - extension := doc.MetaData[file.MetaKeyExtension] - source := doc.MetaData[file.MetaKeySource] - } -} -``` - -## **References** - -- [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 deleted file mode 100644 index f08a860dd86..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/loader_web_url.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Loader - web url -weight: 0 ---- - -## **Overview** - -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). - -### **Features** - -- HTML page parsing by default -- Customizable HTTP client configuration (e.g., proxy) -- Custom content parser (e.g., body or specific container) - -## **Usage** - -### **Initialization** - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/loader/url" -) - -func main() { - loader, err := url.NewLoader(ctx, &url.LoaderConfig{ - Parser: parser, - Client: httpClient, - RequestBuilder: requestBuilder, - }) -} -``` - -Parameters: - -- `Parser`: document parser; default HTML parser extracts main body content -- `Client`: HTTP client; customize timeout, proxy, etc. -- `RequestBuilder`: request builder for method/headers customization - -### **Load Documents** - -```go -docs, err := loader.Load(ctx, document.Source{ URI: "https://example.com/document" }) -``` - -Notes: - -- URI must be a valid HTTP/HTTPS URL -- Default method is GET -- For other methods or custom headers (e.g., auth), configure `RequestBuilder` - -### **Complete Examples** - -#### **Basic Usage** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/document/loader/url" - "github.com/cloudwego/eino/components/document" -) - -func main() { - ctx := context.Background() - - loader, err := url.NewLoader(ctx, nil) - 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** - -```go -package main - -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() - - 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 } - req.Header.Add("User-Agent", "MyBot/1.0") - return req, nil - } - - 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) } -} -``` - -## **References** - -- [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 deleted file mode 100644 index 0b2cabd8a2f..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/parser_html.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Parser - html -weight: 0 ---- - -## **Overview** - -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: - -- extracting plain text from web pages -- retrieving page metadata (title, description, etc.) - -### **Features** - -HTML parser provides: - -- selective content extraction with flexible selectors (html selector) -- automatic metadata extraction -- safe HTML parsing - -## **Usage** - -### **Initialization** - -Initialize via `NewParser` with configuration: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/parser/html" -) - -parser, err := html.NewParser(ctx, &html.Config{ - Selector: &selector, // optional: content selector, defaults to body -}) -``` - -Config: - -- `Selector`: optional, the region to extract using goquery selector syntax - - e.g., `body` extracts `` content - - `#content` extracts the element with id "content" - -### **Metadata Keys** - -Parser auto‑extracts: - -- `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 Example** - -#### **Basic Usage** - -```go -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() - - // init parser - p, err := html.NewParser(ctx, nil) // default config - if err != nil { - panic(err) - } - - // HTML content - htmlContent := ` - - - Sample Page - - - - -
    -

    Welcome

    -

    Main body.

    -
    - - - ` - - // parse - docs, err := p.Parse(ctx, strings.NewReader(htmlContent), - parser.WithURI("https://example.com"), - parser.WithExtraMeta(map[string]any{ - "custom": "value", - }), - ) - if err != nil { - panic(err) - } - - doc := docs[0] - println("content:", doc.Content) - println("title:", doc.MetaData[html.MetaKeyTitle]) - println("desc:", doc.MetaData[html.MetaKeyDesc]) - println("lang:", doc.MetaData[html.MetaKeyLang]) -} -``` - -#### **Using Selectors** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/document/parser/html" -) - -func main() { - ctx := context.Background() - - // only extract element with id content - selector := "#content" - p, err := html.NewParser(ctx, &html.Config{ Selector: &selector }) - if err != nil { - panic(err) - } - - // ... parsing code ... -} -``` - -#### In loader - -See examples in [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) - -## **References** - -- [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 deleted file mode 100644 index 8e7719d2c29..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/parser_pdf.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Parser - pdf -weight: 0 ---- - -## **Overview** - -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: - -- converting PDF documents into plain text -- splitting PDF content by pages - -### **Features** - -PDF parser provides: - -- 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 -- does not preserve whitespace/newline formatting -- complex layouts may affect extraction quality - -## **Usage** - -### **Initialization** - -Initialize via `NewPDFParser` with configuration: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/parser/pdf" -) - -func main() { - parser, err := pdf.NewPDFParser(ctx, &pdf.Config{ - ToPages: true, // split by pages - }) -} -``` - -Config: - -- `ToPages`: whether to split the PDF into multiple documents by pages, default false - -### **Parse Documents** - -```go -docs, err := parser.Parse(ctx, reader, opts...) -``` - -Options: - -- `parser.WithURI` to set document URI -- `parser.WithExtraMeta` to add extra metadata - -### **Complete Example** - -#### **Basic Usage** - -```go -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() - - // init parser - p, err := pdf.NewPDFParser(ctx, &pdf.Config{ ToPages: false }) - if err != nil { - panic(err) - } - - // open file - file, err := os.Open("document.pdf") - if err != nil { - panic(err) - } - defer file.Close() - - // parse - docs, err := p.Parse(ctx, file, - parser.WithURI("document.pdf"), - parser.WithExtraMeta(map[string]any{ "source": "./document.pdf" }), - ) - if err != nil { - panic(err) - } - - for _, doc := range docs { - println(doc.Content) - } -} -``` - -#### In loader - -See examples in [Eino: Document Parser Interface Guide](/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) - -## **References** - -- [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 deleted file mode 100644 index b676353c241..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/splitter_markdown.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -Description: "" -date: "2025-01-07" -lastmod: "" -tags: [] -title: Splitter - markdown -weight: 0 ---- - -## **Overview** - -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). - -### **How It Works** - -1. Detect Markdown headings (`#`, `##`, `###`, etc.) -2. Build a document structure tree by levels -3. Split the document into fragments by headings - -## **Usage** - -### **Initialization** - -Initialize via `NewHeaderSplitter` with configuration: - -```go -splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ - Headers: map[string]string{ - "#": "h1", // H1 - "##": "h2", // H2 - "###": "h3", // H3 - }, - TrimHeaders: false, // keep heading lines in output -}) -``` - -Parameters: - -- `Headers`: required, map heading tokens to metadata keys -- `TrimHeaders`: whether to remove heading lines in output - -### **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 splitter - splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ - Headers: map[string]string{ "#": "h1", "##": "h2", "###": "h3" }, - TrimHeaders: false, - }) - if err != nil { panic(err) } - - // Prepare documents to split - docs := []*schema.Document{{ - ID: "doc1", - Content: `# Document Title - -Intro section. - -## Chapter One - -Chapter one content. - -### Section 1.1 - -Section 1.1 content. - -## Chapter Two - -Chapter two content. - -\`\`\` -# This is a comment in a code block and will not be detected as a heading -\`\`\` -`, - }} - - // Execute splitting - results, err := splitter.Transform(ctx, docs) - if err != nil { panic(err) } - - // Process split results - for i, doc := range results { - 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) } - } - } -} -``` - -## **Features** - -- Supports fenced code blocks ``` and ~~~ -- Automatically maintains heading hierarchy - - New peer headings reset lower-level headings - - Heading level info is passed via metadata - -## **References** - -- [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 deleted file mode 100644 index 352f536d3d1..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/splitter_recursive.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Splitter - recursive -weight: 0 ---- - -## **Overview** - -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). - -### **How It Works** - -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** - -### **Initialization** - -Initialize via `NewSplitter` with configuration: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - 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 -}) -``` - -Parameters: - -- `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 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() - - splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - OverlapSize: 200, - Separators: []string{"\n\n", "\n", "。", "!", "?"}, - KeepType: recursive.KeepTypeEnd, - }) - 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) } - - for i, doc := range results { println("fragment", i+1, ":", doc.Content) } -} -``` - -### **Advanced Usage** - -Custom length function: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - LenFunc: func(s string) int { - // use unicode rune count instead of byte length - return len([]rune(s)) - }, -}) -``` - -Adjust overlap strategy: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - OverlapSize: 300, // larger overlap to keep more context - KeepType: recursive.KeepTypeEnd, // keep separator at end of fragments -}) -``` - -Custom separators: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - Separators: []string{ - "\n\n", // blank line (paragraph) - "\n", // newline - "。", // period - }, -}) -``` - -## **References** - -- [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 deleted file mode 100644 index 8ec6e3adeae..00000000000 --- a/content/en/docs/eino/ecosystem_integration/document/splitter_semantic.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Splitter - semantic -weight: 0 ---- - -## **Overview** - -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). - -### **How It Works** - -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** - -### **Initialization** - -Initialize via `NewSplitter` with configuration: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - 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 -}) -``` - -Parameters: - -- `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()` - -### **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" -) - -func main() { - ctx := context.Background() - - embedder := &embedding.SomeEmbeddingImpl{} // eg: openai embedding - - splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - BufferSize: 2, - MinChunkSize: 100, - Separators: []string{"\n", ".", "?", "!"}, - Percentile: 0.9, - }) - 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) } - for i, doc := range results { println("fragment", i+1, ":", doc.Content) } -} -``` - -### **Advanced Usage** - -Custom length function: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - LenFunc: func(s string) int { return len([]rune(s)) }, // unicode length -}) -``` - -Adjust granularity: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - Percentile: 0.95, // fewer split points - MinChunkSize: 200, // avoid too-small fragments -}) -``` - -Optimize semantic judgment: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - BufferSize: 10, // more context - Separators: []string{"\n\n", "\n", "。", "!", "?", ","}, // custom priority -}) -``` - -## **References** - -- [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 ffba4b9fa7d..ea7071500b0 100644 --- a/content/en/docs/eino/ecosystem_integration/embedding/_index.md +++ b/content/en/docs/eino/ecosystem_integration/embedding/_index.md @@ -1,9 +1,30 @@ --- Description: "" -date: "2026-01-22" +date: "2026-03-02" lastmod: "" tags: [] title: Embedding -weight: 0 +weight: 3 --- +# Embedding Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    ARKREADME_zh.mdREADME.md
    OllamaREADME_zh.mdREADME.md
    OpenAIREADME_zh.mdREADME.md
    QianfanREADME_zh.mdREADME.md
    TencentCloudREADME_zh.mdREADME.md
    dashscopeREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md b/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md deleted file mode 100644 index 496b29df979..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ark.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -Description: "" -date: "2026-01-22" -lastmod: "" -tags: [] -title: Embedding - ARK -weight: 0 ---- - -## **Overview** - -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** - -### **Initialization** - -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 (choose one) - APIKey: "your-api-key", // API Key auth - // or AK/SK auth - AccessKey: "your-access-key", - SecretKey: "your-secret-key", - - // 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 Embeddings** - -Text vectorization is done via `EmbedStrings`: - -```go -embeddings, err := embedder.EmbedStrings(ctx, []string{ - "First text", - "Second text", -}) -``` - -### **Complete Example** - -#### **Basic Usage** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/embedding/ark" -) - -func main() { - ctx := context.Background() - - // init embedder - timeout := 30 * time.Second - embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: "your-api-key", - Model: "ep-20xxxxxxx-xxxxx", - Timeout: &timeout, - }) - if err != nil { - panic(err) - } - - // 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) - } - - // use generated vectors - for i, embedding := range embeddings { - println("text", i+1, "vector dim:", len(embedding)) - } -} -``` - -## **References** - -- [Eino: Embedding Guide](/docs/eino/core_modules/components/embedding_guide) -- [Embedding - OpenAI](/docs/eino/ecosystem_integration/embedding/embedding_openai) -- 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 deleted file mode 100644 index fd36d2b8996..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Embedding - dashscope -weight: 0 ---- - -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 deleted file mode 100644 index 31cdf87601b..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_ollama.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Embedding - Ollama -weight: 0 ---- - -## **Overview** - -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** - -```bash -go get github.com/cloudwego/eino-ext/components/embedding/ollama -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "log" - "os" - "time" - - "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) -} -``` - -## **Configuration** - -The embedder can be configured via the `EmbeddingConfig` struct: - -```go -type EmbeddingConfig struct { - // 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 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 deleted file mode 100644 index 4c73daac2c4..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_openai.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -Description: "" -date: "2026-01-22" -lastmod: "" -tags: [] -title: Embedding - OpenAI -weight: 0 ---- - -## **Overview** - -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: - -- Converting text into high‑dimensional vectors -- Using OpenAI’s embedding models -- Using Azure OpenAI Service embedding models - -## **Usage** - -### **Initialization** - -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 config - APIKey: "your-api-key", - Model: "text-embedding-ada-002", - Timeout: 30 * time.Second, - - // 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 dimension - User: &user, // user identifier -}) -``` - -### **Generate Embeddings** - -Vectorization is done via `EmbedStrings`: - -```go -embeddings, err := embedder.EmbedStrings(ctx, []string{ - "First text", - "Second text", -}) -``` - -### **Complete Example** - -#### **Basic Usage** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/embedding/openai" -) - -func main() { - ctx := context.Background() - - // init embedder - embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{ - APIKey: "your-api-key", - Model: "text-embedding-ada-002", - Timeout: 30 * time.Second, - }) - if err != nil { - panic(err) - } - - // 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) - } - - // use vectors - for i, embedding := range embeddings { - println("text", i+1, "vector dim:", len(embedding)) - } -} -``` - -## **References** - -- [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 deleted file mode 100644 index 0d0a0c8556b..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Embedding - Qianfan -weight: 0 ---- - -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 deleted file mode 100644 index 81c5a8ea119..00000000000 --- a/content/en/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Embedding - TencentCloud -weight: 0 ---- - -## **Tencent Cloud Hunyuan Embedding** - -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** - -- Implements `github.com/cloudwego/eino/components/embedding.Embedder` -- Easy to integrate into Eino RAG workflows -- Built‑in token usage tracking -- Automatically batches large text arrays -- Built‑in callback support - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/embedding/tencentcloud -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/cloudwego/eino-ext/components/embedding/tencentcloud" -) - -func main() { - ctx := context.Background() - - // config - cfg := &tencentcloud.EmbeddingConfig{ - SecretID: os.Getenv("TENCENTCLOUD_SECRET_ID"), - SecretKey: os.Getenv("TENCENTCLOUD_SECRET_KEY"), - Region: "ap-guangzhou", - } - - // create embedder - embedder, err := tencentcloud.NewEmbedder(ctx, cfg) - if err != nil { - panic(err) - } - - // get embeddings - embeddings, err := embedder.EmbedStrings(ctx, []string{"hello world", "bye world"}) - if err != nil { - panic(err) - } - - fmt.Printf("Embeddings: %v\n", embeddings) -} -``` - -## **Configuration** - -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-guangzhou") -} -``` - -## **Details** - -### **Auto Batching** - -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. - -### **Token Usage Tracking** - -Tracks token usage via Eino’s callback system: -- input token count -- total token count - -### **Callback Support** - -Fully supports Eino callbacks: -- error tracking -- start/end event monitoring -- token usage statistics - -## **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 c84033f8cef..2da53529bfa 100644 --- a/content/en/docs/eino/ecosystem_integration/indexer/_index.md +++ b/content/en/docs/eino/ecosystem_integration/indexer/_index.md @@ -1,11 +1,33 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Indexer -weight: 0 +weight: 6 --- -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). +# Indexer Component List +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    ElasticSearch 8README_zh.mdREADME.md
    Elasticsearch 7README_zh.mdREADME.md
    Elasticsearch 9README_zh.mdREADME.md
    Milvus v1 (Legacy)README_zh.mdREADME.md
    Milvus v2 (Recommended)README_zh.mdREADME.md
    OpenSearch 2README_zh.mdREADME.md
    OpenSearch 3README_zh.mdREADME.md
    RedisREADME_zh.mdREADME.md
    volc VikingDBREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md deleted file mode 100644 index f4562a29f27..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: 'Indexer - Elasticsearch 7 ' -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatiotemporal search capabilities. - -An Elasticsearch 7.x indexer implementation for [Eino](https://github.com/cloudwego/eino), implementing the `Indexer` interface. This component seamlessly integrates with Eino's document indexing system, providing powerful vector storage and retrieval capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/indexer.Indexer` -- Easy integration with Eino indexing system -- Configurable Elasticsearch parameters -- Supports vector similarity search -- Supports batch indexing operations -- Supports custom field mapping -- Flexible document vectorization support - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/es7@latest -``` - -## Quick Start - -Here is a simple example of using the indexer: - -```go -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v7" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es7" -) - -const ( - indexName = "eino_example" - fieldContent = "content" - fieldContentVector = "content_vector" - fieldExtraLocation = "location" - docExtraLocation = "location" -) - -func main() { - ctx := context.Background() - - // ES supports multiple connection methods - username := os.Getenv("ES_USERNAME") - password := os.Getenv("ES_PASSWORD") - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - - // Create embedding component using Volcengine ARK - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // Load documents - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // Create ES indexer component - indexer, _ := es7.NewIndexer(ctx, &es7.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es7.FieldValue, err error) { - return map[string]es7.FieldValue{ - fieldContent: { - Value: doc.Content, - EmbedKey: fieldContentVector, // Vectorize document content and save to "content_vector" field - }, - fieldExtraLocation: { - Value: doc.MetaData[docExtraLocation], - }, - }, nil - }, - Embedding: emb, - }) - - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## Configuration - -The indexer can be configured using the `IndexerConfig` struct: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // Required: Elasticsearch client instance - Index string // Required: Index name for storing documents - BatchSize int // Optional: Maximum text embedding batch size (default: 5) - - // Required: Function to map Document fields to Elasticsearch fields - DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error) - - // Optional: Required only when vectorization is needed - 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 -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch) diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md deleted file mode 100644 index 4fd82d23e3b..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Elasticsearch 9 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatiotemporal search capabilities. - -An Elasticsearch 9.x indexer implementation for [Eino](https://github.com/cloudwego/eino), implementing the `Indexer` interface. This enables seamless integration with Eino's vector storage and retrieval system, enhancing semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/indexer.Indexer` -- Easy integration with Eino's indexing system -- Configurable Elasticsearch parameters -- Supports vector similarity search -- Batch indexing operations -- Custom field mapping support -- Flexible document vectorization - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/es9@latest -``` - -## Quick Start - -Here is a quick example of using the indexer, for more details please read components/indexer/es9/examples/indexer/add_documents.go: - -```go -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - "github.com/elastic/go-elasticsearch/v9" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es9" -) - -const ( - indexName = "eino_example" - fieldContent = "content" - fieldContentVector = "content_vector" - fieldExtraLocation = "location" - docExtraLocation = "location" -) - -func main() { - ctx := context.Background() - - // ES supports multiple connection methods - username := os.Getenv("ES_USERNAME") - password := os.Getenv("ES_PASSWORD") - httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH") - - var cert []byte - var err error - if httpCACertPath != "" { - cert, err = os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. Create embedding component (using Ark) - // Replace "ARK_API_KEY", "ARK_REGION", "ARK_MODEL" with actual configuration - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 3. Prepare documents - // Documents typically contain ID and Content - // Can also include additional Metadata for filtering or other purposes - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // 4. Create ES indexer component - indexer, _ := es9.NewIndexer(ctx, &es9.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - // DocumentToFields specifies how to map document fields to ES fields - DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es9.FieldValue, err error) { - return map[string]es9.FieldValue{ - fieldContent: { - Value: doc.Content, - EmbedKey: fieldContentVector, // Vectorize document content and save to "content_vector" field - }, - fieldExtraLocation: { - // Additional metadata field - Value: doc.MetaData[docExtraLocation], - }, - }, nil - }, - // Provide embedding component for vectorization - Embedding: emb, - }) - - // 5. Index documents - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## Configuration - -The indexer can be configured using the `IndexerConfig` struct: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // Required: Elasticsearch client instance - Index string // Required: Index name for storing documents - BatchSize int // Optional: Maximum text count for embedding (default: 5) - - // Required: Function to map Document fields to Elasticsearch fields - DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error) - - // Optional: Required only when vectorization is needed - 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 -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch) diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md deleted file mode 100644 index 88a5c23b245..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_es8.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - ElasticSearch 8 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatio-temporal 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 - -- Implements `github.com/cloudwego/eino/components/indexer.Indexer` -- Easy integration with Eino indexing system -- Configurable Elasticsearch parameters -- Supports vector similarity search -- Batch indexing operations -- Custom field mapping support -- Flexible document embedding - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/es8@latest -``` - -## Quick Start - -Here is a quick example of using the indexer. For more details, please read components/indexer/es8/examples/indexer/add_documents.go: - -```go -import ( - "context" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v8" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es8" -) - -const ( - 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") - - // 1. Create ES client - httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH") - if httpCACertPath != "" { - cert, err := os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. Create embedding component - // Using Volcengine Ark, replace environment variables with real configuration - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 3. Prepare documents - // Documents typically contain ID and Content. You can also add extra metadata for filtering and other purposes. - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // 4. Create ES indexer component - indexer, _ := es8.NewIndexer(ctx, &es8.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - // DocumentToFields specifies how to map document fields to ES fields - 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, // Embed document content and save to "content_vector" field - }, - fieldExtraLocation: { - // Extra metadata field - Value: doc.MetaData[docExtraLocation], - }, - }, nil - }, - // Provide embedding component for vectorization - Embedding: emb, - }) - - // 5. Index documents - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## Configuration - -Configure the indexer using the `IndexerConfig` struct: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // Required: Elasticsearch client instance - Index string // Required: Index name to store documents - BatchSize int // Optional: Maximum number of texts for embedding (default: 5) - - // Required: Function to map Document fields to Elasticsearch fields - DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error) - - // Optional: Only required when vectorization is needed - Embedding embedding.Embedder -} - -// FieldValue defines how a field should be stored and vectorized -type FieldValue struct { - Value any // Raw value to store - EmbedKey string // If set, Value will be vectorized and saved - Stringify func(val any) (string, error) // Optional: Custom string conversion -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [Elasticsearch Go Client Documentation](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 deleted file mode 100644 index 94403ed9651..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvus.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Milvus v1 (Legacy) -weight: 0 ---- - -> **Module Note:** This module (`EINO-ext/milvus`) is based on `milvus-sdk-go`. Since the underlying SDK has been deprecated and only supports up to Milvus 2.4, this module is retained only for backward compatibility. -> -> **Recommendation:** New users should use [Indexer - Milvus 2 (v2.5+)](/docs/eino/ecosystem_integration/indexer/indexer_milvusv2) for continued support. - -## **Milvus Storage** - -Vector storage based on Milvus 2.x that provides an `Indexer` implementation for [Eino](https://github.com/cloudwego/eino). This component integrates seamlessly with Eino's vector storage and retrieval system for semantic search. - -## **Quick Start** - -### **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/milvus -``` - -### **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" -) - -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) -} -``` - -## **Configuration** - -```go -type IndexerConfig struct { - // Client is the milvus client to call - // Required - Client client.Client - - // Default collection configuration - // Collection is the collection name in milvus database - // Optional, default value is "eino_collection" - // If you want to use this configuration, you must add Field configuration, otherwise it will not work properly - Collection string - // Description is the description of the collection - // Optional, default value is "the collection for eino" - Description string - // PartitionNum is the number of collection partitions - // Optional, default value is 1 (disabled) - // If partition number is greater than 1, it means partitioning is enabled, and there must be a partition key in Fields - PartitionNum int64 - // Fields are the collection fields - // Optional, default value is the default fields - Fields []*entity.Field - // SharedNum is the milvus parameter required for creating a collection - // Optional, default value is 1 - SharedNum int32 - // ConsistencyLevel is the milvus collection consistency policy - // Optional, default level is ClBounded (bounded consistency level, default tolerance is 5 seconds) - ConsistencyLevel ConsistencyLevel - // EnableDynamicSchema indicates whether the collection enables dynamic schema - // Optional, default value is false - // Enabling dynamic schema may affect milvus performance - EnableDynamicSchema bool - - // DocumentConverter is the function to convert schema.Document to row data - // Optional, default value is defaultDocumentConverter - DocumentConverter func(ctx context.Context, docs []*schema.Document, vectors [][]float64) ([]interface{}, error) - - // Index configuration for vector column - // MetricType is the metric type for vectors - // Optional, default type is HAMMING - MetricType MetricType - - // Embedding is the vectorization method required to embed values from schema.Document content - // Required - Embedding embedding.Embedder -} -``` - -## **Default Data Model** - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -### External References - -- [Milvus Documentation](https://milvus.io/docs) -- [Milvus Index Types](https://milvus.io/docs/index.md) -- [Milvus Metric Types](https://milvus.io/docs/metric.md) -- [milvus-sdk-go Reference](https://milvus.io/api-reference/go/v2.4.x/About.md) - -### Related Documentation - -- [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/ecosystem_integration/indexer/indexer_milvusv2.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvusv2.md deleted file mode 100644 index 68f526fd5c9..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_milvusv2.md +++ /dev/null @@ -1,395 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Milvus v2 (Recommended) -weight: 0 ---- - -> **Milvus Vector Database Introduction** -> -> Milvus Vector Retrieval Service is a fully managed database service built on open-source Milvus, providing efficient unstructured data retrieval capabilities suitable for diverse AI scenarios. Customers no longer need to worry about underlying hardware resources, reducing usage costs and improving overall efficiency. -> -> Since the company's **internal** Milvus service uses the standard SDK, the **EINO-ext community version** is applicable. - -This package provides a Milvus 2.x (V2 SDK) indexer implementation for the EINO framework, supporting document storage and vector indexing. - -> **Note**: This package requires **Milvus 2.5+** to support server-side functions (such as BM25), basic functionality is compatible with lower versions. - -## Features - -- **Milvus V2 SDK**: Uses the latest `milvus-io/milvus/client/v2` SDK -- **Flexible Index Types**: Supports multiple index builders including Auto, HNSW, IVF series, SCANN, DiskANN, GPU indexes, and RaBitQ (Milvus 2.6+) -- **Hybrid Search Ready**: Native support for hybrid storage of sparse vectors (BM25/SPLADE) and dense vectors -- **Server-side Vector Generation**: Automatic sparse vector generation using Milvus Functions (BM25) -- **Automated Management**: Automatic handling of collection schema creation, index building, and loading -- **Field Analysis**: Configurable text analyzers (supporting Chinese Jieba, English, Standard, etc.) -- **Custom Document Conversion**: Flexible mapping from Eino documents to Milvus columns - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/milvus2 -``` - -## Quick Start - -```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/client/v2/milvusclient" - - milvus2 "github.com/cloudwego/eino-ext/components/indexer/milvus2" -) - -func main() { - // Get 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") - - ctx := context.Background() - - // Create 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 indexer - indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: addr, - Username: username, - Password: password, - }, - Collection: "my_collection", - - Vector: &milvus2.VectorConfig{ - Dimension: 1024, // Match embedding model dimension - MetricType: milvus2.COSINE, - IndexBuilder: milvus2.NewHNSWIndexBuilder().WithM(16).WithEfConstruction(200), - }, - Embedding: emb, - }) - if err != nil { - log.Fatalf("Failed to create indexer: %v", err) - return - } - log.Printf("Indexer created successfully") - - // Store documents - docs := []*schema.Document{ - { - ID: "doc1", - Content: "Milvus is an open-source vector database", - MetaData: map[string]any{ - "category": "database", - "year": 2021, - }, - }, - { - ID: "doc2", - Content: "EINO is a framework for building AI applications", - }, - } - 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 Options - - - - - - - - - - - - - - - -
    FieldTypeDefaultDescription
    Client
    *milvusclient.Client
    -Pre-configured Milvus client (optional)
    ClientConfig
    *milvusclient.ClientConfig
    -Client configuration (required when Client is empty)
    Collection
    string
    "eino_collection"
    Collection name
    Vector
    *VectorConfig
    -Dense vector configuration (Dimension, MetricType, field name)
    Sparse
    *SparseVectorConfig
    -Sparse vector configuration (MetricType, field name)
    IndexBuilder
    IndexBuilder
    AutoIndexBuilder
    Index type builder
    Embedding
    embedding.Embedder
    -Embedder for vectorization (optional). If empty, documents must contain vectors (BYOV).
    ConsistencyLevel
    ConsistencyLevel
    ConsistencyLevelDefault
    Consistency level (
    ConsistencyLevelDefault
    uses Milvus default: Bounded; if not explicitly set, maintains collection-level setting)
    PartitionName
    string
    -Default partition for inserting data
    EnableDynamicSchema
    bool
    false
    Enable dynamic field support
    Functions
    []*entity.Function
    -Schema function definitions (e.g., BM25) for server-side processing
    FieldParams
    map[string]map[string]string
    -Field parameter configuration (e.g., enable_analyzer)
    - -### Dense Vector Configuration (`VectorConfig`) - - - - - - -
    FieldTypeDefaultDescription
    Dimension
    int64
    -Vector dimension (required)
    MetricType
    MetricType
    L2
    Similarity metric type (L2, IP, COSINE, etc.)
    VectorField
    string
    "vector"
    Dense vector field name
    - -### Sparse Vector Configuration (`SparseVectorConfig`) - - - - - - -
    FieldTypeDefaultDescription
    VectorField
    string
    "sparse_vector"
    Sparse vector field name
    MetricType
    MetricType
    BM25
    Similarity metric type
    Method
    SparseMethod
    SparseMethodAuto
    Generation method (
    SparseMethodAuto
    or
    SparseMethodPrecomputed
    )
    - -> **Note**: Only when `MetricType` is `BM25`, `Method` defaults to `Auto`. `Auto` means using Milvus server-side functions (remote functions). For other metric types (such as `IP`), the default is `Precomputed`. - -## Index Builders - -### Dense Index Builders - - - - - - - - - - - - - - - - - - -
    BuilderDescriptionKey Parameters
    NewAutoIndexBuilder()
    Milvus automatically selects optimal index-
    NewHNSWIndexBuilder()
    Graph-based high-performance index
    M
    ,
    EfConstruction
    NewIVFFlatIndexBuilder()
    Clustering-based search
    NList
    NewIVFPQIndexBuilder()
    Product quantization, memory efficient
    NList
    ,
    M
    ,
    NBits
    NewIVFSQ8IndexBuilder()
    Scalar quantization
    NList
    NewIVFRabitQIndexBuilder()
    IVF + RaBitQ binary quantization (Milvus 2.6+)
    NList
    NewFlatIndexBuilder()
    Brute-force exact search-
    NewDiskANNIndexBuilder()
    Disk index for large datasets-
    NewSCANNIndexBuilder()
    Fast search with high recall
    NList
    ,
    WithRawDataEnabled
    NewBinFlatIndexBuilder()
    Brute-force search for binary vectors-
    NewBinIVFFlatIndexBuilder()
    Clustering search for binary vectors
    NList
    NewGPUBruteForceIndexBuilder()
    GPU-accelerated brute-force search-
    NewGPUIVFFlatIndexBuilder()
    GPU-accelerated IVF_FLAT-
    NewGPUIVFPQIndexBuilder()
    GPU-accelerated IVF_PQ-
    NewGPUCagraIndexBuilder()
    GPU-accelerated graph index (CAGRA)
    IntermediateGraphDegree
    ,
    GraphDegree
    - -### Sparse Index Builders - - - - - -
    BuilderDescriptionKey Parameters
    NewSparseInvertedIndexBuilder()
    Inverted index for sparse vectors
    DropRatioBuild
    NewSparseWANDIndexBuilder()
    WAND algorithm for sparse vectors
    DropRatioBuild
    - -### Example: HNSW Index - -```go -indexBuilder := milvus2.NewHNSWIndexBuilder(). - WithM(16). // Maximum connections per node (4-64) - WithEfConstruction(200) // Search width during index construction (8-512) -``` - -### Example: IVF_FLAT Index - -```go -indexBuilder := milvus2.NewIVFFlatIndexBuilder(). - WithNList(256) // Number of cluster units (1-65536) -``` - -### Example: IVF_PQ Index (Memory Efficient) - -```go -indexBuilder := milvus2.NewIVFPQIndexBuilder(). - WithNList(256). // Number of cluster units - WithM(16). // Number of sub-quantizers - WithNBits(8) // Bits per sub-quantizer (1-16) -``` - -### Example: SCANN Index (Fast Search with High Recall) - -```go -indexBuilder := milvus2.NewSCANNIndexBuilder(). - WithNList(256). // Number of cluster units - WithRawDataEnabled(true) // Enable raw data for reranking -``` - -### Example: DiskANN Index (Large Datasets) - -```go -indexBuilder := milvus2.NewDiskANNIndexBuilder() // Disk-based, no additional parameters -``` - -### Example: Sparse Inverted Index - -```go -indexBuilder := milvus2.NewSparseInvertedIndexBuilder(). - WithDropRatioBuild(0.2) // Ratio of small values to ignore during build (0.0-1.0) -``` - -### Dense Vector Metrics - - - - - - -
    Metric TypeDescription
    L2
    Euclidean distance
    IP
    Inner product
    COSINE
    Cosine similarity
    - -### Sparse Vector Metrics - - - - - -
    Metric TypeDescription
    BM25
    Okapi BM25 (
    SparseMethodAuto
    required)
    IP
    Inner product (for precomputed sparse vectors)
    - -### Binary Vector Metrics - - - - - - - - -
    Metric TypeDescription
    HAMMING
    Hamming distance
    JACCARD
    Jaccard distance
    TANIMOTO
    Tanimoto distance
    SUBSTRUCTURE
    Substructure search
    SUPERSTRUCTURE
    Superstructure search
    - -## Sparse Vector Support - -The indexer supports two sparse vector modes: **Auto-Generation** and **Precomputed**. - -### Auto-Generation (BM25) - -Automatically generates sparse vectors from content fields using Milvus server-side functions. - -- **Requirements**: Milvus 2.5+ -- **Configuration**: Set `MetricType: milvus2.BM25`. - -```go -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - // ... basic configuration ... - Collection: "hybrid_collection", - - Sparse: &milvus2.SparseVectorConfig{ - VectorField: "sparse_vector", - MetricType: milvus2.BM25, - // Method defaults to SparseMethodAuto when using BM25 - }, - - // Analyzer configuration for BM25 - FieldParams: map[string]map[string]string{ - "content": { - "enable_analyzer": "true", - "analyzer_params": `{"type": "standard"}`, // Use {"type": "chinese"} for Chinese - }, - }, -}) -``` - -### Precomputed (SPLADE, BGE-M3, etc.) - -Allows storing sparse vectors generated by external models (such as SPLADE, BGE-M3) or custom logic. - -- **Configuration**: Set `MetricType` (usually `IP`) and `Method: milvus2.SparseMethodPrecomputed`. -- **Usage**: Pass sparse vectors via `doc.WithSparseVector()`. - -```go -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - Collection: "sparse_collection", - - Sparse: &milvus2.SparseVectorConfig{ - VectorField: "sparse_vector", - MetricType: milvus2.IP, - Method: milvus2.SparseMethodPrecomputed, - }, -}) - -// Store documents with sparse vectors -doc := &schema.Document{ID: "1", Content: "..."} -doc.WithSparseVector(map[int]float64{ - 1024: 0.5, - 2048: 0.3, -}) -indexer.Store(ctx, []*schema.Document{doc}) -``` - -## Bring Your Own Vectors (BYOV) - -If your documents already contain vectors, you can use the Indexer without configuring an Embedder. - -```go -// Create indexer without embedding -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: "localhost:19530", - }, - Collection: "my_collection", - Vector: &milvus2.VectorConfig{ - Dimension: 128, - MetricType: milvus2.L2, - }, - // Embedding: nil, // Leave empty -}) - -// Store documents with precomputed vectors -docs := []*schema.Document{ - { - ID: "doc1", - Content: "Document with existing vector", - }, -} - -// Attach dense vector to document -// Vector dimension must match collection dimension -vector := []float64{0.1, 0.2, ...} -docs[0].WithDenseVector(vector) - -// Attach sparse vector (optional, if Sparse is configured) -// Sparse vector is a mapping of index -> weight -sparseVector := map[int]float64{ - 10: 0.5, - 25: 0.8, -} -docs[0].WithSparseVector(sparseVector) - -ids, err := indexer.Store(ctx, docs) -``` - -For sparse vectors in BYOV mode, refer to the **Precomputed** section above for configuration. - -## Examples - -See the [https://github.com/cloudwego/eino-ext/tree/main/components/indexer/milvus2/examples](https://github.com/cloudwego/eino-ext/tree/main/components/indexer/milvus2/examples) directory for complete example code: - -- [demo](./examples/demo) - Basic collection setup using HNSW index -- [hnsw](./examples/hnsw) - HNSW index example -- [ivf_flat](./examples/ivf_flat) - IVF_FLAT index example -- [rabitq](./examples/rabitq) - IVF_RABITQ index example (Milvus 2.6+) -- [auto](./examples/auto) - AutoIndex example -- [diskann](./examples/diskann) - DISKANN index example -- [hybrid](./examples/hybrid) - Hybrid search setup (dense + BM25 sparse) (Milvus 2.5+) -- [hybrid_chinese](./examples/hybrid_chinese) - Chinese hybrid search example (Milvus 2.5+) -- [sparse](./examples/sparse) - Pure sparse index example (BM25) -- [byov](./examples/byov) - Bring Your Own Vectors example - -## Getting Help - -- [[Internal] Milvus Quick Start](https://bytedance.larkoffice.com/wiki/P3JBw4PtKiLGPhkUCQZcXbHFnkf) - -If you have any questions or feature suggestions, feel free to join the oncall group. - -### External References - -- [Milvus Documentation](https://milvus.io/docs) -- [Milvus Index Types](https://milvus.io/docs/index.md) -- [Milvus Metric Types](https://milvus.io/docs/metric.md) -- [Milvus Go SDK Reference](https://milvus.io/api-reference/go/v2.6.x/About.md) - -### Related Documentation - -- [Eino: Indexer User Guide](/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever User Guide](/docs/eino/core_modules/components/retriever_guide) diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md deleted file mode 100644 index 97f0616dd63..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - OpenSearch 2 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatiotemporal search capabilities. - -An OpenSearch 2 indexer implementation for [Eino](https://github.com/cloudwego/eino), implementing the `Indexer` interface. This enables seamless integration of OpenSearch into Eino's vector storage and retrieval system, enhancing semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/indexer.Indexer` -- Easy integration with Eino's indexing system -- Configurable OpenSearch parameters -- Supports vector similarity search -- Supports batch indexing operations -- Supports custom field mapping -- Flexible document vectorization support - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/opensearch2@latest -``` - -## Quick Start - -Here is a simple example of how to use the indexer, for more details refer to components/indexer/opensearch2/examples/indexer/main.go: - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v2" - - "github.com/cloudwego/eino-ext/components/indexer/opensearch2" -) - -func main() { - ctx := context.Background() - - client, err := opensearch.NewClient(opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - if err != nil { - log.Fatal(err) - } - - // Create embedding component - emb := createYourEmbedding() - - // Create opensearch indexer component - indexer, _ := opensearch2.NewIndexer(ctx, &opensearch2.IndexerConfig{ - Client: client, - Index: "your_index_name", - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (map[string]opensearch2.FieldValue, error) { - return map[string]opensearch2.FieldValue{ - "content": { - Value: doc.Content, - EmbedKey: "content_vector", - }, - }, nil - }, - Embedding: emb, - }) - - docs := []*schema.Document{ - {ID: "1", Content: "example content"}, - } - - ids, _ := indexer.Store(ctx, docs) - fmt.Println(ids) -} -``` - -## Configuration - -The indexer can be configured using the `IndexerConfig` struct: - -```go -type IndexerConfig struct { - Client *opensearch.Client // Required: OpenSearch client instance - Index string // Required: Index name for storing documents - BatchSize int // Optional: Maximum text embedding batch size (default: 5) - - // Required: Function to map Document fields to OpenSearch fields - DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error) - - // Optional: Required only when vectorization is needed - 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 along with its vector value - Stringify func(val any) (string, error) // Optional: Custom string conversion function -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [OpenSearch Go Client Documentation](https://github.com/opensearch-project/opensearch-go) diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md deleted file mode 100644 index b786d2cf4ab..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - OpenSearch 3 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatiotemporal search capabilities. - -An OpenSearch 3 indexer implementation for [Eino](https://github.com/cloudwego/eino), implementing the `Indexer` interface. This enables seamless integration of OpenSearch into Eino's vector storage and retrieval system, enhancing semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/indexer.Indexer` -- Easy integration with Eino's indexing system -- Configurable OpenSearch parameters -- Supports vector similarity search -- Supports batch indexing operations -- Supports custom field mapping -- Flexible document vectorization support - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/opensearch3@latest -``` - -## Quick Start - -Here is a simple example of how to use the indexer, for more details refer to components/indexer/opensearch3/examples/indexer/main.go: - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v4" - "github.com/opensearch-project/opensearch-go/v4/opensearchapi" - - "github.com/cloudwego/eino-ext/components/indexer/opensearch3" -) - -func main() { - ctx := context.Background() - - client, err := opensearchapi.NewClient(opensearchapi.Config{ - Client: opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }, - }) - if err != nil { - log.Fatal(err) - } - - // Create embedding component - emb := createYourEmbedding() - - // Create opensearch indexer component - indexer, _ := opensearch3.NewIndexer(ctx, &opensearch3.IndexerConfig{ - Client: client, - Index: "your_index_name", - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (map[string]opensearch3.FieldValue, error) { - return map[string]opensearch3.FieldValue{ - "content": { - Value: doc.Content, - EmbedKey: "content_vector", - }, - }, nil - }, - Embedding: emb, - }) - - docs := []*schema.Document{ - {ID: "1", Content: "example content"}, - } - - ids, _ := indexer.Store(ctx, docs) - fmt.Println(ids) -} -``` - -## Configuration - -The indexer can be configured using the `IndexerConfig` struct: - -```go -type IndexerConfig struct { - Client *opensearchapi.Client // Required: OpenSearch client instance - Index string // Required: Index name for storing documents - BatchSize int // Optional: Maximum text embedding batch size (default: 5) - - // Required: Function to map Document fields to OpenSearch fields - DocumentToFields func(ctx context.Context, doc *schema.Document) (map[string]FieldValue, error) - - // Optional: Required only when vectorization is needed - 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 along with its vector value - Stringify func(val any) (string, error) // Optional: Custom string conversion function -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to join the oncall group. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [OpenSearch Go Client Documentation](https://github.com/opensearch-project/opensearch-go) diff --git a/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md b/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md deleted file mode 100644 index e94dc64e4f3..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_redis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Indexer - Redis -weight: 0 ---- - -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 deleted file mode 100644 index c692ea029e1..00000000000 --- a/content/en/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Indexer - volc VikingDB -weight: 0 ---- - -## **Overview** - -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). - -### **VikingDB Service Overview** - -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: - -- Use VikingDB's built-in embedding (Embedding V2) -- Use a custom embedding model - -## **Usage** - -### **Initialization** - -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 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, // use built-in embedding - ModelName: "text2vec-base", // model name - UseSparse: true, // use sparse vectors - Embedding: embedder, // custom embedder - }, - - AddBatchSize: 5, // batch add size -}) -``` - -### **Complete Examples** - -#### **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() - - // 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", - UseSparse: true, - }, - }) - if err != nil { - panic(err) - } - - // documents - docs := []*schema.Document{ - { - Content: "This is the first document content", - }, - { - Content: "This is the second document content", - }, - } - - // store - ids, err := idx.Store(ctx, docs) - if err != nil { - panic(err) - } - - // handle returned IDs - for i, id := range ids { - println("doc", i+1, "stored ID:", id) - } -} -``` - -#### **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" -) - -func main() { - ctx := context.Background() - - // init embedder (openai example) - embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{}) - if err != nil { - panic(err) - } - - // 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, - }, - }) - if err != nil { - panic(err) - } - - // documents - docs := []*schema.Document{ - { - Content: "Document content one", - }, - { - Content: "Document content two", - }, - } - - // store - ids, err := idx.Store(ctx, docs) - if err != nil { - panic(err) - } - - // handle returned IDs - for i, id := range ids { - println("doc", i+1, "stored ID:", id) - } -} -``` - -## **References** - -- [Eino: Indexer Guide](/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide) -- [Volcengine VikingDB Guide](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 085904e8bad..df97cfa48ef 100644 --- a/content/en/docs/eino/ecosystem_integration/retriever/_index.md +++ b/content/en/docs/eino/ecosystem_integration/retriever/_index.md @@ -1,11 +1,34 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Retriever -weight: 0 +weight: 7 --- -Retriever is used to recall content indexed by [Indexer](/docs/eino/ecosystem_integration/indexer). In AI applications, [Embedding](/docs/eino/ecosystem_integration/embedding) is typically used for semantic similarity recall. +# Retriever Component List +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    DifyREADME_zh.mdREADME.md
    Elasticsearch 7README_zh.mdREADME.md
    Elasticsearch 8README_zh.mdREADME.md
    Elasticsearch 9README_zh.mdREADME.md
    Milvus v1 (Legacy)README_zh.mdREADME.md
    Milvus v2 (Recommended)README_zh.mdREADME.md
    OpenSearch 2README_zh.mdREADME.md
    OpenSearch 3README_zh.mdREADME.md
    RedisREADME_zh.mdREADME.md
    volc VikingDBREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md deleted file mode 100644 index 6b9c11176a2..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_dify.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Retriever - Dify -weight: 0 ---- - -## **Dify Retriever** - -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** - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino retrieval -- Configurable retrieval parameters -- Supports reranking - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/dify -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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() - - 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** - -Configure via `RetrieverConfig`: - -```go -type RetrieverConfig struct { - APIKey string - Endpoint string // default: https://api.dify.ai/v1 - DatasetID string - RetrievalModel *RetrievalModel - Timeout time.Duration -} - -type RetrievalModel struct { - SearchMethod SearchMethod - RerankingEnable *bool - RerankingMode *string - RerankingModel *RerankingModel - Weights *float64 - TopK *int - ScoreThresholdEnabled *bool - ScoreThreshold *float64 -} -``` - -## **Document Metadata** - -Adds the following metadata on retrieved docs: - -- `orig_doc_id`: original doc ID in Dify -- `orig_doc_name`: original doc name in Dify -- `keywords`: extracted keywords - -Helpers: - -```go -docID := dify.GetOrgDocID(doc) -docName := dify.GetOrgDocName(doc) -keywords := dify.GetKeywords(doc) -``` - -## **More Details** - -- Dify: https://github.com/langgenius/dify -- Eino: https://github.com/cloudwego/eino diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md deleted file mode 100644 index fa6622d5685..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 7 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides Elasticsearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatio-temporal search capabilities. - -An Elasticsearch 7.x retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. This component seamlessly integrates with Eino's retrieval system to provide enhanced semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino retrieval system -- Configurable Elasticsearch parameters -- Multiple search modes: - - Exact Match (text search) - - Dense Vector Similarity (semantic search) - - Raw String (custom queries) -- Support for default result parser and custom parsers -- Filter support for fine-grained queries - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/es7@latest -``` - -## Quick Start - -Here is a simple example of using the retriever: - -```go -import ( - "context" - "fmt" - "os" - - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v7" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/es7" - "github.com/cloudwego/eino-ext/components/retriever/es7/search_mode" -) - -func main() { - ctx := context.Background() - - // Connect to Elasticsearch - username := os.Getenv("ES_USERNAME") - password := os.Getenv("ES_PASSWORD") - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - - // Create embedding component using Volcengine ARK for vector search - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // Create retriever with dense vector similarity search - retriever, _ := es7.NewRetriever(ctx, &es7.RetrieverConfig{ - Client: client, - Index: "my_index", - TopK: 10, - SearchMode: search_mode.DenseVectorSimilarity(search_mode.DenseVectorSimilarityTypeCosineSimilarity, "content_vector"), - Embedding: emb, - }) - - // Retrieve documents - docs, _ := retriever.Retrieve(ctx, "search query") - - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s, Score: %v\n", doc.ID, doc.Content, doc.Score()) - } -} -``` - -## Search Modes - -### Exact Match - -Uses Elasticsearch match query for simple text search: - -```go -searchMode := search_mode.ExactMatch("content") -``` - -### Dense Vector Similarity - -Uses script_score with dense vectors for semantic search: - -```go -// Cosine similarity -searchMode := search_mode.DenseVectorSimilarity( - search_mode.DenseVectorSimilarityTypeCosineSimilarity, - "content_vector", -) - -// Other similarity types: -// - DenseVectorSimilarityTypeDotProduct -// - DenseVectorSimilarityTypeL1Norm -// - DenseVectorSimilarityTypeL2Norm -``` - -### Raw String Request - -Pass custom JSON queries directly: - -```go -searchMode := search_mode.RawStringRequest() - -// Then use a JSON query string as the search query -query := `{"query": {"bool": {"must": [{"match": {"content": "search term"}}]}}}` -docs, _ := retriever.Retrieve(ctx, query) -``` - -## Configuration - -```go -type RetrieverConfig struct { - Client *elasticsearch.Client // Required: Elasticsearch client - Index string // Required: Index name - TopK int // Optional: Number of results (default: 10) - ScoreThreshold *float64 // Optional: Minimum score threshold - SearchMode SearchMode // Required: Search strategy - ResultParser func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) // Optional: Custom parser - Embedding embedding.Embedder // Required for vector search modes -} -``` - -## Using Filters - -Add query filters using the `WithFilters` option: - -```go -filters := []interface{}{ - map[string]interface{}{ - "term": map[string]interface{}{ - "category": "news", - }, - }, -} - -docs, _ := retriever.Retrieve(ctx, "query", es7.WithFilters(filters)) -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch) -- [Elasticsearch 7.10 Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/7.10/query-dsl.html) diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md deleted file mode 100644 index bd481dc27e2..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 9 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides Elasticsearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatio-temporal search capabilities. - -An Elasticsearch 9.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 to enhance semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino's retrieval system -- Configurable Elasticsearch parameters -- Support for vector similarity search -- Multiple search modes (including approximate search) -- Custom result parsing support -- Flexible document filtering - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/es9@latest -``` - -## Quick Start - -Here is a quick example using approximate search mode. For more details, see components/retriever/es9/examples/approximate/approximate.go: - -```go -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - "github.com/elastic/go-elasticsearch/v9" - "github.com/elastic/go-elasticsearch/v9/typedapi/types" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/es9" - "github.com/cloudwego/eino-ext/components/retriever/es9/search_mode" -) - -const ( - indexName = "eino_example" - fieldContent = "content" - fieldContentVector = "content_vector" - fieldExtraLocation = "location" - docExtraLocation = "location" -) - -func main() { - ctx := context.Background() - - // ES supports multiple connection methods - username := os.Getenv("ES_USERNAME") - password := os.Getenv("ES_PASSWORD") - httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH") - - var cert []byte - var err error - if httpCACertPath != "" { - cert, err = os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. Create embedding component (using Ark) - // Replace "ARK_API_KEY", "ARK_REGION", "ARK_MODEL" with actual configuration - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // Create retriever component - retriever, _ := es9.NewRetriever(ctx, &es9.RetrieverConfig{ - Client: client, - Index: indexName, - TopK: 5, - SearchMode: search_mode.SearchModeApproximate(&search_mode.ApproximateConfig{ - QueryFieldName: fieldContent, - VectorFieldName: fieldContentVector, - Hybrid: true, - // RRF is only available under certain 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, - }) - - // Search without filters - docs, _ := retriever.Retrieve(ctx, "tourist attraction") - - // Search with filters - docs, _ = retriever.Retrieve(ctx, "tourist attraction", - es9.WithFilters([]types.Query{{ - Term: map[string]types.TermQuery{ - fieldExtraLocation: { - CaseInsensitive: of(true), - Value: "China", - }, - }, - }}), - ) - - fmt.Printf("retrieved docs: %+v\n", docs) -} - -func of[T any](v T) *T { - return &v -} -``` - -## Configuration - -The retriever can be configured using the `RetrieverConfig` struct: - -```go -type RetrieverConfig struct { - Client *elasticsearch.Client // Required: Elasticsearch client instance - Index string // Required: Index name for retrieving documents - TopK int // Required: Number of results to return - - // Required: Search mode configuration - SearchMode search_mode.SearchMode - - // Optional: Function to parse Elasticsearch hits into Documents - // If not provided, a default parser will be used: - // 1. Extract "content" field from source as Document.Content - // 2. Use other source fields as Document.MetaData - ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error) - - // Optional: Required only when query vectorization is needed - Embedding embedding.Embedder -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [Elasticsearch Go Client Documentation](https://github.com/elastic/go-elasticsearch) diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md deleted file mode 100644 index afc9070a303..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_es8.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 8 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides ElasticSearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatiotemporal search capabilities. - -## **ES8 Retriever** - -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 capabilities. - -## **Features** - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino's retrieval system -- Configurable Elasticsearch parameters -- Supports vector similarity search -- Multiple search modes including approximate search -- Supports custom result parsing -- Flexible document filtering - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/es8@latest -``` - -## **Quick Start** - -Here is a quick example of how to use the retriever in approximate search mode. You can read `components/retriever/es8/examples/approximate/approximate.go` for more details: - -```go -import ( - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - elasticsearch "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" -) - -const ( - indexName = "eino_example" - fieldContent = "content" - fieldContentVector = "content_vector" - fieldExtraLocation = "location" - docExtraLocation = "location" -) - -func main() { - ctx := context.Background() - - // ES supports multiple connection methods - 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 is only available under 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 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** - -The retriever can be configured via the `RetrieverConfig` struct: - -```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 - - // Required: Search mode configuration - SearchMode search_mode.SearchMode - - // Required: Function to parse Elasticsearch hits into Documents - ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error) - - // Optional: Only needed when query vectorization is required - Embedding embedding.Embedder -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -- [Eino Documentation](https://github.com/cloudwego/eino) -- [Elasticsearch Go Client Documentation](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 deleted file mode 100644 index a590396af64..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvus.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Milvus v1 (Legacy) -weight: 0 ---- - -> **Module Note:** This module (`EINO-ext/milvus`) is based on `milvus-sdk-go`. Since the underlying SDK has been discontinued and only supports up to Milvus 2.4, this module is retained only for backward compatibility. -> -> **Recommendation:** New users should use [Retriever - Milvus 2 (v2.5+)](/docs/eino/ecosystem_integration/retriever/retriever_milvusv2) for continued support. - -## **Milvus Search** - -Vector search implementation based on Milvus 2.x that provides a `Retriever` interface implementation for [Eino](https://github.com/cloudwego/eino). This component seamlessly integrates with Eino's vector storage and retrieval system to enhance semantic search capabilities. - -## **Quick Start** - -### **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/milvus -``` - -### **Create Milvus Search** - -```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" -) - -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) - } -} -``` - -## **Configuration** - -```go -type RetrieverConfig struct { - // Client is the milvus client to call - // Required - Client client.Client - - // Retriever common configuration - // Collection is the collection name in milvus database - // Optional, default value is "eino_collection" - Collection string - // Partition is the partition name of the collection - // Optional, default value is empty - Partition []string - // VectorField is the vector field name in the collection - // Optional, default value is "vector" - VectorField string - // OutputFields are the fields to return - // Optional, default value is empty - OutputFields []string - // DocumentConverter is the function to convert search result to s.Document - // Optional, default value is defaultDocumentConverter - DocumentConverter func(ctx context.Context, doc client.SearchResult) ([]*s.Document, error) - // MetricType is the metric type of the vector - // Optional, default value is "HAMMING" - MetricType entity.MetricType - // TopK is the top k results to return - // Optional, default value is 5 - TopK int - // ScoreThreshold is the threshold of the search result - // Optional, default value is 0 - ScoreThreshold float64 - // SearchParams - // Optional, default value is entity.IndexAUTOINDEXSearchParam, level 1 - Sp entity.SearchParam - - // Embedding is the method to embed values from s.Document content that need to be embedded - // Required - Embedding embedding.Embedder -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -### External References - -- [Milvus Documentation](https://milvus.io/docs) -- [Milvus Index Types](https://milvus.io/docs/index.md) -- [Milvus Metric Types](https://milvus.io/docs/metric.md) -- [milvus-sdk-go Reference](https://milvus.io/api-reference/go/v2.4.x/About.md) - -### Related Documentation - -- [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/ecosystem_integration/retriever/retriever_milvusv2.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvusv2.md deleted file mode 100644 index 4737a9f7735..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_milvusv2.md +++ /dev/null @@ -1,286 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: 'Retriever - Milvus v2 (Recommended) ' -weight: 0 ---- - -> **Milvus Vector Database Introduction** -> -> Milvus Vector Retrieval Service is a fully managed database service built on open-source Milvus, providing efficient unstructured data retrieval capabilities suitable for diverse AI scenarios. Users no longer need to worry about underlying hardware resources, reducing costs and improving overall efficiency. -> -> Since the **internal** Milvus service uses the standard SDK, the **EINO-ext community version** is applicable. - -This package provides a Milvus 2.x (V2 SDK) retriever implementation for the EINO framework, supporting vector similarity search with multiple search modes. - -> **Note**: This package requires **Milvus 2.5+** for server-side functions (like BM25). Basic functionality is compatible with lower versions. - -## Features - -- **Milvus V2 SDK**: Uses the latest `milvus-io/milvus/client/v2` SDK -- **Multiple Search Modes**: Supports approximate search, range search, hybrid search, iterator search, and scalar search -- **Dense + Sparse Hybrid Search**: Combines dense vectors and sparse vectors with RRF reranking -- **Custom Result Conversion**: Configurable result-to-document conversion - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/milvus2 -``` - -## Quick Start - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/milvus-io/milvus/client/v2/milvusclient" - - milvus2 "github.com/cloudwego/eino-ext/components/retriever/milvus2" - "github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode" -) - -func main() { - // Get 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") - - ctx := context.Background() - - // Create 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 retriever - retriever, err := milvus2.NewRetriever(ctx, &milvus2.RetrieverConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: addr, - Username: username, - Password: password, - }, - Collection: "my_collection", - TopK: 10, - SearchMode: search_mode.NewApproximate(milvus2.COSINE), - Embedding: emb, - }) - if err != nil { - log.Fatalf("Failed to create retriever: %v", err) - return - } - log.Printf("Retriever created successfully") - - // Retrieve documents - documents, err := retriever.Retrieve(ctx, "search query") - if err != nil { - log.Fatalf("Failed to retrieve: %v", err) - return - } - - // Print documents - for i, doc := range documents { - fmt.Printf("Document %d:\n", i) - fmt.Printf(" ID: %s\n", doc.ID) - fmt.Printf(" Content: %s\n", doc.Content) - fmt.Printf(" Score: %v\n", doc.Score()) - } -} -``` - -## Configuration Options - - - - - - - - - - - - - - - -
    FieldTypeDefaultDescription
    Client
    *milvusclient.Client
    -Pre-configured Milvus client (optional)
    ClientConfig
    *milvusclient.ClientConfig
    -Client configuration (required when Client is nil)
    Collection
    string
    "eino_collection"
    Collection name
    TopK
    int
    5
    Number of results to return
    VectorField
    string
    "vector"
    Dense vector field name
    SparseVectorField
    string
    "sparse_vector"
    Sparse vector field name
    OutputFields
    []string
    All fieldsFields to return in results
    SearchMode
    SearchMode
    -Search strategy (required)
    Embedding
    embedding.Embedder
    -Embedder for query vectorization (required)
    DocumentConverter
    func
    Default converterCustom result-to-document conversion
    ConsistencyLevel
    ConsistencyLevel
    ConsistencyLevelDefault
    Consistency level (
    ConsistencyLevelDefault
    uses collection's level; no per-request override applied)
    Partitions
    []string
    -Partitions to search
    - -## Search Modes - -Import search modes from `github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode`. - -### Approximate Search - -Standard approximate nearest neighbor (ANN) search. - -```go -mode := search_mode.NewApproximate(milvus2.COSINE) -``` - -### Range Search - -Search within a specified distance range (vectors within `Radius`). - -```go -// L2: distance <= Radius -// IP/Cosine: score >= Radius -mode := search_mode.NewRange(milvus2.L2, 0.5). - WithRangeFilter(0.1) // Optional: inner boundary for ring search -``` - -### Sparse Search (BM25) - -Pure sparse vector search using BM25. Requires Milvus 2.5+ with sparse vector field and Functions enabled. - -```go -// Pure sparse search (BM25) requires specifying OutputFields to get content -// MetricType: BM25 (default) or IP -mode := search_mode.NewSparse(milvus2.BM25) - -// In configuration, use "*" or specific fields to ensure content is returned: -// OutputFields: []string{"*"} -``` - -### Hybrid Search (Dense + Sparse) - -Multi-vector search combining dense and sparse vectors with result reranking. Requires a collection with both dense and sparse vector fields (see indexer sparse example). - -```go -import ( - "github.com/milvus-io/milvus/client/v2/milvusclient" - milvus2 "github.com/cloudwego/eino-ext/components/retriever/milvus2" - "github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode" -) - -// Define hybrid search with dense + sparse sub-requests -hybridMode := search_mode.NewHybrid( - milvusclient.NewRRFReranker().WithK(60), // RRF reranker - &search_mode.SubRequest{ - VectorField: "vector", // Dense vector field - VectorType: milvus2.DenseVector, // Default value, can be omitted - TopK: 10, - MetricType: milvus2.L2, - }, - // Sparse SubRequest - &search_mode.SubRequest{ - VectorField: "sparse_vector", // Sparse vector field - VectorType: milvus2.SparseVector, // Specify sparse type - TopK: 10, - MetricType: milvus2.BM25, // Use BM25 or IP - }, -) - -// Create retriever (sparse vector generation handled server-side by Milvus Function) -retriever, err := milvus2.NewRetriever(ctx, &milvus2.RetrieverConfig{ - ClientConfig: &milvusclient.ClientConfig{Address: "localhost:19530"}, - Collection: "hybrid_collection", - VectorField: "vector", // Default dense field - SparseVectorField: "sparse_vector", // Default sparse field - TopK: 5, - SearchMode: hybridMode, - Embedding: denseEmbedder, // Standard Embedder for dense vectors -}) -``` - -### Iterator Search - -Batch-based traversal suitable for large result sets. - -> [!WARNING] -> -> The `Retrieve` method in `Iterator` mode fetches **all** results until reaching the total limit (`TopK`) or end of collection. This may consume significant memory for extremely large datasets. - -```go -// 100 is the batch size (entries per network call) -mode := search_mode.NewIterator(milvus2.COSINE, 100). - WithSearchParams(map[string]string{"nprobe": "10"}) - -// Use RetrieverConfig.TopK to set the total limit (IteratorLimit). -``` - -### Scalar Search - -Metadata-only filtering without vector similarity (uses filter expression as query). - -```go -mode := search_mode.NewScalar() - -// Query with filter expression -docs, err := retriever.Retrieve(ctx, `category == "electronics" AND year >= 2023`) -``` - -### Dense Vector Metrics - - - - - - -
    Metric TypeDescription
    L2
    Euclidean distance
    IP
    Inner product
    COSINE
    Cosine similarity
    - -### Sparse Vector Metrics - - - - - -
    Metric TypeDescription
    BM25
    Okapi BM25 (required for BM25 search)
    IP
    Inner product (for pre-computed sparse vectors)
    - -### Binary Vector Metrics - - - - - - - - -
    Metric TypeDescription
    HAMMING
    Hamming distance
    JACCARD
    Jaccard distance
    TANIMOTO
    Tanimoto distance
    SUBSTRUCTURE
    Substructure search
    SUPERSTRUCTURE
    Superstructure search
    - -> **Important**: The metric type in SearchMode must match the index metric type used when creating the collection. - -## Examples - -See [https://github.com/cloudwego/eino-ext/tree/main/components/retriever/milvus2/examples](https://github.com/cloudwego/eino-ext/tree/main/components/retriever/milvus2/examples) for complete example code: - -- [approximate](./examples/approximate) - Basic ANN search -- [range](./examples/range) - Range search example -- [hybrid](./examples/hybrid) - Hybrid multi-vector search (dense + BM25) -- [hybrid_chinese](./examples/hybrid_chinese) - Chinese hybrid search example -- [iterator](./examples/iterator) - Batch iterator search -- [scalar](./examples/scalar) - Scalar/metadata filtering -- [grouping](./examples/grouping) - Grouped search results -- [filtered](./examples/filtered) - Vector search with filters -- [sparse](./examples/sparse) - Pure sparse search example (BM25) - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -### External References - -- [Milvus Documentation](https://milvus.io/docs) -- [Milvus Index Types](https://milvus.io/docs/index.md) -- [Milvus Metric Types](https://milvus.io/docs/metric.md) -- [Milvus Go SDK Reference](https://milvus.io/api-reference/go/v2.6.x/About.md) - -### Related Documentation - -- [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/ecosystem_integration/retriever/retriever_opensearch2.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_opensearch2.md deleted file mode 100644 index 3aa07861cab..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_opensearch2.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - OpenSearch 2 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides Elasticsearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatio-temporal search capabilities. - -An OpenSearch 2 retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. This enables OpenSearch to seamlessly integrate into Eino's vector retrieval system, enhancing semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino's retrieval system -- Configurable OpenSearch parameters -- Support for vector similarity search and keyword search -- Multiple search modes: - - KNN (Approximate Nearest Neighbor) - - Exact Match (keyword search) - - Raw String (native JSON request body) - - Dense Vector Similarity (script score, dense vectors) - - Neural Sparse (sparse vectors) -- Custom result parsing support - -## Search Mode Compatibility - - - - - - - - - - - -
    Search ModeMinimum OpenSearch VersionDescription
    ExactMatch
    1.0+Standard Query DSL
    RawString
    1.0+Standard Query DSL
    DenseVectorSimilarity
    1.0+Uses
    script_score
    and painless vector functions
    Approximate
    (KNN)
    1.0+Basic KNN supported since 1.0. Efficient filtering (Post-filtering) requires 2.4+ (Lucene HNSW) or 2.9+ (Faiss).
    Approximate
    (Hybrid)
    2.10+Generates
    bool
    query. Requires 2.10+
    normalization-processor
    for advanced score normalization (Convex Combination). Basic
    bool
    queries work in earlier versions (1.0+).
    Approximate
    (RRF)
    2.19+Requires
    score-ranker-processor
    (2.19+) and
    neural-search
    plugin.
    NeuralSparse
    (Query Text)
    2.11+Requires
    neural-search
    plugin and deployed model.
    NeuralSparse
    (TokenWeights)
    2.11+Requires
    neural-search
    plugin.
    - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/opensearch2@latest -``` - -## Quick Start - -Here is a simple example of how to use the retriever: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v2" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/opensearch2" - "github.com/cloudwego/eino-ext/components/retriever/opensearch2/search_mode" -) - -func main() { - ctx := context.Background() - - client, err := opensearch.NewClient(opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - if err != nil { - log.Fatal(err) - } - - // Create embedding component using Volcengine ARK - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // Create retriever component - retriever, _ := opensearch2.NewRetriever(ctx, &opensearch2.RetrieverConfig{ - Client: client, - Index: "your_index_name", - TopK: 5, - // Choose search mode - SearchMode: search_mode.Approximate(&search_mode.ApproximateConfig{ - VectorField: "content_vector", - K: 5, - }), - ResultParser: func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) { - // Parse hit map to Document - id, _ := hit["_id"].(string) - source := hit["_source"].(map[string]interface{}) - content, _ := source["content"].(string) - return &schema.Document{ID: id, Content: content}, nil - }, - Embedding: emb, - }) - - docs, err := retriever.Retrieve(ctx, "search query") - if err != nil { - fmt.Printf("retrieve error: %v\n", err) - return - } - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s\n", doc.ID, doc.Content) - } -} -``` - -## Configuration - -The retriever can be configured using the `RetrieverConfig` struct: - -```go -type RetrieverConfig struct { - Client *opensearch.Client // Required: OpenSearch client instance - Index string // Required: Index name for retrieving documents - TopK int // Required: Number of results to return - - // Required: Search mode configuration - // Pre-built implementations are provided in the search_mode package: - // - search_mode.Approximate(&ApproximateConfig{...}) - // - search_mode.ExactMatch(field) - // - search_mode.RawStringRequest() - // - search_mode.DenseVectorSimilarity(type, vectorField) - // - search_mode.NeuralSparse(vectorField, &NeuralSparseConfig{...}) - SearchMode SearchMode - - // Optional: Function to parse OpenSearch hits (map[string]interface{}) to Document - // If not provided, a default parser will be used. - ResultParser func(ctx context.Context, hit map[string]interface{}) (doc *schema.Document, err error) - - // Optional: Required only when query vectorization is needed - Embedding embedding.Embedder -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [OpenSearch Go Client Documentation](https://github.com/opensearch-project/opensearch-go) diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md deleted file mode 100644 index 33b02fdcae5..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - OpenSearch 3 -weight: 0 ---- - -> **Cloud Search Service Introduction** -> -> Cloud Search Service is a fully managed, one-stop information retrieval and analysis platform that provides Elasticsearch and OpenSearch engines, supporting full-text search, vector search, hybrid search, and spatio-temporal search capabilities. - -An OpenSearch 3 retriever implementation for [Eino](https://github.com/cloudwego/eino) that implements the `Retriever` interface. This enables OpenSearch to seamlessly integrate into Eino's vector retrieval system, enhancing semantic search capabilities. - -## Features - -- Implements `github.com/cloudwego/eino/components/retriever.Retriever` -- Easy integration with Eino's retrieval system -- Configurable OpenSearch parameters -- Support for vector similarity search and keyword search -- Multiple search modes: - - KNN (Approximate Nearest Neighbor) - - Exact Match (keyword search) - - Raw String (native JSON request body) - - Dense Vector Similarity (script score, dense vectors) - - Neural Sparse (sparse vectors) -- Custom result parsing support - -## Search Mode Compatibility - - - - - - - - - - - -
    Search ModeMinimum OpenSearch VersionDescription
    ExactMatch
    1.0+Standard Query DSL
    RawString
    1.0+Standard Query DSL
    DenseVectorSimilarity
    1.0+Uses
    script_score
    and painless vector functions
    Approximate
    (KNN)
    1.0+Basic KNN supported since 1.0. Efficient filtering (Post-filtering) requires 2.4+ (Lucene HNSW) or 2.9+ (Faiss).
    Approximate
    (Hybrid)
    2.10+Generates
    bool
    query. Requires 2.10+
    normalization-processor
    for advanced score normalization (Convex Combination). Basic
    bool
    queries work in earlier versions (1.0+).
    Approximate
    (RRF)
    2.19+Requires
    score-ranker-processor
    (2.19+) and
    neural-search
    plugin.
    NeuralSparse
    (Query Text)
    2.11+Requires
    neural-search
    plugin and deployed model.
    NeuralSparse
    (TokenWeights)
    2.11+Requires
    neural-search
    plugin.
    - -## Installation - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/opensearch3@latest -``` - -## Quick Start - -Here is a simple example of how to use the retriever: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v4" - "github.com/opensearch-project/opensearch-go/v4/opensearchapi" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/opensearch3" - "github.com/cloudwego/eino-ext/components/retriever/opensearch3/search_mode" -) - -func main() { - ctx := context.Background() - - client, err := opensearchapi.NewClient(opensearchapi.Config{ - Client: opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }, - }) - if err != nil { - log.Fatal(err) - } - - // Create embedding component using Volcengine ARK - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // Create retriever component - retriever, _ := opensearch3.NewRetriever(ctx, &opensearch3.RetrieverConfig{ - Client: client, - Index: "your_index_name", - TopK: 5, - // Choose search mode - SearchMode: search_mode.Approximate(&search_mode.ApproximateConfig{ - VectorField: "content_vector", - K: 5, - }), - ResultParser: func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) { - // Parse hit map to Document - id, _ := hit["_id"].(string) - source := hit["_source"].(map[string]interface{}) - content, _ := source["content"].(string) - return &schema.Document{ID: id, Content: content}, nil - }, - Embedding: emb, - }) - - docs, err := retriever.Retrieve(ctx, "search query") - if err != nil { - fmt.Printf("retrieve error: %v\n", err) - return - } - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s\n", doc.ID, doc.Content) - } -} -``` - -## Configuration - -The retriever can be configured using the `RetrieverConfig` struct: - -```go -type RetrieverConfig struct { - Client *opensearchapi.Client // Required: OpenSearch client instance - Index string // Required: Index name for retrieving documents - TopK int // Required: Number of results to return - - // Required: Search mode configuration - // Pre-built implementations are provided in the search_mode package: - // - search_mode.Approximate(&ApproximateConfig{...}) - // - search_mode.ExactMatch(field) - // - search_mode.RawStringRequest() - // - search_mode.DenseVectorSimilarity(type, vectorField) - // - search_mode.NeuralSparse(vectorField, &NeuralSparseConfig{...}) - SearchMode SearchMode - - // Optional: Function to parse OpenSearch hits (map[string]interface{}) to Document - // If not provided, a default parser will be used. - ResultParser func(ctx context.Context, hit map[string]interface{}) (doc *schema.Document, err error) - - // Optional: Required only when query vectorization is needed - Embedding embedding.Embedder -} -``` - -## Getting Help - -If you have any questions or feature suggestions, feel free to reach out. - -- [Eino Documentation](https://www.cloudwego.io/docs/eino/) -- [OpenSearch Go Client Documentation](https://github.com/opensearch-project/opensearch-go) diff --git a/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md b/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md deleted file mode 100644 index aaa15a7940a..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_redis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Retriever - Redis -weight: 0 ---- - -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 deleted file mode 100644 index f418f566ddc..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Retriever - volc Knowledge -weight: 0 ---- - -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 deleted file mode 100644 index 2dd786fc7ab..00000000000 --- a/content/en/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Retriever - volc VikingDB -weight: 0 ---- - -## **Overview** - -Volcengine VikingDB retriever is an implementation of the Retriever interface. Volcengine VikingDB is a vector database service provided by Volcengine that offers high-performance vector retrieval capabilities. This component interacts with the service via the Volcengine VikingDB Go SDK. It implements [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide). - -## **Usage** - -### **Initialization** - -Initialize the Volcengine VikingDB retriever via `NewRetriever` with the following main configuration parameters: - -```go -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 - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - 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", - }, - }, -}) -``` - -### **Retrieve Documents** - -Document retrieval is implemented via the `Retrieve` method: - -```go -docs, err := retriever.Retrieve(ctx, "query text", retriever.WithTopK(5)) -``` - -### **Complete Examples** - -#### **Basic Retrieval** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb" -) - -func main() { - ctx := context.Background() - - // Initialize retriever - r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api-vikingdb.volces.com", - Region: "cn-beijing", - AK: "your-ak", - SK: "your-sk", - Collection: "your-collection", - Index: "your-index", - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: true, - ModelName: "model-name", - UseSparse: true, - DenseWeight: 0.5, - }, - TopK: ptrOf(5), - }) - if err != nil { - panic(err) - } - - // Execute retrieval - docs, err := r.Retrieve(ctx, "How to use VikingDB?") - if err != nil { - panic(err) - } - - // Handle results - for _, doc := range docs { - println("Document ID:", doc.ID) - println("Content:", doc.Content) - println("Score:", doc.MetaData["_score"]) - } -} -``` - -#### **Custom Embedding** - -```go -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 example) - embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{}) - if err != nil { - panic(err) - } - - // Initialize retriever - r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api-vikingdb.volces.com", - Region: "cn-beijing", - AK: "your-ak", - SK: "your-sk", - Collection: "your-collection", - Index: "your-index", - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: false, - Embedding: embedder, - }, - }) - if err != nil { - panic(err) - } - - // Execute retrieval - docs, err := r.Retrieve(ctx, "query text") - if err != nil { - panic(err) - } - - // Handle results - for _, doc := range docs { - println(doc.Content) - } -} -``` - -## **Related Documentation** - -- [Eino: Retriever Guide](/docs/eino/core_modules/components/retriever_guide) -- [Volcengine VikingDB Documentation](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 ad093e615f8..1701a61a21d 100644 --- a/content/en/docs/eino/ecosystem_integration/tool/_index.md +++ b/content/en/docs/eino/ecosystem_integration/tool/_index.md @@ -1,9 +1,33 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Tool -weight: 0 +weight: 4 --- +# Tool Component List + +For detailed documentation of each component in this category, please refer to the GitHub README: + + + + + + + + + + + + +
    Component NameChinese DocsEnglish Docs
    MCPREADME_zh.mdREADME.md
    BingsearchREADME_zh.mdREADME.md
    BrowserUseREADME_zh.mdREADME.md
    CommandLineREADME_zh.mdREADME.md
    DuckDuckGoSearchREADME_zh.mdREADME.md
    GooglesearchREADME_zh.mdREADME.md
    HTTPRequestREADME_zh.mdREADME.md
    SequentialThinkingREADME_zh.mdREADME.md
    WikipediaREADME_zh.mdREADME.md
    + +--- + +**Notes**: + +- The links above point directly to the latest documentation in the GitHub repository +- Chinese and English documentation are updated synchronously +- To view historical versions or submit documentation suggestions, please visit the GitHub repository diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md b/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md deleted file mode 100644 index 462e5254167..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_bingsearch.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - Bingsearch -weight: 0 ---- - -## **Bing Search Tool** - -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** - -- Implements `github.com/cloudwego/eino/components/tool.InvokableTool` -- Easy integration with Eino’s tool system -- Configurable search parameters - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/bingsearch -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "log" - "os" - "time" - - "github.com/bytedance/sonic" - "github.com/cloudwego/eino-ext/components/tool/bingsearch" -) - -func main() { - // 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** - -Tool is configurable via `Config`: - -```go -// Config represents the Bing search tool configuration. -type Config struct { - // 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** - -```go -type SearchRequest struct { - Query string `json:"query" jsonschema_description:"The query to search the web for"` - Offset int `json:"page" jsonschema_description:"The index of the first result to return, default is 0"` -} -``` - -### **Response Schema** - -```go -type SearchResponse struct { - Results []*searchResult `json:"results" jsonschema_description:"The results of the search"` -} - -type searchResult struct { - Title string `json:"title" jsonschema_description:"The title of the search result"` - URL string `json:"url" jsonschema_description:"The link of the search result"` - Description string `json:"description" jsonschema_description:"The description of the search result"` -} -``` - -## **More Details** - -- [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 deleted file mode 100644 index 89e78cbc63a..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_browseruse.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - Browseruse -weight: 0 ---- - -## **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** - -- Implements `github.com/cloudwego/eino/components/tool.BaseTool` -- Easy integration with Eino's tool system -- Supports executing browser actions - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/browseruse@latest -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "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() -} -``` - -## **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 deleted file mode 100644 index da50ff58c4e..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_commandline.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - Commandline -weight: 0 ---- - -## **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** - -- Implements `github.com/cloudwego/eino/components/tool.InvokableTool` -- Easy integration with Eino's tool system -- Support executing command-line instructions in Docker containers - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/commandline@latest -``` - -## **Quick Start** - -Editor: - -```go -package main - -import ( - "context" - "log" - - "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) } - // 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 -package main - -import ( - "context" - "log" - - "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) } - 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** - -- [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 deleted file mode 100644 index b55eb339171..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - DuckDuckGoSearch -weight: 0 ---- - -## **Overview** - -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** - -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", - ToolDesc: "search for information by duckduckgo", - Region: duckduckgo.RegionWT, // The geographical region for results. - MaxResults: 3, // Limit the number of results returned. -}) -``` - -### **Search Parameters** - -```go -type TextSearchRequest struct { - // Query is the user's search query - Query string `json:"query"` - // TimeRange is the search time range - // Default: TimeRangeAny - TimeRange TimeRange `json:"time_range"` -} -``` - -### **Complete Example** - -```go -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "time" - - "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2" -) - -func main() { - ctx := context.Background() - - // Create configuration - config := &duckduckgo.Config{ - MaxResults: 3, // Limit to return 20 results - Region: duckduckgo.RegionWT, - Timeout: 10 * time.Second, - } - - // Create search client - tool, err := duckduckgo.NewTextSearchTool(ctx, config) - if err != nil { - log.Fatalf("NewTextSearchTool of duckduckgo failed, err=%v", err) - } - - results := make([]*duckduckgo.TextSearchResult, 0, config.MaxResults) - - searchReq := &duckduckgo.TextSearchRequest{ - Query: "eino", - } - jsonReq, err := json.Marshal(searchReq) - if err != nil { - log.Fatalf("Marshal of search request failed, err=%v", err) - } - - resp, err := tool.InvokableRun(ctx, string(jsonReq)) - if err != nil { - log.Fatalf("Search of duckduckgo failed, err=%v", err) - } - - var searchResp duckduckgo.TextSearchResponse - if err = json.Unmarshal([]byte(resp), &searchResp); err != nil { - log.Fatalf("Unmarshal of search response failed, err=%v", err) - } - - results = append(results, searchResp.Results...) - - // Print results - fmt.Println("Search Results:") - fmt.Println("==============") - fmt.Printf("%s\n", searchResp.Message) - for i, result := range results { - fmt.Printf("\n%d. Title: %s\n", i+1, result.Title) - fmt.Printf(" URL: %s\n", result.URL) - fmt.Printf(" Summary: %s\n", result.Summary) - } - fmt.Println("") - fmt.Println("==============") -} -``` - -### **Search Result Example** - -```json -{ - "message": "Found 3 results successfully.", - "results": [ - { - "title": "GitHub - cloudwego/eino: The ultimate LLM/AI application development ...", - "url": "https://github.com/cloudwego/eino", - "summary": "Eino ['aino] (pronounced similarly to \"I know\") aims to be the ultimate LLM application development framework in Golang. Drawing inspirations 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 ..." - }, - { - "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." - } - ] -} -``` - -## **References** - -- [Eino: ToolsNode Guide](/docs/eino/core_modules/components/tools_node_guide) -- [Tool - Googlesearch](/docs/eino/ecosystem_integration/tool/tool_googlesearch) -- [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 deleted file mode 100644 index d8612985e74..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_googlesearch.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - Googlesearch -weight: 0 ---- - -## **Overview** - -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** - -Google search tool is initialized via `NewGoogleSearchTool` with core parameters: - -```go -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: results per page - Lang: "zh-CN", // Optional: UI language - ToolName: "google_search", // Optional: tool name - ToolDesc: "google search tool", // Optional: tool description -}) -``` - -Note: obtain APIKey and SearchEngineID from Google Programmable Search Engine: https://programmablesearchengine.google.com/controlpanel/all - -### **Search Parameters** - -```go -type SearchRequest struct { - Query string `json:"query"` - Num int `json:"num"` - Offset int `json:"offset"` - Lang string `json:"lang"` -} -``` - -### **Complete Example** - -```go -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino-ext/components/tool/googlesearch" -) - -func main() { - ctx := context.Background() - - googleAPIKey := os.Getenv("GOOGLE_API_KEY") - googleSearchEngineID := os.Getenv("GOOGLE_SEARCH_ENGINE_ID") - - if googleAPIKey == "" || googleSearchEngineID == "" { - log.Fatal("[GOOGLE_API_KEY] and [GOOGLE_SEARCH_ENGINE_ID] must set") - } - - // create tool - searchTool, err := googlesearch.NewTool(ctx, &googlesearch.Config{ - APIKey: googleAPIKey, - SearchEngineID: googleSearchEngineID, - Lang: "zh-CN", - Num: 5, - }) - if err != nil { - log.Fatal(err) - } - - // prepare params - req := googlesearch.SearchRequest{ - Query: "Golang concurrent programming", - Num: 3, - Lang: "en", - } - - args, err := json.Marshal(req) - if err != nil { - log.Fatal(err) - } - - // do search - resp, err := searchTool.InvokableRun(ctx, string(args)) - if err != nil { - log.Fatal(err) - } - - var searchResp googlesearch.SearchResult - if err := json.Unmarshal([]byte(resp), &searchResp); err != nil { - log.Fatal(err) - } - - // Print results - fmt.Println("Search Results:") - fmt.Println("==============") - for i, result := range searchResp.Items { - fmt.Printf("\n%d. Title: %s\n", i+1, result.Title) - fmt.Printf(" Link: %s\n", result.Link) - fmt.Printf(" Desc: %s\n", result.Desc) - } - fmt.Println("") - fmt.Println("==============") - - // seems like: - // Search Results: - // ============== - // 1. Title: My Concurrent Programming book is finally PUBLISHED!!! : r/golang - // Link: https://www.reddit.com/r/golang/comments/18b86aa/my_concurrent_programming_book_is_finally/ - // Desc: Posted by u/channelselectcase - 398 votes and 46 comments - // 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 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… - - // ============== -} -``` - -### **Search Result Example** - -```json -{ - "query": "Golang concurrent programming", - "items": [ - { - "link": "https://example.com/article1", - "title": "Go Concurrent Programming Practice", - "snippet": "An article about Go concurrent programming...", - "desc": "Detailed introduction to goroutines and channels in Go..." - }, - { - "link": "https://example.com/article2", - "title": "Understanding Concurrency in Go", - "snippet": "A comprehensive guide to concurrent programming...", - "desc": "Learn about Go's concurrency model and best practices..." - } - ] -} -``` - -## **References** - -- [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 deleted file mode 100644 index 0f90c03bdd1..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_httprequest.md +++ /dev/null @@ -1,304 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - httprequest -weight: 0 ---- - -## **HTTP Request Tool** - -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** - -- Implements `github.com/cloudwego/eino/components/tool.InvokableTool` -- Supports GET, POST, PUT, and DELETE requests -- Configurable headers and `http.Client` -- Simple integration with Eino’s tool system - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/httprequest -``` - -## **Quick Start** -Below are standalone examples for GET and POST tools. - -### **GET Example** - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/bytedance/sonic" - get "github.com/cloudwego/eino-ext/components/tool/httprequest/get" -) - -func main() { - // 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 Example** - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/bytedance/sonic" - post "github.com/cloudwego/eino-ext/components/tool/httprequest/post" -) - -func main() { - config := &post.Config{} - - ctx := context.Background() - - 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}`, - } - - args, err := sonic.Marshal(request) - if err != nil { - log.Fatalf("Error marshaling JSON: %v", err) - } - - resp, err := tool.InvokableRun(ctx, string(args)) - if err != nil { - log.Fatalf("Post failed: %v", err) - } - - fmt.Println(resp) -} -``` - -## **Configuration** - -Shared `Config` fields (example): - -```go -// Config represents common settings for HTTP request tools. -type Config struct { - // 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 -} -``` - -Request Schemas: - -```go -type GetRequest struct { - URL string `json:"url" jsonschema_description:"URL to perform GET request on"` -} - -type PostRequest struct { - URL string `json:"url" jsonschema_description:"URL to perform POST request on"` - Body string `json:"body" jsonschema_description:"Request body to send for POST"` -} -``` - -## **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" - 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" -) - -func float32Ptr(f float32) *float32 { return &f } - -func main() { - 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 - version: "1.0.0" -servers: - - url: https://jsonplaceholder.typicode.com -paths: - /posts: - get: - summary: Get posts - parameters: - - name: _limit - in: query - required: false - schema: - type: integer - example: 2 - description: Limit the number of results - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - userId: - type: integer - id: - type: integer - title: - type: string - body: - type: string - /comments: - get: - summary: Get comments - parameters: - - name: _limit - in: query - required: false - schema: - type: integer - example: 2 - description: Limit the number of results - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - postId: - type: integer - id: - type: integer - name: - type: string - email: - type: string - body: - type: string -` - - 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) } -} -``` - -## **More Information** - -- [Eino Documentation](https://github.com/cloudwego/eino) -- [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 deleted file mode 100644 index fd9fb1ee76d..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_mcp.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Eino Tool - MCP -weight: 0 ---- - -## Overview - -[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is a standardized open protocol introduced by Anthropic for model access to resources. Eino provides wrappers so you can directly use resources exposed by an existing MCP Server. - -This section introduces the [MCPTool](https://modelcontextprotocol.io/docs/concepts/tools) wrapper, which implements Eino's `InvokableTool` interface ([Eino: ToolsNode guide](/docs/eino/core_modules/components/tools_node_guide)). - - - -Also see: - -[ChatTemplate - MCP](/docs/eino/ecosystem_integration/chat_template/chat_template_mcp) - -## Usage - -### QuickStart - -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" - -// stdio client -cli, err := client.NewStdioMCPClient(myCommand, myEnvs, myArgs...) - -// sse client -cli, err := client.NewSSEMCPClient(myBaseURL) -// sse client needs to manually start asynchronous communication -// while stdio does not require it. -err = cli.Start(ctx) -``` - -mcp-go also supports other methods to create a Client (such as InProcess). For more information, see: [https://mcp-go.dev/transports](https://mcp-go.dev/transports) - -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" - -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) -``` - -Then create Eino tools using the Client: - -```go -import "github.com/cloudwego/eino-ext/components/tool/mcp" - -tools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli}) -``` - -Tools can be called directly: - -``` -for i, mcpTool := range mcpTools { - fmt.Println(i, ":") - info, err := mcpTool.Info(ctx) - if err != nil { - log.Fatal(err) - } - fmt.Println("Name:", info.Name) - fmt.Println("Desc:", info.Desc) - fmt.Println() -} -``` - -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" -) - -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}, -}) -``` - -### Specify Tool by Name - -`GetTools` supports filtering by tool names to avoid unintended tools: - -```go -import "github.com/cloudwego/eino-ext/components/tool/mcp" - -tools, err := mcp.GetTools(ctx, &mcp.Config{ - Cli: cli, - ToolNameList: []string{"name"}, -}) -``` - -## More Information - -Practice example: [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) diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md b/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md deleted file mode 100644 index 49d0b99a6be..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - sequentialthinking -weight: 0 ---- - -## **Sequential Thinking Tool** - -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** - -- Guided step-by-step thinking -- Dynamic prompts and self-reflection -- Improves problem-solving quality - -## **Use Cases** - -- 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 - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/sequentialthinking@latest -``` - -## **Quick Start** - -```go -package main - -import ( - "context" - "fmt" - - "github.com/bytedance/sonic" - - "github.com/cloudwego/eino-ext/components/tool/sequentialthinking" -) - -func main() { - 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) -} -``` diff --git a/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md b/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md deleted file mode 100644 index 7b1b3618319..00000000000 --- a/content/en/docs/eino/ecosystem_integration/tool/tool_wikipedia.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -title: Tool - wikipedia -weight: 0 ---- - -## **Overview** - -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** - -- Implements `github.com/cloudwego/eino/components/tool.InvokableTool` -- Easy integration with Eino’s tool system -- Configurable search parameters - -## **Installation** - -```bash -go get github.com/cloudwego/eino-ext/components/tool/wikipedia -``` - -## **Quick Start** - -```go -package main - -import ( - "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) } - - tools := []tool.BaseTool{t} - // ... configure and use ToolsNode -} -``` - -## **Configuration** - -```go -type Config struct { - // 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 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 caps returned page content length; longer content is truncated. - DocMaxChars int `json:"doc_max_chars"` - - // Timeout is the max wait time for HTTP response. - Timeout time.Duration `json:"timeout"` - - // TopK number of search results to return. - TopK int `json:"top_k"` - - // MaxRedirect max allowed redirects. - MaxRedirect int `json:"max_redirect"` - - // Language for Wikipedia search. - Language string `json:"language"` - - // 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"` -} -``` - -### **Request Schema** - -```go -type SearchRequest struct { - Query string `json:"query" jsonschema_description:"The query to search the web for"` -} -``` - -### **Response Schema** - -```go -type SearchResponse struct { - Results []*Result `json:"results" jsonschema_description:"The results of the search"` -} -type SearchResult struct { - Title string `json:"title" jsonschema_description:"The title of the search result"` - URL string `json:"url" jsonschema_description:"The url of the search result"` - Extract string `json:"extract" jsonschema_description:"The extract of the search result"` - Snippet string `json:"snippet" jsonschema_description:"The snippet of the search result"` -} -``` - -## **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 8fbb5b82420..1f573d65c70 100644 --- a/content/en/docs/eino/overview/_index.md +++ b/content/en/docs/eino/overview/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Overview' @@ -9,26 +9,26 @@ weight: 1 ## Introduction -**Eino ['aino]** (pronounced like "I know", reflecting the vision we aim for) is designed to be the ultimate LLM application development framework based on Go. Drawing inspiration from excellent open-source LLM application development frameworks such as LangChain and LlamaIndex, while incorporating cutting-edge research and practical applications, Eino provides an LLM application development framework that emphasizes simplicity, extensibility, reliability, and effectiveness, aligned with Go programming conventions. +**Eino['aino]** (pronunciation similar to: i know, hoping the framework can achieve the vision of "i know") aims to provide the ultimate LLM application development framework based on the Go language. It draws inspiration from many excellent LLM application development frameworks in the open-source community, such as LangChain and LlamaIndex, while also incorporating cutting-edge research results and practical applications, providing an LLM application development framework that emphasizes simplicity, extensibility, reliability, and effectiveness, and is more in line with Go programming conventions. -Eino provides value through: +Eino provides the following value: -- A carefully curated set of **component** abstractions and implementations that can be easily reused and composed to build LLM applications. -- **Agent Development Kit (ADK)**, providing high-level abstractions for building AI agents, supporting multi-agent orchestration, human-in-the-loop interrupt mechanisms, and preset agent patterns. -- A powerful **orchestration** framework that handles heavy lifting like type checking, streaming data handling, concurrency management, aspect injection, and option assignment for you. -- A concise and thoughtfully designed **API**. -- A growing collection of best practices in the form of integrated **flows** and **examples**. -- Practical **DevOps tools** covering the entire development lifecycle from visual development and debugging to online tracing and evaluation. +- A carefully curated set of **component** abstractions and implementations that can be easily reused and combined for building LLM applications. +- **Agent Development Kit (ADK)**, providing high-level abstractions for building AI agents, supporting multi-agent orchestration, human-in-the-loop interrupt mechanisms, and pre-built agent patterns. +- A powerful **orchestration** framework that handles the heavy lifting of type checking, stream processing, concurrency management, aspect injection, option assignment, and more. +- A carefully designed **API** with a focus on simplicity and clarity. +- An ever-expanding collection of best practices in the form of integrated **flows** and **examples**. +- A set of practical **DevOps tools** covering the entire development lifecycle from visual development and debugging to online tracing and evaluation. -With these capabilities and tools, Eino standardizes, simplifies, and accelerates work across different stages of the AI application development lifecycle: +With these capabilities and tools, Eino can standardize, simplify operations, and improve efficiency at different stages of the AI application development lifecycle: -[Eino GitHub Repository](https://github.com/cloudwego/eino) +[Eino GitHub Repository Link](https://github.com/cloudwego/eino) ## Quick Start -Use components directly: +Direct component usage: ```go model, _ := openai.NewChatModel(ctx, config) // create an invokable LLM instance @@ -37,22 +37,22 @@ message, _ := model.Generate(ctx, []*Message{ UserMessage("what does the future AI App look like?")}) ``` -Of course, you can use it this way, Eino provides many useful components out of the box. But by using orchestration, you can achieve more, for three reasons: +Of course, you can use it like this - Eino provides many useful out-of-the-box components. But by using orchestration, you can achieve more, for three reasons: -- 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. +- Orchestration encapsulates common patterns of LLM applications. +- Orchestration solves the challenge of handling LLM streaming responses. +- Orchestration handles type safety, concurrency management, aspect injection, and option assignment for you. -Eino offers three orchestration APIs: +Eino provides three sets of APIs for orchestration: - - - - + + + +
    APIFeatures & Use Cases
    ChainA simple forward-only directed chain.
    GraphA directed graph (cyclic or acyclic). Powerful and flexible.
    WorkflowAn acyclic directed graph with field-level data mapping in structs.
    APIFeatures and Use Cases
    ChainSimple chain-style directed graph, can only move forward.
    GraphDirected cyclic or acyclic graph. Powerful and flexible.
    WorkflowDirected acyclic graph with data mapping at the struct field level.
    -Let's build a simple chain: a **ChatTemplate** followed by a **ChatModel**. +Let's create a simple chain: a ChatTemplate followed by a ChatModel. @@ -64,7 +64,7 @@ chain, _ := NewChain[map[string]any, *Message](). chain.Invoke(ctx, map[string]any{"query": "what's your name?"}) ``` -Now, let's create a **Graph** where a **ChatModel** either returns a result directly or makes at most one tool call. +Now, let's create a Graph, a ChatModel that either outputs the result directly or calls a Tool at most once. @@ -89,7 +89,7 @@ return err out, err := compiledGraph.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"}) ``` -Next, let's build a **Workflow** to flexibly map inputs and outputs at the field level: +Now, let's create a Workflow that can flexibly map inputs and outputs at the field level: @@ -144,13 +144,13 @@ our, err := runnable.Invoke(ctx, []*schema.Message{ Eino's **graph orchestration** provides the following capabilities out of the box: -- **Type checking**: Ensures input/output types between two nodes match at compile time. -- **Stream handling**: Concatenates message streams before passing them to ChatModel and ToolsNode nodes when needed, and duplicates the stream for callback handlers. -- **Concurrency management**: Since StatePreHandler is thread-safe, shared state can be safely read and written. -- **Aspect injection**: Injects callback aspects before and after ChatModel execution if the specified ChatModel implementation does not inject them itself. -- **Option assignment**: Options can be set globally, per component type, or per specific node. +- **Type Checking**: Ensures that the input and output types of two nodes match at compile time. +- **Stream Processing**: Concatenates message streams before passing them to ChatModel and ToolsNode nodes when needed, and copies the stream to callback handlers. +- **Concurrency Management**: Since StatePreHandler is thread-safe, shared state can be safely read and written. +- **Aspect Injection**: If the specified ChatModel implementation does not inject callbacks itself, callback aspects will be injected before and after ChatModel execution. +- **Option Assignment**: Invoke Options can be set globally, or for specific component types or specific nodes. -For example, it's easy to extend a compiled graph with callbacks: +For example, you can easily extend compiled graphs with callbacks: ```go handler := NewHandlerBuilder(). @@ -167,7 +167,7 @@ handler := NewHandlerBuilder(). compiledGraph.Invoke(ctx, input, WithCallbacks(handler)) ``` -And you can easily assign options to different nodes: +Or you can easily assign options to different nodes: ```go // assign to All nodes @@ -180,11 +180,11 @@ compiledGraph.Invoke(ctx, input, WithChatModelOption(WithTemperature(0.5)) compiledGraph.Invoke(ctx, input, WithCallbacks(handler).DesignateNode("node_1")) ``` -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. +Now, let's create a "ReAct" agent: a ChatModel bound with some Tools. It receives input messages, autonomously decides whether to call a Tool or output the final result. The Tool execution result becomes the input message to the chat model again, serving as context for the next round of autonomous decision-making. -Eino's **Agent Development Kit (ADK)** provides a ready-to-use `ChatModelAgent` to implement this pattern: +Eino's **Agent Development Kit (ADK)** provides the out-of-the-box `ChatModelAgent` to implement this pattern: ```go agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ @@ -211,7 +211,7 @@ for { ADK handles the ReAct loop internally, emitting events for each step of the agent reasoning process. -Beyond the basic ReAct pattern, ADK provides powerful capabilities for building production-grade agent systems: +Beyond the basic ReAct pattern, ADK also provides powerful capabilities for building production-grade agent systems: **Multi-Agent and Context Management**: Agents can transfer control to sub-agents or be wrapped as tools. The framework automatically manages conversation context across agent boundaries: @@ -222,10 +222,10 @@ mainAgentWithSubs, _ := adk.SetSubAgents(ctx, mainAgent, []adk.Agent{researchAge When `mainAgent` transfers to `researchAgent`, conversation history is automatically rewritten to provide appropriate context for the sub-agent. -Agents can also be wrapped as tools, allowing one agent to call another agent within its tool-calling workflow: +Agents can also be wrapped as tools, allowing one agent to call another within its tool-calling workflow: ```go -// Wrap an agent as a tool that can be called by other agents +// Wrap agent as a tool that can be called by other agents researchTool := adk.NewAgentTool(ctx, researchAgent) ``` @@ -239,10 +239,10 @@ return adk.Interrupt(ctx, "Please confirm this action") iter, _ := runner.Resume(ctx, checkpointID) ``` -**Preset Agent Patterns**: Out-of-the-box implementations for common architectures: +**Pre-built Agent Patterns**: Out-of-the-box implementations for common architectures: ```go -// Deep Agent: A battle-tested complex task orchestration pattern, +// Deep Agent: A battle-tested pattern for complex task orchestration, // with built-in task management, sub-agent delegation, and progress tracking deepAgent, _ := deep.New(ctx, &deep.Config{ Name: "deep_agent", @@ -252,13 +252,13 @@ deepAgent, _ := deep.New(ctx, &deep.Config{ ToolsConfig: adk.ToolsConfig{...}, }) -// Supervisor pattern: One agent coordinates multiple experts +// Supervisor pattern: one agent coordinates multiple experts supervisorAgent, _ := supervisor.New(ctx, &supervisor.Config{ Supervisor: coordinatorAgent, SubAgents: []adk.Agent{writerAgent, reviewerAgent}, }) -// Sequential execution: Agents run in sequence +// Sequential execution: agents run in sequence seqAgent, _ := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{ SubAgents: []adk.Agent{plannerAgent, executorAgent, summarizerAgent}, }) @@ -281,96 +281,96 @@ agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ ### Rich Components -- Common building blocks are abstracted as **components**, each with multiple ready-to-use **component implementations**. - - Abstractions include ChatModel, Tool, PromptTemplate, Retriever, Document Loader, Lambda, and more. - - Each component type has its own interface: defining input and output types, option types, and sensible stream processing paradigms. +- Common building blocks are abstracted as **components**, and each component abstraction has multiple out-of-the-box **component implementations**. + - Component abstractions such as ChatModel, Tool, PromptTemplate, Retriever, Document Loader, Lambda, etc. + - Each component type has its own interface: defined input and output types, defined option types, and reasonable stream processing paradigms. - Implementation details are transparent. When orchestrating components, you only need to focus on the abstraction level. -- 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, implementation details remain transparent. For instance, you can use MultiQueryRetriever anywhere a Retriever is accepted. +- Implementations can be nested and contain complex business logic. + - ReAct Agent, MultiQueryRetriever, Host MultiAgent, etc. They consist of multiple components and complex business logic. + - From the outside, their implementation details remain transparent. For example, MultiQueryRetriever can be used anywhere that accepts a Retriever. ## **Agent Development Kit (ADK)** The **ADK** package provides high-level abstractions optimized for building AI agents: -- **ChatModelAgent**: ReAct-style agent that automatically handles tool calling, conversation state, and reasoning loops. +- **ChatModelAgent**: ReAct-style agent that automatically handles tool calls, conversation state, and reasoning loops. - **Multi-Agent and Context Engineering**: Build hierarchical agent systems where conversation history is automatically managed during agent transfers and agent-as-tool calls, enabling seamless context sharing between specialized agents. -- **Workflow Agents**: Compose agents using `SequentialAgent`, `ParallelAgent`, and `LoopAgent` for complex execution flows. -- **Human-in-the-Loop**: `Interrupt` and `Resume` mechanisms with checkpoint persistence for workflows requiring human approval or input. -- **Preset Patterns**: Out-of-the-box implementations including Deep Agent (task orchestration), Supervisor (hierarchical coordination), and Plan-Execute-Replan. +- **Workflow Agents**: Combine agents using `SequentialAgent`, `ParallelAgent`, and `LoopAgent` for complex execution flows. +- **Human-in-the-Loop**: `Interrupt` and `Resume` mechanism with checkpoint persistence for workflows requiring human approval or input. +- **Pre-built Patterns**: Out-of-the-box implementations including Deep Agent (task orchestration), Supervisor (hierarchical coordination), and Plan-Execute-Replan. - **Agent Middleware**: Extensible middleware system for adding tools (filesystem operations) and managing context (token reduction). ### Powerful Orchestration (Graph/Chain/Workflow) For fine-grained control, Eino provides **graph orchestration** capabilities where data flows from Retriever / Document Loader / ChatTemplate to ChatModel, then to Tool, and is parsed into the final answer. -- Component instances are **nodes** of the graph, and **edges** are the data flow channels. +- Component instances are **Nodes** of the graph, and **Edges** are data flow channels. - Graph orchestration is powerful and flexible enough to implement complex business logic: - - **Type checking, stream handling, concurrency management, aspect injection, and option distribution** are all handled by the framework. - - At runtime, you can perform **branch** execution, read/write global **state**, or use workflows for field-level data mapping. + - **Type checking, stream processing, concurrency management, aspect injection, and option assignment** are all handled by the framework. + - **Branch** execution at runtime, read and write global **State**, or use Workflow for field-level data mapping. ## **Callbacks** -**Callbacks** handle cross-cutting concerns like logging, tracing, and metrics. Callbacks can be applied directly to components, orchestration graphs, or ADK agents. +**Callbacks** handle cross-cutting concerns such as logging, tracing, and metrics. Callbacks can be applied directly to components, orchestration graphs, or ADK agents. - Five callback types are supported: OnStart, OnEnd, OnError, OnStartWithStreamInput, OnEndWithStreamOutput. - Custom callback handlers can be added at runtime via Options. -### Robust Streaming +### Complete Stream Processing -- Stream processing is crucial because ChatModel emits fragments of the final message in real time. This matters even more under orchestration, where more components need to handle fragmented message data. -- 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. -- With these stream processing capabilities, whether a component can handle streams or outputs streams becomes transparent to users. -- Compiled graphs can run in four different input/output paradigms: +- Stream data processing is important because ChatModel outputs fragments of complete messages in real-time as it generates them. This becomes especially important in orchestration scenarios where more components need to handle fragmented message data. +- For downstream nodes that only accept non-streaming input (like ToolsNode), Eino automatically **concatenates** the stream. +- During graph execution, when a stream is needed, Eino automatically **converts** non-streaming to streaming. +- When multiple streams converge to a single downstream node, Eino automatically **merges** these streams. +- When a stream is passed to multiple different downstream nodes or to callback handlers, Eino automatically **copies** these streams. +- Orchestration elements like **Branch** or **StateHandler** are also stream-aware and can handle streams. +- With these stream data processing capabilities, whether a component "can handle streams or will output streams" becomes transparent to users. +- Compiled Graphs can be run with 4 different stream input/output paradigms: - - - - - + + + + +
    ParadigmDescription
    InvokeAccepts non-streaming I, returns non-streaming O
    StreamAccepts non-streaming I, returns StreamReader[O]
    CollectAccepts StreamReader[I], returns non-streaming O
    TransformAccepts StreamReader[I], returns StreamReader[O]
    Stream Processing ParadigmExplanation
    InvokeReceives non-stream type I, returns non-stream type O
    StreamReceives non-stream type I, returns stream type StreamReader[O]
    CollectReceives stream type StreamReader[I], returns non-stream type O
    TransformReceives stream type StreamReader[I], returns stream type StreamReader[O]
    -## Eino Architecture +## Eino Framework Structure The Eino framework consists of several parts: -- [Eino](https://github.com/cloudwego/eino): Contains type definitions, streaming data handling mechanisms, component abstraction definitions, orchestration functionality, callback mechanisms, etc. +- [Eino](https://github.com/cloudwego/eino): Contains type definitions, stream data processing mechanisms, component abstraction definitions, orchestration functionality, callback mechanisms, etc. - [EinoExt](https://github.com/cloudwego/eino-ext): Component implementations, callback handler implementations, component usage examples, and various tools such as evaluators, prompt optimizers, etc. > 💡 -> For ByteDance-internal components, there is a corresponding internal repository: +> For components used internally at ByteDance, there are corresponding internal code repositories: > -> EinoBytedExt: [https://code.byted.org/search/flow/eino-byted-ext](https://code.byted.org/search/flow/eino-byted-ext) +> EinoBytedExt: [https://code.byted.org/flow/eino-byted-ext](https://code.byted.org/flow/eino-byted-ext) > -> It includes components currently intended for internal use, such as llmgateway, bytedgpt, fornax tracing, bytees, etc. +> Contains component implementations currently positioned for internal use, such as llmgateway, bytedgpt, fornax tracing, bytees, etc. -- [Eino DevOps](https://github.com/cloudwego/eino-ext/tree/main/devops): Visual development, visual debugging, etc. -- [EinoExamples](https://github.com/cloudwego/eino-examples): A repository containing example applications and best practices. +- [Eino Devops](https://github.com/cloudwego/eino-ext/tree/main/devops): Visual development, visual debugging, etc. +- [EinoExamples](https://github.com/cloudwego/eino-examples): A code repository containing example applications and best practices. -See also: [Eino Architecture Overview](/docs/eino/overview/eino_arch/) +See: [Eino Framework Structure Description](/docs/eino/overview/Eino Framework Structure Description) -## Documentation +## Detailed Documentation -For learning and using Eino, we provide a comprehensive Eino user manual to help you quickly understand concepts in Eino and master the skills of developing and designing AI applications based on Eino. Try it here: [Eino User Manual](https://www.cloudwego.io/docs/eino/) +For learning and using Eino, we provide comprehensive Eino user manuals to help you quickly understand concepts in Eino and master the skills of designing AI applications based on Eino. Get started with the [Eino User Manual](https://www.cloudwego.io/docs/eino/)~ -If you want to get hands-on quickly and understand the process of building AI applications with Eino, we recommend starting with: [Eino: Quick Start](https://www.cloudwego.io/docs/eino/quick_start/) +For a quick start to understand the process of building AI applications with Eino, we recommend first reading [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) +Complete API Reference: [https://pkg.go.dev/github.com/cloudwego/eino](https://pkg.go.dev/github.com/cloudwego/eino) -## Dependencies +## Dependency Requirements -- Go 1.18 or higher +- Go 1.18 and above ## **Code Standards** -This repository has `golangci-lint` checks enabled to enforce basic code standards. You can check locally with the following command: +This repository has enabled `golangci-lint` checking to enforce basic code standards. You can check locally with the following command: ```bash golangci-lint run ./... @@ -378,26 +378,26 @@ golangci-lint run ./... Main rules include: -- Exported functions, interfaces, packages, etc. need comments that conform to GoDoc standards. -- Code format must conform to `gofmt -s` standards. -- Import order must conform to `goimports` standards (std -> third party -> local). +- Exported functions, interfaces, packages, etc. need to have comments that comply with GoDoc standards. +- Code format must comply with `gofmt -s` standards. +- Import order must comply with `goimports` standards (std -> third party -> local). ## Security -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). +If you find a potential security issue in this project, or if you think you may have found a security issue, please notify the ByteDance security team through our [Security Center](https://security.bytedance.com/src) or [Vulnerability Report Email](mailto:sec@bytedance.com). -Please **do not** create a public GitHub issue. +Please **do not** create public GitHub Issues. -## Contact +## Contact Us - 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 user group ([register for Lark](https://www.feishu.cn/) and scan the QR code to join) +- Lark user group (scan the QR code to join the group after [registering Lark](https://www.feishu.cn/)) - ByteDance internal OnCall group -## License +## Open Source 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/eino_adk0_1.md b/content/en/docs/eino/overview/eino_adk0_1.md index 2c8ddb19634..e5c773a8daa 100644 --- a/content/en/docs/eino/overview/eino_adk0_1.md +++ b/content/en/docs/eino/overview/eino_adk0_1.md @@ -1,62 +1,79 @@ --- Description: "" -date: "2025-12-09" +date: "2026-01-20" lastmod: "" tags: [] -title: 'Eino ADK: Master Core Agent Patterns and Build a Production-Grade Agent System' -weight: 3 +title: 'Eino ADK: Master AI Agent Core Design Patterns, Build Agent Systems from Scratch' +weight: 6 --- -# Introduction +# Preface -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. +As large language models break through the bottleneck of "understanding and generation", Agents rapidly become the mainstream form of AI deployment. From intelligent customer service to automated office work, almost all scenarios require Agents to bridge LLM capabilities and execute specific tasks. -But pain points emerge: many teams struggle to connect LLMs with systems; agents lack state management and “forget”, while complex flows raise development complexity. +But pain points emerge with technological evolution. Some teams struggle to connect LLMs with business systems, causing Agents to only "talk empty"; some suffer from missing state management, making Agents frequently "lose memory" during task execution. Complex interaction flows further increase development difficulty. -**Eino ADK (Agent Development Kit)** provides Go developers with a complete, flexible, and powerful framework for agent development — addressing core challenges head-on. +For this reason, **Eino ADK (Agent Development Kit) was created to provide Go developers with a complete, flexible, and powerful agent development framework**, directly addressing core challenges in traditional development. -## What is an Agent? +## 🙋 What is an Agent? -Agents are autonomous, executable intelligent units that can learn, adapt, and make decisions. Core capabilities include: +An Agent represents an independent, executable intelligent task unit that can learn autonomously, adapt, and make decisions. Main 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 +- **Reasoning**: Agents can analyze data, identify patterns, use logic and available information to draw conclusions, make inferences, and solve problems. +- **Action**: Agents take actions or execute tasks based on decisions, plans, or external inputs to achieve goals. +- **Observation**: Agents autonomously collect relevant information (such as computer vision, natural language processing, or sensor data analysis) to understand context and lay the foundation for informed decision-making. +- **Planning**: Agents can determine necessary steps, evaluate potential actions, and select the best course of action based on available information and expected outcomes. +- **Collaboration**: Agents can effectively collaborate with others (whether humans or other AI agents) in complex and dynamic environments. -Any LLM-interaction scenario can be abstracted as an agent, for example: +You can think of it as an "intelligent entity" that can understand instructions, execute tasks, and provide responses. Any scenario requiring interaction with large language models (LLMs) can be abstracted as an Agent. For example: -- A weather query agent -- A meeting scheduler agent -- A domain-specific QA agent +- An Agent for querying weather information. +- An Agent for scheduling meetings. +- An Agent capable of answering domain-specific knowledge questions. -## What is Eino ADK? +## 🙋‍♂️ 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/). +[Eino ADK](https://github.com/cloudwego/eino) is an Agent and Multi-Agent development framework designed specifically for the Go language, with design references from [Google-ADK](https://google.github.io/adk-docs/agents/)'s definitions of Agents and collaboration mechanisms. -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: +It's not just a tool library, but a complete agent development system: through unified abstract interfaces, flexible composition patterns, and powerful collaboration mechanisms, it breaks down complex AI applications into independent, composable agent units, allowing developers to build complex agent systems like assembling LEGO blocks: -- 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. +- **Less glue code**: Unified interfaces and event streams make complex task decomposition more natural. +- **Fast orchestration**: Preset patterns + workflows assemble pipelines in minutes. +- **More controllable**: Interruptible, resumable, auditable - Agent collaboration process is "visible". -Design philosophy: “simple things are simple; complex things are possible”. Focus on business logic without low-level complexity. +Whether you're a newcomer to AI applications or an experienced developer, ADK can provide you with suitable tools and patterns. Its design philosophy is "simple things are simple to do, complex things are also achievable" - letting developers focus on implementing business logic without worrying about underlying technical complexity. # Core Building Blocks -## ChatModelAgent — the Brain for Decisions +## 🧠 ChatModelAgent: The Brain for Intelligent Decisions -`ChatModelAgent` implements the classic [ReAct](https://react-lm.github.io/) loop: +`ChatModelAgent` is the most important pre-built component in ADK. It encapsulates the interaction logic with large language models and implements the classic [ReAct](https://react-lm.github.io/) (Reason-Act-Observe) pattern. The running process is: -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 +1. Call LLM (Reason) +2. LLM returns tool call request (Action) +3. ChatModelAgent executes tool (Act) +4. Return tool results to LLM (Observation), continue generating with previous context until the model determines no Tool call is needed, then end. -Use cases include structured research and IT operations troubleshooting, with progressive reasoning and validation. Create a tool-enabled `ChatModelAgent` quickly: +The core of the ReAct pattern is the closed loop of "**Think → Act → Observe → Think again**", solving the pain points of traditional Agents' "blind action" or "disconnect between reasoning and action". Here are some possible practical scenarios: + +- **Industry Track Analysis**: Using the ReAct pattern avoids information overload from collecting all information at once, focusing on core issues through step-by-step reasoning; using data to verify thinking rather than relying on intuition for decisions, the process is explainable, improving the accuracy of generated reports. + - **Think-1**: To judge track potential, need 4 types of information: "policy support strength, industry growth rate, leading company profitability, supply chain bottlenecks". + - **Act-1**: Call API to get overall industry financial report data + - **Think-2**: Analyze data, determine high industry growth + policy endorsement, but rising upstream prices may squeeze mid-to-downstream profits, need further verification of impact + - **Act-2**: Call API to get supply/demand, industry research reports and other detailed data + - **Think-3**: Integrate conclusions to generate analysis report, attach key data sources +- **IT Fault Operations**: Using the ReAct pattern gradually narrows down the problem scope, avoiding blind operations; each step has evidence, convenient for secondary verification by operations engineers before implementing solutions, providing a basis for subsequent review and preventive measures. + - **Think-1**: Clarify common causes of failure, e.g., common causes of downtime are "CPU overload, insufficient memory, full disk, service crash", need to check basic monitoring data first + - **Act-1**: Call "Monitoring System API" to query server metrics data + - **Think-2**: Determine main cause, e.g., if CPU utilization is abnormal, further investigate which processes have high CPU usage + - **Act-2**: Use "Process Management Tool" to check TOP processes, see if there are abnormal services + - **Think-3**: Found logging service abnormal, possibly "log files too large" or "configuration error", need to further check logging service configuration and log file size + - **Act-3**: Execute bash commands, found log files too large, also rotation not enabled in configuration, no maximum log size set + - **Think-4**: Provide feasible solutions to operations engineer: clean logs, modify configuration to enable rotation, restart logging service and application + +`ChatModelAgent` leverages the powerful capabilities of LLM for reasoning, understanding natural language, making decisions, generating responses, and tool interaction, **acting as the "thinking" part of agent applications**. You can quickly build a `ChatModelAgent` with `ReAct` capability using ADK: ```go import github.com/cloudwego/eino/adk @@ -77,93 +94,76 @@ chatAgent := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ }) ``` -ReAct emphasizes the closed loop of "Think → Act → Observe → Think again", avoiding blind actions and separating reasoning from execution. Example scenarios: +## 🎭 WorkflowAgents: Precision Pipelines -- 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 +Eino ADK provides WorkflowAgents patterns dedicated to coordinating sub-Agent execution flows, used to manage Agent running modes through predefined logic, producing deterministic execution processes, helping achieve **predictable and controllable multi-Agent collaboration methods**. You can arrange and combine the following patterns as needed, combined with `ChatModelAgent` to construct complete workflow pipelines that meet your needs: -## 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: +- **Sequential Agent**: Executes Agents registered in the configuration in order once and then ends, following these principles: + - **Linear execution**: Strictly follows the order of the SubAgents array. + - **Output passing**: Each Agent in the configuration can obtain the complete input of Sequential Agent and outputs of preceding Agents. + - **Supports early exit**: If any sub-Agent produces an exit/interrupt action, the entire Sequential flow terminates immediately. +- Possible practical scenarios include: + - **Data ETL**: `ExtractAgent` (extract order data from MySQL) → `TransformAgent` (clean null values, format dates) → `LoadAgent` (load to data warehouse) + - **CI/CD Pipeline**: `CodeCloneAgent` (pull code from repository) → `UnitTestAgent` (run unit tests, return error and analysis report on test case failure) → `CompileAgent` (compile code) → `DeployAgent` (deploy to target environment) ```go import github.com/cloudwego/eino/adk -// Execute: plan → search → write +// Execute in sequence: plan research -> search materials -> write report sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{ Name: "research_pipeline", SubAgents: []adk.Agent{ - planAgent, // plan research - searchAgent, // search information - writeAgent, // write report + planAgent, // Plan research + searchAgent, // Search materials + 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` +- **Parallel Agent**: Executes Agents registered in the configuration concurrently, ends after all Agents complete, following these principles: + - **Concurrent execution**: All sub-Agents start simultaneously, running in parallel in independent goroutines. + - **Shared input**: All sub-Agents receive the same initial input when calling Parallel Agent. + - **Wait and aggregate results**: Internally uses sync.WaitGroup to wait for all sub-Agents to complete, collects all sub-Agent execution results and outputs to `AsyncIterator` in order received. +- Possible practical scenarios include: + - **Multi-source data collection**: `MySQLCollector` (collect user table) + `PostgreSQLCollector` (collect order table) + `MongoDBCollector` (collect product reviews) + - **Multi-channel push**: `WeChatPushAgent` (push to WeChat official account) + `SMSPushAgent` (send SMS) + `AppPushAgent` (push to APP) ```go import github.com/cloudwego/eino/adk -// Concurrent: sentiment + keywords + summary +// Concurrent execution: sentiment analysis + keyword extraction + content summary parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{ Name: "multi_analysis", SubAgents: []adk.Agent{ - sentimentAgent, // sentiment analysis - keywordAgent, // keyword extraction - summaryAgent, // summarization + sentimentAgent, // Sentiment analysis + keywordAgent, // Keyword extraction + summaryAgent, // Content summary }, }) ``` - + -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 +- **Loop Agent**: Executes Agents registered in the configuration in order and loops multiple times, following these principles: + - **Loop execution**: Repeatedly executes the SubAgents sequence, each loop is a complete Sequential execution process. + - **Result accumulation**: Results from each iteration accumulate, later iteration inputs can access all historical information. + - **Conditional exit**: Supports terminating the loop by outputting an event containing `ExitAction` or reaching max iterations, configuring `MaxIterations=0` means infinite loop. +- Possible practical scenarios include: + - **Data synchronization**: `CheckUpdateAgent` (check source database increments) → `IncrementalSyncAgent` (sync incremental data) → `VerifySyncAgent` (verify consistency) + - **Stress testing**: `StartClientAgent` (start test client) → `SendRequestsAgent` (send requests) → `CollectMetricsAgent` (collect performance metrics) ```go import github.com/cloudwego/eino/adk -// Loop 5 times: analyze → improve → validate +// Loop 5 times, each sequence: analyze current state -> propose improvement plan -> verify improvement effect loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{ Name: "iterative_optimization", SubAgents: []adk.Agent{ - analyzeAgent, // analyze current state - improveAgent, // propose improvements - validateAgent, // validate improvements + analyzeAgent, // Analyze current state + improveAgent, // Propose improvement plan + validateAgent, // Verify improvement effect }, MaxIterations: 5, }) @@ -171,21 +171,35 @@ loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{ -## Prebuilt Multi-Agent Patterns +## 🛠️ Pre-built Multi-Agent Patterns + +Eino ADK, based on best engineering practices accumulated from daily Multi-Agent collaboration, provides users with **two pre-built Multi-Agent patterns**, ready to use out of the box without designing collaboration logic from scratch, covering two core scenarios: "centralized coordination" and "structured problem solving", efficiently supporting intelligent collaboration for complex tasks. -Two production-grade multi-agent paradigms: +#### 🎯 Supervisor Pattern: Centralized Coordination -### Supervisor — Centralized Coordination +Supervisor Agent is a centralized Multi-Agent collaboration pattern provided by ADK, designed to provide solutions for general scenarios of centralized decision-making and distributed execution. It consists of one Supervisor Agent and multiple SubAgents, where: -One supervisor orchestrates multiple sub-agents; subs return results deterministically. +- Supervisor Agent is responsible for task allocation, result aggregation after sub-Agent completion, and next-step decisions. +- Sub-Agents focus on executing specific tasks and automatically return task control to the Supervisor upon completion. +The Supervisor pattern has the following characteristics: + +- **Centralized control**: Supervisor uniformly manages sub-Agents, can dynamically adjust task allocation based on input and sub-Agent execution results. +- **Deterministic callback**: Sub-Agents return running results to the Supervisor Agent after completion, avoiding collaboration flow interruption. +- **Loose coupling extension**: Sub-Agents can be independently developed, tested, and replaced, convenient for expansion and maintenance. + +The hierarchical structure of the Supervisor pattern is well-suited for scenarios of **dynamically coordinating multiple specialized Agents to complete complex tasks**, such as: + +- **Research project management**: Supervisor assigns research, experimentation, and report writing tasks to different sub-Agents. +- **Customer service flow**: Supervisor routes to technical support, after-sales, or sales sub-Agents based on user issue type. + ```go import github.com/cloudwego/eino/adk/prebuilt/supervisor -// Research project management: create a supervisor-pattern multi-agent -// Sub-agents: research, experimentation, report +// Research project management: create a supervisor pattern multi-agent +// Contains research, experimentation, report sub-Agents supervisor, err := supervisor.New(ctx, &supervisor.Config{ SupervisorAgent: supervisorAgent, SubAgents: []adk.Agent{ @@ -196,79 +210,77 @@ supervisor, err := supervisor.New(ctx, &supervisor.Config{ }) ``` -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: +#### 🎯 Plan-Execute Pattern: Structured Problem Solving -- 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 Agent is an ADK-provided Multi-Agent collaboration pattern based on the "plan-execute-reflect" paradigm (referencing the paper **Plan-and-Solve Prompting**), designed to solve problems of step-by-step decomposition, execution, and dynamic adjustment for complex tasks. Through collaborative work of three core agents - Planner, Executor, and Replanner - it achieves structured planning, tool call execution, progress evaluation, and dynamic replanning, ultimately achieving user goals, where: -### Plan-Execute — Structured Problem Solving - -Planner creates a plan; Executor executes the current step; Replanner evaluates and adjusts. +- **Planner**: Based on user goals, generates an initial task plan with detailed, structured steps +- **Executor**: Executes the first step in the current plan +- **Replanner**: Evaluates execution progress, decides whether to correct the plan and continue with Executor, or end the task - - -Plan-Execute characteristics: +The Plan-Execute pattern has the following 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 +- **Clear layered architecture**: By decomposing tasks into planning, execution, and reflection/replanning phases, forming a clearly layered cognitive process, reflecting the closed-loop cognitive strategy of "think first, then act, then adjust based on feedback", achieving good results in various scenarios. +- **Dynamic iterative optimization**: Replanner judges in real-time whether the task is complete or needs plan adjustment based on execution results and current progress, supporting dynamic replanning. This mechanism effectively solves the bottleneck of traditional single planning being unable to cope with environmental changes and task uncertainty, improving system robustness and flexibility. +- **Clear responsibilities and loose coupling**: The Plan-Execute pattern works through collaboration of multiple agents, supporting independent development, testing, and replacement. Modular design facilitates extension and maintenance, following engineering best practices. +- **Good extensibility**: Not dependent on specific language models, tools, or Agents, convenient for integrating diverse external resources, meeting different application scenario requirements. -Representative scenarios: +The "Plan → Execute → Replan" closed-loop structure of the Plan-Execute pattern is well-suited for **complex task scenarios requiring multi-step reasoning, dynamic adjustment, and tool integration**, such as: -- 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 +- **Complex research analysis**: Decompose research problems through planning, execute multiple rounds of data retrieval and calculation, dynamically adjust research direction and hypotheses, improving analysis depth and accuracy. +- **Automated workflow management**: Decompose complex business processes into structured steps, combine multiple tools (such as database queries, API calls, computation engines) for step-by-step execution, and dynamically optimize processes based on execution results. +- **Multi-step problem solving**: Suitable for scenarios requiring step-by-step reasoning and multi-tool collaboration, such as legal consulting, technical diagnosis, strategy formulation, ensuring feedback and adjustment at each execution step. +- **Intelligent assistant task execution**: Support intelligent assistants to plan task steps based on user goals, call external tools to complete specific operations, and adjust subsequent plans based on replanning thinking combined with user feedback, improving task completion integrity and accuracy. ```go import github.com/cloudwego/eino/adk/prebuilt/planexecute +// Plan-Execute pattern research assistant 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", + Instruction: "Formulate detailed research plan, including literature research, data collection, analysis methods, etc.", Model: gpt4Model, }), Executor: adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ Name: "research_executor", - ToolsConfig: adk.ToolsConfig{ Tools: []tool.BaseTool{ scholarSearchTool, dataAnalysisTool, citationTool } }, + ToolsConfig: adk.ToolsConfig{ + Tools: []tool.BaseTool{ + scholarSearchTool, + dataAnalysisTool, + citationTool, + }, + }, }), Replanner: replannerAgent, }) ``` -### DeepAgents — Planning-Driven Centralized Collaboration +#### 🎯 DeepAgents Pattern: Planning-Driven Centralized Collaboration -DeepAgents centralizes coordination under a Main Agent that runs a ReAct loop with tool calls: +DeepAgents is a Multi-Agent pattern under unified coordination of a Main Agent. The Main Agent runs in a ReAct flow with tool-calling capable ChatModel: -- 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 +- Break down user goals into structured todos and track progress through WriteTodos +- Select and call corresponding SubAgents to execute subtasks through unified entry TaskTool; main/sub agent contexts are isolated to avoid intermediate steps polluting the main flow. +- Aggregate results returned by sub-agents; call WriteTodos again if necessary to update progress or replan until completion. -Characteristics: +The DeepAgents pattern has the following 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 +- **Strengthened task decomposition and progress management**: Form clear subtasks and milestones through WriteTodos, making complex goals decomposable and trackable. +- **More robust context isolation**: Sub-agents execute in "clean" context, main agent only aggregates results, reducing interference from redundant thought chains and tool call traces on the main flow. +- **Unified delegation entry, easy to extend**: TaskTool abstracts all sub-agent and tool capabilities into a unified calling surface, convenient for adding or replacing specialized sub-agents. +- **Flexible closed-loop between planning and execution**: Planning can be called as a tool on demand; unnecessary planning can be skipped for simple tasks, reducing LLM call costs and time. +- **Boundaries and tradeoffs**: Over-decomposition increases call counts and costs; higher requirements for subtask division and prompt optimization, model needs stable tool-calling and planning capabilities. -Representative scenarios: +The core value of DeepAgent lies in automating workflows requiring multi-step, multi-role collaboration. It's not just a single-function executor, but a "project manager" with deep thinking, planning, and dynamic adjustment capabilities, suitable scenarios include: -- 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 +- **Multi-role collaboration complex business flows**: Centered on R&D, testing, release, legal, operations multi-role collaboration, centrally delegate subtasks and uniformly aggregate; set checkpoints and rollback strategies at each stage, progress is visible and retryable. +- **Staged management of long processes**: Planning decomposes cleaning, validation, lineage analysis, quality inspection steps, sub-agents run in isolated contexts; only rerun relevant stages on exceptions, products are uniformly reconciled and aggregated. +- **Execution environments requiring strict context isolation**: Unified entry collects materials and requests, TaskTool routes legal, risk control, finance subtasks separately; clear boundaries between subtasks that are invisible to each other, progress and traces are auditable, failures can be retried without affecting other stages. ```go import github.com/cloudwego/eino/adk/prebuilt/deep @@ -276,16 +288,20 @@ 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 }, + SubAgents: []adk.Agent{ + LegalAgent, + RiskControlAgent, + FinanceAgent, + }, MaxIteration: 100, }) ``` # Foundation Design -## Unified Agent Abstraction +## 🎯 Unified Agent Abstraction -All agents share a minimal interface, with standardized inputs and event-driven outputs: +The core of ADK is a simple yet powerful `Agent` interface: ```go type Agent interface { @@ -295,156 +311,244 @@ type Agent interface { } ``` -This standardization enables composition, observability, and control across complex systems. +Each Agent has a clear identity (Name), clear responsibilities (Description), and standardized execution method (Run), providing the foundation for discovery and invocation between Agents. Whether it's a simple Q&A bot or a complex multi-step task processing system, it can be implemented through this unified interface. -## Asynchronous Event-Driven Architecture +## ⚡ Asynchronous Event-Driven Architecture -ADK uses an asynchronous event stream via `AsyncIterator[*AgentEvent]`, and runs agents with a `Runner`: +ADK adopts an asynchronous event stream design, implementing non-blocking event processing through `AsyncIterator[*AgentEvent]`, and running Agents through the `Runner` framework: -- 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 +- **Real-time response**: `AgentEvent` contains specific node outputs during Agent execution (Agent replies, tool processing results, etc.), users can immediately see the Agent's thinking process and intermediate results. +- **Execution tracking**: `AgentEvent` additionally carries state modification actions and running trajectory, convenient for development debugging and understanding Agent behavior. +- **Automatic flow control**: The framework automatically handles interrupt, jump, and exit behaviors through `Runner`, requiring no additional user intervention. -## Flexible Collaboration Mechanisms +## 🤝 Flexible Collaboration Mechanisms -Agents in the same system can collaborate by sharing state or triggering runs: +Eino ADK supports Agents within the same system to collaborate in multiple ways (exchange data or trigger runs): -- Shared Session: a KV store alive during a run for cross-agent state and data sharing +- **Shared Session**: KV storage that persists during a single run, used to support cross-Agent state management and data sharing. ```go +// Get all SessionValues func GetSessionValues(ctx context.Context) map[string]any + +// Get a value from SessionValues by key, second return value is false if key doesn't exist, otherwise true func GetSessionValue(ctx context.Context, key string) (any, bool) + +// Add SessionValues func AddSessionValue(ctx context.Context, key string, value any) + +// Batch add SessionValues 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 +- **Transfer**: Hand off task to sub-Agent for continued processing, carrying this Agent's output result context. Suitable for scenarios where agent functions can clearly divide boundaries and hierarchies, commonly combined with ChatModelAgent, using LLM generation results for dynamic routing. Structurally, two Agents collaborating in this way are called parent and child Agents: ```go +// Set parent-child Agent relationship func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error) + +// Specify target Agent name, construct Transfer Event 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. +- **Explicit invocation (ToolCall)**: Call Agent as a tool. Suitable for scenarios where Agent running only needs clear parameters rather than complete running context, commonly combined with ChatModelAgent, running as a tool and returning results to ChatModel for continued processing. Additionally, ToolCall also supports calling ordinary tools constructed according to tool interface that don't contain Agents. ```go +// Convert Agent to Tool func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool ``` -## Interrupt and Resume +## 🔄 **Interrupt and Resume Mechanism** -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. +Eino ADK provides runtime interrupt and resume functionality, allowing a running Agent to actively interrupt and save its current state, and resume execution from the interruption point in the future. This feature assists development in scenarios of long waiting, pausable, or requiring external input (Human in the loop). -- 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 +- During Agent internal running, proactively notify `Runner` to interrupt running by throwing `Event` containing `Interrupt Action`, and allow carrying additional information for caller to read and use. +- `Runner` records current running state through `CheckPointStore` registered at initialization +- When ready to run again, restart the Agent running from the breakpoint through the `Resume` method carrying new information needed for resume ```go -runner := adk.NewRunner(ctx, adk.RunnerConfig{ Agent: complexAgent, CheckPointStore: memoryStore }) +// 1. Create Runner supporting breakpoint resume +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: complexAgent, + CheckPointStore: memoryStore, // Memory state storage +}) + +// 2. Start execution 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)) + event, ok := iter.Next() + if !ok { + break + } + if event.Err != nil { + log.Fatal(event.Err) + } + if event.Action != nil { + // 3. Interrupt event thrown internally by Agent + if event.Action.Interrupted != nil { + ii, _ := json.MarshalIndent(event.Action.Interrupted.Data, "", "\t") + fmt.Printf("action: interrupted\n") + fmt.Printf("interrupt snapshot: %v", string(ii)) + } } } +// 4. Receive user input from stdin scanner := bufio.NewScanner(os.Stdin) fmt.Print("\nyour input here: ") scanner.Scan() +fmt.Println() nInput := scanner.Text() +// 5. Resume execution from breakpoint carrying user input information iter, err := runner.Resume(ctx, "1", adk.WithToolOptions([]tool.Option{subagents.WithNewInput(nInput)})) ``` -# Quickstart +# Quick Start -## Install +## Installation ```go go get github.com/cloudwego/eino@latest ``` -## Project Manager Agent +## Project Development Manager Agent -A supervisor-pattern agent coordinating research, coding, and review: +The following example uses Eino ADK to build a project development manager agent, targeting multi-faceted management collaboration scenarios: -- 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 +- Project Manager Agent: Project manager agent, using Supervisor pattern overall, with the following Agent functions: + - `ResearchAgent`: Research Agent, responsible for research and generating feasible solutions, supports receiving additional context information from users after interruption to improve research solution generation accuracy. + - `CodeAgent`: Coding Agent, uses knowledge base tools to recall relevant knowledge as reference, generating high-quality code. + - `ReviewAgent`: Review Agent, uses sequential workflow orchestration of problem analysis, evaluation generation, and evaluation validation steps to review research results/coding results, giving reasonable evaluations for project manager decision-making. + - `ProjectManagerAgent`: Project manager Agent, routes and coordinates multiple sub-agents responsible for different dimensions of work based on dynamic user input. +- Possible work scenarios for this Agent: + - **Implementing a project from scratch**: Project manager starts from requirements, works through research, coding, and review Agents, finally completing project delivery. + - **Improving existing projects**: Project manager gets points needing improvement from review Agent, hands to coding Agent for implementation, then hands to review Agent for reviewing modified code. + - **Conducting technical research**: Project manager requests research Agent to generate technical research report, then review Agent gives review opinions. Caller decides subsequent actions combining returned technical research report and review opinions. -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: +The design of this example covers most concepts introduced in the article, you can review the various design concepts mentioned earlier based on the example. Additionally, consider how to complete writing this example in ordinary development mode, ADK's advantages become immediately apparent: - - - - - - + + + + + +
    DesignTraditionalWith Eino ADK
    Agent abstractionNo unified definition; hard collaboration; high maintenanceUnified; clear responsibilities; clean code; parallel agent development
    Inputs/outputsUnstandardized; rely on ad-hoc logsEvent-driven; iterator exposes run; WYSIWYG
    Agent collaborationManual context passingFramework-managed context
    Interrupt/resumeImplement from scratch (serialize/restore/state)Register `CheckPointStore` in Runner
    Agent patternsImplement from scratchPrebuilt, production-ready patterns
    Design PointTraditional Development ModeDevelopment Based on Eino ADK
    Agent abstractionNo unified definition, poor team collaboration efficiency, high later maintenance costUnified definition, independent responsibilities, clean code, convenient for separate Agent development
    Input/OutputNo unified definition, chaotic I/O, running process can only be manually logged, not conducive to debuggingUnified definition, all event-driven, running process exposed through iterator, what you see is what you get
    Agent collaborationManually pass context through codeFramework automatically passes context
    Interrupt/Resume capabilityNeed to implement from scratch, solving serialization/deserialization, state storage/recovery issuesOnly need to register CheckPointStore in Runner to provide breakpoint data storage medium
    Agent patternsNeed to implement from scratchMultiple mature patterns available out of the box
    +Core code as follows, complete code see [source code](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-project-manager) provided in Eino-Examples project: + ```go func main() { ctx := context.Background() + // Init chat model for agents 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" }(), + ByAzure: func() bool { + return os.Getenv("OPENAI_BY_AZURE") == "true" + }(), }) - if err != nil { log.Fatal(err) } + 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) } + // Init research agent + researchAgent, err := agents.NewResearchAgent(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) } + // Init code agent + codeAgent, err := agents.NewCodeAgent(ctx, tcm) + if err != nil { + log.Fatal(err) + } - runner := adk.NewRunner(ctx, adk.RunnerConfig{ Agent: supervisorAgent, EnableStreaming: true, CheckPointStore: newInMemoryStore() }) + // Init technical agent + reviewAgent, err := agents.NewReviewAgent(ctx, tcm) + if err != nil { + log.Fatal(err) + } + + // Init project manager agent + s, err := agents.NewProjectManagerAgent(ctx, tcm) + if err != nil { + log.Fatal(err) + } + + // Combine agents into ADK supervisor pattern + // Supervisor: project manager + // Sub-agents: researcher / coder / reviewer + supervisorAgent, err := supervisor.New(ctx, &supervisor.Config{ + Supervisor: s, + SubAgents: []adk.Agent{researchAgent, codeAgent, reviewAgent}, + }) + if err != nil { + log.Fatal(err) + } + + // Init Agent runner + runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: supervisorAgent, + EnableStreaming: true, // enable stream output + CheckPointStore: newInMemoryStore(), // enable checkpoint for interrupt & resume + }) + // Replace it with your own query query := "please generate a simple ai chat project with python." checkpointID := "1" + // Start runner with a new checkpoint id 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 } + 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 } + if !interrupted { + return + } + + // interrupt and ask for additional user context scanner := bufio.NewScanner(os.Stdin) fmt.Print("\ninput additional context for web search: ") scanner.Scan() + fmt.Println() nInput := scanner.Text() + // Resume by checkpoint id, with additional user context injection iter, err = runner.Resume(ctx, checkpointID, adk.WithToolOptions([]tool.Option{agents.WithNewInput(nInput)})) - if err != nil { log.Fatal(err) } + if err != nil { + log.Fatal(err) + } for { - event, ok := iter.Next(); if !ok { break } - if event.Err != nil { log.Fatal(event.Err) } + event, ok := iter.Next() + if !ok { + break + } + if event.Err != nil { + log.Fatal(event.Err) + } prints.Event(event) } } @@ -452,16 +556,16 @@ func main() { # 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. +Eino ADK is not just a development framework, but a complete agent development ecosystem. Through unified abstractions, flexible composition, and powerful collaboration mechanisms, it enables Go developers to easily build various AI applications 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 +> - 📚 View more documentation: [Eino ADK Documentation](https://www.cloudwego.io/docs/eino/core_modules/eino_adk/) +> - 🛠️ Browse ADK source code: [Eino ADK Source](https://github.com/cloudwego/eino/tree/main/adk) +> - 💡 Explore all examples: [Eino ADK Examples](https://github.com/cloudwego/eino-examples/tree/main/adk) +> - 🤝 Join the developer community: Exchange experiences and best practices with other developers > -> Eino ADK makes agent development simple and powerful! +> Eino ADK makes agent development simple yet 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 index 1a03d0444f3..9b8fd9a49b8 100644 --- a/content/en/docs/eino/overview/eino_adk_excel_agent.md +++ b/content/en/docs/eino/overview/eino_adk_excel_agent.md @@ -3,87 +3,93 @@ Description: "" date: "2025-12-02" lastmod: "" tags: [] -title: 'Build Your First AI Agent with Eino ADK: Excel Agent End-to-End' -weight: 4 +title: 'Build Your First AI Agent with Eino ADK: Starting from Excel Agent in Practice' +weight: 7 --- -## From Excel Agent to Eino ADK +## Understanding Eino ADK Through the Excel Agent -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. +This article will introduce you to how to build a powerful multi-agent system using **Eino ADK** (**Agent Development Kit**). Previous Eino ADK introduction link: [Eino ADK: Master AI Agent Core Design Patterns, Build Agent Systems from Scratch](https://mp.weixin.qq.com/s/ffGjlDEzEzroo8w6knlLqw) -Earlier Eino ADK introduction: https://www.cloudwego.io/docs/eino/core_modules/eino_adk/ +The example is based on the Excel Agent, a real business scenario. The Excel Agent is an intelligent assistant that can "understand your words, read your spreadsheets, and write and execute code". It breaks down complex Excel processing work into clear steps, completing various Excel data processing tasks reliably through automatic planning, tool calling, and result verification. -Full source: [eino-examples/adk/multiagent/integration-excel-agent](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent) +Next, we will demonstrate how this Agent is built step by step using Eino ADK, starting from the complete architecture and functionality of the Excel Agent. This will help you understand the core design features of Eino ADK in a simple and accessible way, helping you get started with Eino ADK quickly and move toward building custom agents and AI application systems. + +The complete code for this example is located at [GitHub](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent), and you can browse and download it at any time. ### 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. +The Excel Agent is an "intelligent assistant that understands Excel". It first breaks down problems into steps, then executes and verifies results step by step. It can understand user questions and uploaded file content, propose feasible solutions, and select appropriate tools (system commands, generate and run Python code, web queries, etc.) to complete tasks. -Architecture: +The Excel Agent is a Multi-Agent system built on Eino ADK. The complete architecture is shown in the figure below: -Agents: +The Excel Agent contains several Agents with the following functions: + +- **Planner**: Analyzes user input and breaks down user problems into executable plans +- **Executor**: Correctly executes the first step in the current plan +- **CodeAgent**: Receives instructions from the Executor, calls various tools (such as reading/writing files, running Python code, etc.) to complete tasks +- **WebSearchAgent**: Receives instructions from the Executor and performs web searches +- **Replanner**: Based on the Executor's execution results and existing plans, decides whether to continue execution, adjust the plan, or complete execution +- **ReportAgent**: Generates summary reports based on the running process and results -- 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 +### Typical Use Cases for the Excel Agent -Use cases: +In real business scenarios, you can treat the Excel Agent as an "Excel expert + automation engineer". When you provide a raw spreadsheet and target description, it will propose a solution and execute it: -- Data cleaning and formatting -- Analysis and report generation -- Budget automation -- Data matching and merging +- **Data Cleaning and Formatting**: Complete deduplication, null value handling, and date format standardization from an Excel file containing large amounts of data. +- **Data Analysis and Report Generation**: Extract monthly sales totals from sales data, perform aggregation statistics and pivot analysis, and finally generate and export chart reports. +- **Automated Budget Calculation**: Automatically calculate the total budget based on different departments' budget requests and generate department budget allocation tables. +- **Data Matching and Merging**: Match and merge customer information tables from multiple different sources to generate a complete customer information database. -End-to-end flow: +The complete execution flow of the Excel Agent is: > 💡 > **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. +> - **Less manual work**, letting the Agent automatically complete complex and tedious Excel processing tasks. +> - **More stable output quality**, reducing omissions and errors through the "plan-execute-reflect" closed loop. +> - **Stronger scalability**, with each Agent built independently, low coupling facilitates iterative updates. -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. +The Excel Agent can be used independently, or as a sub-Agent integrated into a composite multi-expert system, routed to by external systems to solve Excel domain-related problems. -### ChatModelAgent — ReAct at the Core +Below, we will gradually break down the Excel Agent to gain an in-depth understanding of the core design features of Eino ADK and how to use these features to build efficient, flexible AI application systems. -`ChatModelAgent` uses the [ReAct](https://react-lm.github.io/) pattern (think–act–observe): +### ChatModelAgent: The Foundation for Interacting with LLMs - +`ChatModelAgent` is a core pre-built Agent in Eino ADK that internally uses the [ReAct](https://react-lm.github.io/) pattern (a chain-of-reasoning pattern that lets the model "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: +`ChatModelAgent` is designed to let ChatModel perform explicit, step-by-step "thinking", combining the thinking process to drive actions, observing historical thinking processes and action results to continue with the next step of thinking and action, ultimately solving complex problems: -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 +- Call ChatModel (Reason) +- LLM returns tool call requests (Action) +- ChatModelAgent executes tools (Act) +- Return tool results to LLM (Observation), combined with previous context to continue generation until the model determines no more tool calls are needed -### Plan-Execute — Structured Collaboration +In the Excel Agent, the core of each Agent is such a `ChatModelAgent`. Taking the Executor running the step [Read the header information of the user's input spreadsheet] as an example, we can understand how the ReAct pattern manifests in `ChatModelAgent` by observing the complete running process: + +1. Executor: After judgment, transfers the task to CodeAgent for execution +2. CodeAgent: Receives the task [Read the header information of the user's input spreadsheet] + 1. **Think-1**: The context doesn't provide all files in the working directory, need to check + 2. **Act-1**: Call the Bash tool, ls to view all files in the working directory + 3. **Think-2**: Found the user's input file, determines need to write Python code to read the xlsx spreadsheet's first row + 4. **Act-2**: Call the PythonRunner tool, write and run code, get the execution result + 5. **Think-3**: Got the xlsx first row, determines task is complete +3. Execution complete, return the spreadsheet header information to Executor -Planner creates plans; Executor executes steps via tools; Replanner evaluates progress and replans. +### Plan-Execute Agent: A Multi-Agent Collaboration Framework Based on "Plan-Execute-Reflect" -Plans are structured lists of steps. Eino ADK provides `Plan` interfaces and default JSON serializable plan structures. +Plan-Execute Agent is a multi-agent collaboration framework in Eino ADK based on the "plan-execute-reflect" paradigm, designed to solve the problems of step-by-step decomposition, execution, and dynamic adjustment of complex tasks. Through the collaborative work of **Planner**, **Executor**, and **Replanner** - three core agents, it achieves structured task planning, tool call execution, progress evaluation, and dynamic replanning, ultimately achieving user goals: ```go -// Full code: https://github.com/cloudwego/eino/blob/main/adk/prebuilt/planexecute/plan_execute.go +// Complete 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) @@ -100,13 +106,13 @@ func New(ctx context.Context, cfg *Config) (adk.Agent, error) -For Excel Agent, the trio matches the goal of solving Excel-domain problems: +The Excel Agent's core capability is precisely [solving user problems in the Excel domain], which aligns with this agent collaboration framework: -- 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**: Clarifies goals, automatically decomposes into executable steps +- **Executor**: Calls tools (Excel reading, system commands, Python code) to complete each detailed step in the plan +- **Replanner**: Decides to continue, adjust the plan, or end based on execution 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: +Planner and Replanner break down vague user instructions into clear, executable step lists - plans (Plan) containing multiple steps (Step). Eino ADK provides a flexible Plan interface definition for this, supporting users to customize Plan structure and details: ```go type Plan interface { @@ -121,7 +127,7 @@ type Plan interface { } ``` -By default, the framework provides a builtin plan structure. Example plan produced by Excel Agent: +By default, the framework uses the built-in Plan structure as a fallback configuration. For example, here is a complete execution plan generated by the Excel Agent: ```sql ### Task Plan @@ -133,333 +139,403 @@ By default, the framework provides a builtin plan structure. Example plan produc - [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 +### Workflow Agents: Controllable Multi-Agent Execution Pipeline -Excel Agent needs: +In the Excel Agent, there are situations where agents need to run in a specific order: -1) Sequential: Planner → Executor/Replanner (Planner runs once) -2) Loop: Executor + Replanner repeated -3) Sequential: ReportAgent at the end +1. **Sequential execution**: Run Planner first, then Executor and Replanner; Planner runs only once. +2. **Loop execution**: Executor and Replanner need to run multiple times in a loop as needed, each loop runs Executor first then Replanner +3. **Sequential execution**: After Plan-Executor completes overall execution, run ReportAgent once for summary. -Use Eino ADK’s `SequentialAgent`, `LoopAgent`, and `ParallelAgent` to build controlled pipelines. +For these scenarios with fixed execution flows, Eino ADK provides three workflow orchestration methods to help users quickly build controllable workflows: - +- **SequentialAgent**: Executes a series of sub-Agents sequentially according to the order provided in the configuration. After each sub-Agent completes execution, its output is passed to the next sub-Agent through the History mechanism, forming a linear execution chain. -```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 + + // Execute in sequence: Create research plan -> Search materials -> Write report + sequential := adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{ + Name: "research_pipeline", + SubAgents: []adk.Agent{ + planAgent, // Create research plan + 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, -}) -``` +- **LoopAgent**: Repeatedly executes the configured sub-Agent sequence until the maximum number of iterations is reached or a sub-Agent produces an ExitAction. The results of each iteration accumulate, and subsequent iterations can access all historical information. LoopAgent is implemented based on SequentialAgent. + + ```go + import github.com/cloudwego/eino/adk + + // Loop execution 5 times, each sequence: Analyze current state -> Propose improvement plan -> Verify improvement results + loop := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{ + Name: "iterative_optimization", + SubAgents: []adk.Agent{ + analyzeAgent, // Analyze current state + improveAgent, // Propose improvement plan + validateAgent, // Verify improvement results + }, + 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 - }, -}) -``` +- **ParallelAgent**: Allows multiple sub-Agents to execute concurrently based on the same input context. All sub-Agents receive the same initial input, each running in an independent goroutine (a lightweight concurrent execution unit in Go), finally collecting all sub-Agent execution results and outputting them sequentially to `AsyncIterator`. + + ```go + import github.com/cloudwego/eino/adk + + // Concurrent execution: Sentiment analysis + Keyword extraction + Content summary + parallel := adk.NewParallelAgent(ctx, &adk.ParallelAgentConfig{ + Name: "multi_analysis", + SubAgents: []adk.Agent{ + sentimentAgent, // Sentiment analysis + keywordAgent, // Keyword extraction + summaryAgent, // Content summary + }, + }) + ``` -### Agent Abstraction — Unified and Extensible +### Agent Abstraction: The Foundation for Flexibly Defining Agents -All agents implement a minimal interface with standardized inputs and event-driven outputs. Prebuilt agents follow this contract; you can create custom ones easily. +The core of Eino ADK is a simple yet powerful Agent interface. Each Agent has a clear identity (Name), clear responsibilities (Description), and standardized execution methods (Run), providing the foundation for Agent discovery and invocation. Whether it's a simple Q&A bot or a complex multi-step task processing system, they can all be implemented through this unified interface. -- Unified Agent abstraction — prebuilt agents (ChatModelAgent, Plan-Execute, Workflow Agents) follow this interface; you can implement custom agents too: +- **Unified Agent Abstraction**: All pre-built Agents provided by ADK (ChatModelAgent, Plan-Execute Agent, Workflow Agents) follow this interface definition. You can also write custom Agents based on this interface to meet customized requirements. -```go -type Agent interface { - Name(ctx context.Context) string - Description(ctx context.Context) string - Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent] -} -``` + ```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**: Agents typically have LLM as their core, so the Agent input defined by Eino ADK is consistent with LLM input: -- Standardized input — aligned with LLM I/O: + ```go + type AgentInput struct { + Messages []Message + EnableStreaming bool + } -```go -type AgentInput struct { - Messages []Message - EnableStreaming bool -} + type Message = *schema.Message // *schema.Message is the structure definition for model input and output + ``` +- **Asynchronous Event-Driven Output**: The Agent's output is an asynchronous iterator of AgentEvent, where AgentEvent represents the core event data generated by the Agent during its execution. It contains the Agent's metadata, output, behavior, and error information: -type Message = *schema.Message -``` + ```go + type AgentEvent struct { + AgentName string // Name of the Agent that produced the Event (automatically filled by the framework) -- Asynchronous event-driven output — an iterator of `AgentEvent` containing metadata, outputs, actions, and errors: + RunPath []RunStep // Complete execution trace to reach the current Agent (automatically filled by the framework) -```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 -} + Output *AgentOutput // Agent output message content -type AgentOutput struct { - MessageOutput *MessageVariant // Model message output - CustomizedOutput any // Customized output content -} + Action *AgentAction // Agent action event 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 -} + Err error // Agent error + } -type AgentAction struct { - Exit bool // Agent exit - Interrupted *InterruptInfo // Agent interrupted - TransferToAgent *TransferToAgentAction // Agent transfer - CustomizedAction any // Customized Agent action -} -``` + type AgentOutput struct { + MessageOutput *MessageVariant // Model message output content + + CustomizedOutput any // Customized output content + } + + type MessageVariant struct { + IsStreaming bool // Whether it's 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: +The asynchronous iterator allows the Agent to send messages to the iterator at any time during execution (Agent's model call results, tool execution results, intermediate states, etc.), while the caller consumes this series of events in an ordered, blocking manner: ```go -iter := myAgent.Run(ctx, "hello") +iter := myAgent.Run(ctx, "hello") // get AsyncIterator + for { event, ok := iter.Next() - if !ok { break } + if !ok { + break + } // handle event } ``` -### Agent Collaboration — Data Passing Under the Hood +### Agent Collaboration: Data Passing Hidden Behind Agents + +The nodes in the Excel Agent architecture diagram represent each specific Agent, and the edges represent data flow and task transfer. When building multi-Agent systems, it's crucial to enable efficient and accurate information sharing between different Agents. -Nodes are agents; edges represent data flow and task handoff. ADK provides robust data sharing mechanisms: +This information includes not only the Agent's input and output, but also various additional information that is global and partially visible, such as: -- 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. +- Executor execution needs to get a structured plan (Plan) that can be broken down into detailed steps (Step) from Planner/Replanner, rather than an unstructured raw LLM output message. +- ReportAgent needs to get the complete execution plan, execution process, and execution artifacts to correctly generate reports. + +Eino ADK includes two basic data passing mechanisms: + +- **History**: Every AgentEvent produced by an Agent is saved to this hidden History. When calling a new Agent, the AgentEvents in History are converted and concatenated into AgentInput. By default, Assistant or Tool Messages from other Agents are converted to User Messages. This is equivalent to telling the current LLM: "Just now, Agent_A called some_tool and returned some_result. Now, it's your turn to make a decision." In this way, other Agents' behaviors are treated as "external information" or "factual statements" provided to the current Agent, rather than its own behavior, thus avoiding LLM context confusion. -- 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. +- **Shared Session**: Persistent KV storage during a single execution process, used to support cross-Agent state management and data sharing. Any Agent in a single execution can read and write SessionValues at any time. Taking the Plan-Execute Agent pattern as an example, Planner generates the initial plan and writes it to Session; Executor reads the plan from Session and executes it; Replanner reads the current plan from Session, combines it with execution results, and writes the updated plan back to Session to overwrite the current plan. -```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 -``` + ```go + // Get all SessionValues within an Agent + func GetSessionValues(ctx context.Context) map[string]any + + // Get value from SessionValues by specified key within an Agent + func GetSessionValue(ctx context.Context, key string) (any, bool) + + // Add SessionValues within an Agent + func AddSessionValue(ctx context.Context, key string, value any) + + // Batch add SessionValues within an Agent + func AddSessionValues(ctx context.Context, kvs map[string]any) + + // WithSessionValues injects SessionValues from outside before Agent execution + func WithSessionValues(v map[string]any) AgentRunOption + ``` -Collaboration modes: +In addition to comprehensive data passing mechanisms between Agents, Eino ADK provides multiple Agent collaboration patterns based on practical experience: -- 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. +- **Preset Agent Execution Order (Workflow)**: Execute according to the flow preset in code, the Agent execution order is predetermined and predictable. Corresponds to the three paradigms mentioned in the Workflow Agents section. +- **Transfer Execution**: Carry the current Agent's output result context and transfer the task to a sub-Agent for continued processing. Suitable for scenarios where agent functionality can be clearly divided into boundaries and hierarchies, often used in combination with ChatModelAgent for dynamic routing through LLM generation results. Structurally, two Agents collaborating in this way are called parent-child Agents: ```go +// Set parent-child Agent relationship func SetSubAgents(ctx context.Context, agent Agent, subAgents []Agent) (Agent, error) + +// Construct Transfer Event by specifying target Agent name 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. +- **Explicit Invocation (ToolCall)**: Treat the Agent as a tool for invocation, suitable for scenarios where Agent execution only requires clear and explicit parameters rather than complete execution context. Often combined with ChatModelAgent, running the Agent as a tool and returning results to ChatModel for continued processing. Additionally, ToolCall also supports calling regular tools that conform to the tool interface construction without containing an Agent. ```go +// Convert Agent to Tool func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool ``` -## Excel Agent — How to Run +## Excel Agent Example Execution + +### Configure Environment and Input/Output Paths -### Environment and Paths +- Environment Variables: The complete environment variables required for Excel Agent execution can be found in the project README. +- Execution Input: Includes a user requirement description and a series of files to be processed, where: -- 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): + - The first line in `main.go` represents the user's input requirement description, which you can modify: ```go func main() { - // query := schema.UserMessage("统计附件文件中推荐的小说名称及推荐次数,并将结果写到文件中。凡是带有《》内容都是小说名称,形成表格,表头为小说名称和推荐次数,同名小说只列一行,推荐次数相加") - // query := schema.UserMessage("读取模拟出题.csv 中的内容,规范格式将题目、答案、解析、选项放在同一行,简答题只把答案写入解析即可") - query := schema.UserMessage("请帮我将 question.csv 表格中的第一列提取到一个新的 csv 中") + // query := schema.UserMessage("Count the recommended novel names and recommendation counts in the attachment file, and write the results to a file. Any content with 《》 is a novel name, form a table with headers for novel name and recommendation count, list same-named novels in one row only, and add up recommendation counts") + // query := schema.UserMessage("Read the content in 模拟出题.csv, standardize the format to put question, answer, explanation, options in the same row, for short-answer questions just write the answer into the explanation") + query := schema.UserMessage("Please help me extract the first column from the question.csv spreadsheet into a new csv") } ``` + - `adk/multiagent/integration-excel-agent/playground/input` is the default attachment input path. The attachment input path can be configured, refer to README. + - The `adk/multiagent/integration-excel-agent/playground/test_data` path provides several example files. You can copy files to the attachment input path for test execution: - - 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`: - - ``` + ```go % tree adk/multiagent/integration-excel-agent/playground/test_data adk/multiagent/integration-excel-agent/playground/test_data ├── questions.csv ├── 推荐小说.txt └── 模拟出题.csv - + 1 directory, 3 files ``` +- Execution Output: Input attachments, intermediate artifacts, and final results from Excel Agent execution are all placed in the working path: `adk/multiagent/integration-excel-agent/playground/${uuid}`. The output path can be configured, refer to README. -- Outputs — attachments, intermediate artifacts, and final results live under `adk/multiagent/integration-excel-agent/playground/${uuid}` (configurable). +### View Execution Results -### Inspecting Results +A single Excel Agent execution creates a new working directory under the output path, completing tasks in that directory. Intermediate artifacts and final results generated during execution are written to that directory. -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: +Taking the task `Please help me extract the first column from question.csv spreadsheet into a new csv` as an example, the files in the working directory after execution 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 +1. Original input: `question.csv` obtained from the input path +2. Execution plan from Planner/Replanner: `plan.md` + + ```go + ### 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. Code written by CodeAgent in Executor: `$uuid.py` + + ```go + import pandas as pd + + df = pd.read_csv('questions.csv') + first_column = df.iloc[:, _0_] + first_column.to_csv('extracted_first_column.csv', index=_False_) + ``` +4. Intermediate artifacts: `extracted_first_column.csv` and `first_column.csv` + + ```go + 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." + } + ] + } + ``` + +### Execution Process Output + +The Excel Agent outputs the execution results of each step to the log. Below, still using the task `Please help me extract the first column from question.csv spreadsheet into a new csv` as an example, we will show you several key steps and their outputs during Excel Agent execution, and through explaining the steps, intuitively present the Agent's execution flow and powerful capabilities: + +- Planner generates initial plan in JSON format + + ```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 calls CodeAgent as a tool to execute the first step in the plan + + ```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 the PythonRunner tool to run code, and uses ReAct pattern for automatic error correction, fixing errors in the code -```yaml -name: Executor -tool name: PythonRunner -arguments: {"code":"```python\nfirst_column = df.iloc[:, 0]\n```"} + ```yaml + # CodeAgent uses PythonRunner tool to run code + 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 + # PythonRunner code execution error + 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 + # ReAct pattern automatic error correction, fixing non-executable code 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." } ] } -``` + # Code execution successful, return execution result + name: Executor + path: [{SequentialAgent} {plan_execute_replan} {Planner} {execute_replan} {Executor}] + tool response: + 0 multiple-choice + 1 multiple-choice + 2 multiple-choice + 3 multiple-choice + 4 multiple-choice + Name: type, dtype: object + ``` +- Replanner determines plan is complete, submits execution results 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"**: **"New CSV file containing the first column data from the original spreadsheet"**, + **"path"**: **"extracted_first_column.csv"** + } + ] + } + ``` +- ReportAgent generates summary and ends execution + + ```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: +What the Excel Agent demonstrates is not the techniques of a "single agent", but a set of Multi-Agent system engineering methodology based 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 +- Built on the ReAct capability of ChatModelAgent as the foundation, enabling the model to "think and call tools". +- With WorkflowAgents orchestration capabilities, allowing each Agent in the Multi-Agent system to run in the user-expected order. +- With the Planner–Executor–Replanner closed loop, enabling complex tasks to be "decomposable and error-correctable". +- With History/Session data passing mechanisms, enabling multi-Agent systems to "collaborate and replay". > 💡 -> **Start your agent development journey** +> **Start Your Agent Development Journey Now** +> +> - ⌨️ View Excel Agent source code: [GitHub Excel Agent Source Code](https://github.com/cloudwego/eino-examples/tree/main/adk/multiagent/integration-excel-agent) +> - 📚 View more documentation: [Eino ADK Documentation](https://www.cloudwego.io/docs/eino/core_modules/eino_adk/) +> - 🛠️ Browse ADK source code: [Eino ADK Source Code](https://github.com/cloudwego/eino/tree/main/adk) +> - 💡 Explore all ADK examples: [Eino ADK Examples](https://github.com/cloudwego/eino-examples/tree/main/adk) +> - 🤝 Join the developer community: Exchange experiences and best practices with other developers > -> - ⌨️ 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 +> Eino ADK makes agent development simple and powerful! diff --git a/content/en/docs/eino/overview/eino_open_source.md b/content/en/docs/eino/overview/eino_open_source.md index a50c42bfc3a..9577d22764f 100644 --- a/content/en/docs/eino/overview/eino_open_source.md +++ b/content/en/docs/eino/overview/eino_open_source.md @@ -1,158 +1,187 @@ --- Description: "" -date: "2025-11-20" +date: "2025-12-01" lastmod: "" tags: [] -title: LLM Application Development Framework — Eino Open Source Release! -weight: 1 +title: LLM Application Development Framework — Eino is Now Open Source! +weight: 3 --- -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. +Today, after more than half a year of internal use and iteration at ByteDance, the Go-based comprehensive LLM application development framework — Eino — is officially open source under CloudWeGo! -Eino defines clear “components” and provides powerful “orchestration”, covering the full development lifecycle, helping developers build deep LLM applications quickly. +Eino is based on clear "component" definitions, provides powerful "orchestration", and covers the entire development lifecycle, aiming to help developers build the most sophisticated LLM applications as quickly as possible. -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: +Have you ever had these feelings: wanting to add LLM capabilities to your application but unsure how to start in this relatively new field; wanting to continuously stay at the forefront of research and apply the latest industry achievements, but the application development framework you use hasn't been updated for months; wanting to understand Python code in your project and confirm the type of a variable or parameter requires repeatedly checking the context; uncertain whether the model-generated results are good enough, wanting to use them but not daring to; needing extra exploration and learning for other supporting tools in necessary steps like debugging, tracing, and evaluation outside of development. If so, you're welcome to learn about and try Eino, because Eino, as an LLM application development framework aimed at covering the entire DevOps lifecycle, has the following characteristics: -- 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 +- Stable core, simple and easy-to-understand API, clear onboarding path, smooth learning curve. +- Ultimate extensibility, highly active R&D work, long-term sustainability. +- Based on the strongly-typed language Golang, code is readable, maintainable, and highly reliable. +- Backed by extensive practical experience from ByteDance's core business lines. +- Provides ready-to-use supporting tools. -Eino is ByteDance’s preferred full-code framework for LLM applications, adopted across multiple business lines and hundreds of services. +Eino has become ByteDance's preferred full-code development framework for LLM applications internally, with multiple business lines including Doubao, TikTok, Coze, and hundreds of services already integrated. -Project: `https://github.com/cloudwego/eino`, `https://github.com/cloudwego/eino-ext` +Project address: [https://github.com/cloudwego/eino](https://github.com/cloudwego/eino), [https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext) -We will build around the open-source Eino repository, keeping one unified codebase for both internal and external usage. +In the future, we will take the Eino open-source library as the core code repository, insisting on **using the same codebase internally and externally**, working with the community to build the best LLM application development framework. -## Quick Look at Eino +## Quick Introduction to Eino -Eino is a devops-spanning framework — from best-practice samples (Eino Examples) to tooling for each stage: +Eino is an LLM application development framework covering the entire DevOps lifecycle, from best practice examples in Eino Examples to toolchains at each stage, all within Eino's domain: -Eino organizes LLM applications around reusable “components”. For example, the core `ChatModel`: +So what exactly can Eino do? First, Eino consists of individual LLM domain "**components**". For example, the most core is the Chat Model for interacting with LLMs: ```go -model, _ := ark.NewChatModel(ctx, config) +model, _ := ark.NewChatModel(ctx, config) // Create a Doubao LLM 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?")} ``` -Direct component usage is fine — Eino provides many implementations. But LLM apps share patterns: +Using components directly like above is of course fine - Eino provides many useful component implementations to choose from. However, LLM applications have their own characteristics and patterns, such as: -- 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. +- The core is the LLM, with business logic centered on "how to provide sufficient and effective context to the LLM" and "how to reliably let the LLM's output affect the environment". The core component types, data types, and interaction patterns can be enumerated, and the overall structure can be described by a directed graph. +- LLM output is streaming, meaning downstream of the model must effectively handle streaming data, including real-time stream processing, stream copying, merging multiple streams, concatenating single streams, etc. +- Building on directed graphs, a series of sub-problems emerge: concurrency handling, fan-in/fan-out, general-purpose aspects, option distribution, etc. -Eino’s orchestration addresses these general problems. +Eino's orchestration capabilities are a comprehensive solution to these general problems. -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. +Taking the ReAct Agent as an example: a ChatModel (LLM) "binds" Tools, receives input Messages, and the ChatModel autonomously determines whether to call a Tool or output the final result. Tool execution results become Messages fed back to the ChatModel as context for the next round of autonomous judgment. -Implemented via components plus graph orchestration; concise code cleanly maps to the diagram. +The above ReAct Agent, which performs autonomous decision-making and routing based on ChatModel, is implemented using Eino's components and Graph orchestration. The code is clear and concise, mapping cleanly to the flowchart. -- Implementation: `flow/agent/react` in the repo -- User manual: `/docs/eino/core_modules/flow_integration_components/react_agent_manual/` +- Code implementation details: [flow/agent/react](https://github.com/cloudwego/eino/blob/main/flow/agent/react/react.go) +- ReAct Agent user manual: [react_agent_manual](https://www.cloudwego.io/docs/eino/core_modules/flow_integration_components/react_agent_manual/) -This orchestration is just a few dozen lines: +In Eino, this is a few dozen lines of graph orchestration code: ```go +// Build a ReAct Agent, compiled into a Runnable with input []*Message and output *Message + +// Create a Graph containing state, for storing request-dimension Message context graph = NewGraph[[]*Message, *Message]( WithGenLocalState(func(ctx context.Context) *state { return &state{Messages: make([]*Message, 0, config.MaxStep+1)} })) +// Store context and responses from a round into the Graph's temporary 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's output may be a stream of multiple Messages +// This StreamGraphBranch can complete judgment based on just the first packet of the stream, 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 } - if len(msg.ToolCalls) == 0 { return END, nil } + + if msg, err := sr.Recv(); err != nil { + return "", err + } else if len(msg.ToolCalls) == 0 { + return END, nil + } + return nodeKeyTools, nil }, map[string]bool{nodeKeyTools: true, END: true}) -_ = graph.AddBranch(nodeKeyModel, modelPostBranch) +_ = graph.AddBranch(nodeKeyModel, modelPostBranch) + +// toolsNode execution results are fed back to chatModel _ = graph.AddEdge(nodeKeyTools, nodeKeyModel) +// Compile Graph: type checking, callback injection, automatic stream conversion, generate executor agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep)) ``` -Behind these lines, Eino automatically: +Behind these few dozen lines of code, Eino automatically does several things: -- 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 +- Type checking, ensuring adjacent node types align at compile time. +- Stream wrapping, the compiled Runnable supports both Invoke and Stream calls, regardless of whether internal Tools support streaming. +- Concurrency management, reads and writes to the public state are concurrent-safe. +- Aspect injection, if a component (like a tool) doesn't implement callback injection itself, Eino auto-injects it. +- Option distribution, the compiled Runnable can flexibly receive and distribute options to specified nodes. -## Eino’s Unique Advantages +## Eino's Unique Advantages -LLM applications evolve rapidly; a great framework must encapsulate “stable” domain abstractions while expanding horizontally and vertically as research progresses. +Software applications based on large language models are in a rapid development phase, with new technologies, ideas, and practices constantly emerging. As application developers, we need to efficiently and reliably apply industry-consensus best practices, while continuously learning and upgrading our understanding to comprehensively grasp the possibilities in this new field. Therefore, an excellent LLM application development framework needs to both **encapsulate the "unchanging" common core elements within the domain** and **agilely expand horizontally and vertically** based on the latest developments. -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. +On the other hand, currently mainstream frameworks like LangChain and LlamaIndex are based on Python. While leveraging Python's rich ecosystem enables rapid implementation of diverse functionality, they also inherit Python's issues as a dynamic language - "weak type checking" and "high long-term maintenance costs". As LLM applications rapidly enter large-scale online operation, the **high reliability** and **high maintainability** achieved through Golang, a strongly-typed language, become increasingly valuable. -Eino is practice-driven — built with feedback and iteration across Doubao/TikTok and other high-frequency, diverse scenarios. +LLM-based application development is a relatively new field, sometimes requiring learning by doing and validating understanding through practice. Leveraging ByteDance's diverse scenarios, rapid iteration, and massive feedback from high-frequency applications like Doubao and TikTok, Eino has unique advantages in **practice-driven design**. -Beyond APIs, production frameworks need tooling for real complex scenarios. +Finally, production-grade frameworks need to face real, complex business scenarios. Therefore, beyond intuitive and easy-to-use API design, providing purposefully designed development **tools** can effectively help developers understand and handle complexity and accelerate the development process. ### 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) +We believe there exists a common list of components that together constitute the common building blocks of LLM applications. Each type of component as an interface has complete, stable definitions: specific input and output types, clear runtime options, and sensible stream processing paradigms. + +Building on clear component definitions, we believe that LLM application development has universal foundational capabilities, including but not limited to: streaming programming capabilities for handling model output; Callback capabilities for supporting aspect functionality and exposing internal component state; option extension capabilities beyond the component interface definition scope for specific implementations. + +Building on component definitions and universal foundational capabilities, we believe that LLM application development has relatively fixed data flow and orchestration paradigms: centered on ChatModel (LLM), injecting user input and system prompts through ChatTemplate, injecting context through Retriever, Document Loader & Transformer, generating through ChatModel, outputting Tool Calls and executing them, or outputting final results. Based on this, Eino provides different orchestration paradigms for the above components: Chain, a chain-style 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: -### Agile Extensibility +### Agile Extension + +Each type of component 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 as components, can implement and continuously extend their own special functionalities. + +When actual business scenarios require functionality that needs to enter orchestration but doesn't correspond to any component definition, Eino supports declaring custom functions as Lambda type. 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 exist and will continue to emerge specific orchestration paradigms of multiple components. These paradigms encapsulate validated research results or practical experience, such as ReAct Agent, Host Multi-Agent, etc. These ready-to-use encapsulations condense best practices in LLM application development and will continue to expand vertically as our understanding improves. -- 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 +During component and graph execution, developers can embed custom callback logic at fixed points to inject aspect functionality. + +In summary, the Eino framework has full extensibility: -### Reliable and Maintainable +### Highly Reliable and Maintainable + +When writing Eino code in Golang, developers can fully leverage Golang's strong typing features, declaring specific types for all components, Lambdas, orchestration products, etc. This is like drawing a precise map for the code, allowing developers to follow clear paths for maintenance and extension. Even as project scale grows and functionality continues to iterate, high maintainability can still be preserved. + +At the same time, Eino's orchestration capabilities fully utilize the compile-time validation capabilities of the strong type system, exposing type matching issues as early as possible during graph compilation rather than graph runtime. Early and clear exposure of type matching issues helps developers quickly locate and fix them, reducing hard-to-troubleshoot failures and performance issues caused by type errors at runtime. + +On the other hand, Eino follows modular design, with the core library and each component implementation being separate go modules, with each go module achieving minimal dependencies. Additionally, API design follows principles of "simplicity", "intuitiveness", and "isomorphism", supplemented by comprehensive documentation from shallow to deep, trying to make the learning curve smoother. Most importantly, Eino adopts clear layered design, with clear responsibilities and cohesive functionality at each layer, improving maintainability while better ensuring stability. -- 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 +Eino Framework Structure Diagram: ### Practice-Driven -Design evolves from real needs and usage. Examples: Workflow designed for field-granularity mapping; `Message` structure enhanced based on Doubao feedback. +The design and development process of the Eino framework is rooted in two foundations: "meeting real needs" and "practice-driven design". The functionality evolution process is tightly combined with the integration process of ByteDance's various business lines, always listening to developers' voices and validating design reasonableness through actual usage effects. For example, we received the requirement from TikTok to "enable field-level granularity data mapping and passing in graphs", which became the basis for designing Workflow; listening to usage pain points from Doubao, we enhanced the Message struct as the model input/output type. In the future open-source ecosystem co-building process, we will continue to adhere to these principles, meeting the real needs of a broader range of users and developers, and seriously practicing and refining across a larger scope. ### Tooling Ecosystem -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. +Tracing, debugging, and visualization are three important supporting tools for orchestration engines. Eino has built-in tracing callbacks and integrates with APMPlus and Langfuse platforms. It also provides IDE plugins that allow visualizing the orchestrated graph at any time during coding, debugging runs, and even quickly building graphs through UI drag-and-drop and exporting as Eino code. ## Quick Start -Explore the Eino User Manual to learn concepts and build AI apps with Eino: +For learning and using Eino, we provide comprehensive Eino user manuals to help everyone quickly understand concepts in Eino and master skills for designing AI applications based on Eino. Get started with "[Eino: Quick Start](https://www.cloudwego.io/docs/eino/quick_start/)" now~ -- Quick Start: `/docs/eino/quick_start/` +For any questions, you can communicate with us and provide feedback through the Lark group below or [Eino Issues](https://github.com/cloudwego/eino/issues)~ -For questions, reach us via the community or [Eino Issues](https://github.com/cloudwego/eino/issues). +## Related Links -## 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 -- Website: https://www.cloudwego.io +Project website: __[https://www.cloudwego.io](https://www.cloudwego.io)__ -Join the community: +Scan the QR code to join the Lark community group: diff --git a/content/en/docs/eino/overview/graph_or_agent.md b/content/en/docs/eino/overview/graph_or_agent.md index 6f28d8aee9b..000dcaef6cb 100644 --- a/content/en/docs/eino/overview/graph_or_agent.md +++ b/content/en/docs/eino/overview/graph_or_agent.md @@ -1,22 +1,22 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Agent or Graph? AI Application Path Analysis -weight: 5 +weight: 8 --- ## Introduction: Two Coexisting AI Interaction Paradigms -Many application interfaces integrate different forms of AI capabilities, as shown below: +Many application interfaces have integrated different forms of AI capabilities, as shown below: This seemingly simple screenshot represents two forms of "AI applications": - The "Agent" represented by the "chat box". **Agents use LLM (Large Language Model) as the decision center, autonomously plan and can conduct multi-turn interactions**, naturally suited for handling open-ended, continuous tasks, manifesting as a "dialogue" form. -- The "Graph" represented by "buttons" or "APIs". For example, the "Recording Summary" button above, the Graph behind it is roughly "Recording" -> "LLM understands and summarizes" -> "Save recording" - this kind of fixed process. **The core of Graph lies in the determinism of its process and the closure of tasks**, completing specific goals through predefined nodes and edges, manifesting as a "function" form. For example, video generation is an "API" form AI application: +- The "Graph" represented by "buttons" or "APIs". For example, the "Recording Summary" button above - the Graph behind it is roughly "Recording" → "LLM understands and summarizes" → "Save recording" - this kind of fixed process. **The core of Graph lies in the determinism of its process and the closure of tasks**, completing specific goals through predefined nodes and edges, manifesting as a "function" form. For example, video generation is an "API" form AI application: @@ -53,11 +53,11 @@ flowchart TD This article explores in detail the differences and connections between Agent and Graph, two forms of AI applications, proposes that "the best integration point is to encapsulate Graph as Agent's Tool", and provides recommended usage patterns for [Eino](https://github.com/cloudwego/eino) developers. -## Core Concepts Clarification +## Core Concept Analysis ### Basic Definitions -- **Graph**: A flowchart **predefined** by developers with a clear topology. Its nodes can be code functions, API calls, or LLMs, and inputs and outputs are typically structured. **The core characteristic is "determinism"**, meaning given the same input, the execution path and final output are predictable. +- **Graph**: A flowchart **predefined** by developers with a clear topology. Its nodes can be code functions, API calls, or LLMs, and inputs and outputs are typically structured. **The core characteristic is "determinism"** - given the same input, the execution path and final output are predictable. - **Agent**: An entity centered on LLM that can **autonomously plan, decide, and execute** tasks. It completes goals through **dynamic interaction** with the environment (Tools, users, other Agents), and its behavior is uncertain. **The core characteristic is "autonomy"**. - **Tool**: Any external capability that an Agent can call, typically a **function or API that encapsulates specific functionality**. Tools themselves can be synchronous or asynchronous, stateful or stateless. They are only responsible for execution and do not have autonomous decision-making capabilities. - **Orchestration**: The process of **organizing and coordinating multiple compute units (nodes, Agents) to work together**. In this article, it specifically refers to predefining static processes through Graphs. @@ -65,7 +65,7 @@ This article explores in detail the differences and connections between Agent an ### Deep Comparison - + @@ -252,7 +252,7 @@ In summary: Treating an Agent simply as a Graph node is **inefficient**; a bette Since direct integration of Agent and Graph at the micro level (nodes) faces difficulties, is there a more elegant way to combine them at the macro level? The answer is yes, and this bridge is "Tool". If we observe the meanings of Graph and Tool, we can find many similarities:
    DimensionAgentGraph
    Feature DimensionAgentGraph
    Core DriverLLM Autonomous DecisionDeveloper Preset Process
    InputUnstructured natural language, images, etc.Structured data
    DeliverableProcess and result equally importantFocused on final result
    - + 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 a1dad7817f5..34d3556f1bb 100644 --- a/content/en/docs/eino/quick_start/simple_llm_application.md +++ b/content/en/docs/eino/quick_start/simple_llm_application.md @@ -1,57 +1,57 @@ --- Description: "" -date: "2025-12-09" +date: "2026-01-20" lastmod: "" tags: [] -title: Build a Minimal LLM Application +title: Building a Simple LLM Application weight: 1 --- -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. +This guide will help you quickly get started using Eino framework's ChatModel to build a simple LLM application. We will demonstrate how to use ChatModel through implementing a "Programmer Encouragement Assistant" example. > 💡 -> Code snippets in this guide: [eino-examples/quickstart/chat](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat) +> Code snippets from the examples in this article can be found at: [eino-examples/quickstart/chat](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat) -## ChatModel Overview +## **ChatModel Introduction** -`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. +ChatModel is Eino framework's abstraction for conversational large models, providing a unified interface to interact with different large model services (such as OpenAI, Ollama, etc.). -> For a detailed component guide, see: [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide) +> For more detailed component introduction, see: [Eino: ChatModel Guide](/docs/eino/core_modules/components/chat_model_guide) -## Message Structure and Usage +## **Message Structure and Usage** -In Eino, conversations are represented by `schema.Message`, an abstraction for a single chat message. Each message includes: +In Eino, conversations are represented through `schema.Message`, which is Eino's abstract definition of a conversation message. Each Message contains the following important fields: -- `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 +- `Role`: The role of the message, which can be: + - `system`: System instruction, used to set the model's behavior and role + - `user`: User input + - `assistant`: Model's reply + - `tool`: Tool call result +- `Content`: The specific content of the message -## Implementing the Encouragement Assistant +## **Implementing the Programmer Encouragement Assistant** -We’ll build a Programmer Encouragement Assistant that offers technical suggestions and supportive messaging when the developer is feeling down. +Let's learn how to use ChatModel by implementing a Programmer Encouragement Assistant. This assistant can not only provide technical advice but also offer psychological support when programmers feel discouraged. -### Create a Template and Generate Messages +### Creating Conversation Templates and Generating Messages -Eino provides versatile templating for constructing messages sent to the model: +Eino provides powerful templating functionality for building messages to input to the large model: -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) +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's built-in text/template format (e.g., "Hello, {{.name}}!") +2. Message placeholders: Support inserting a group of messages (such as conversation history) ```go -// optional=false means the message list is required; -// an absent variable in template input causes an error +// optional=false means required message list, will error if corresponding variable not found in template input schema.MessagesPlaceholder("chat_history", false) ``` -> For details, see: [Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide) +> For more detailed component introduction, see: [Eino: ChatTemplate Guide](/docs/eino/core_modules/components/chat_template_guide) -Complete example using `FString` format and a messages placeholder: +Below is the complete code for creating and using conversation templates with FString format + message placeholders: ```go // eino-examples/quickstart/chat/template.go @@ -63,47 +63,47 @@ import ( "github.com/cloudwego/eino/schema" ) -// Create a template using FString +// Create template using FString format template := prompt.FromMessages(schema.FString, // System message template - 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."), + schema.SystemMessage("You are a {role}. You need to respond in a {style} tone. Your goal is to help programmers maintain a positive and optimistic mindset, providing technical advice while also caring about their mental health."), - // Insert conversation history (omit for a new conversation) + // Insert needed conversation history (leave empty for new conversations) schema.MessagesPlaceholder("chat_history", true), // User message template schema.UserMessage("Question: {question}"), ) -// Render messages from the template +// Use template to generate messages messages, err := template.Format(context.Background(), map[string]any{ "role": "Programmer Encouragement Assistant", "style": "positive, warm, and professional", - "question": "My code keeps throwing errors and I feel frustrated. What should I do?", - // Dialogue history (simulate two rounds) + "question": "My code keeps throwing errors, I feel so frustrated, what should I do?", + // Conversation history (simulating two rounds of conversation history in this example) "chat_history": []*schema.Message{ - 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), + schema.UserMessage("Hello"), + schema.AssistantMessage("Hey! I'm your Programmer Encouragement Assistant! Remember, every excellent programmer grows through debugging. How can I help you?", nil), + schema.UserMessage("I feel like the code I write is terrible"), + schema.AssistantMessage("Every programmer goes through this stage! What's important is that you're constantly learning and improving. Let's look at the code together, I believe through refactoring and optimization, it will get better. Remember, Rome wasn't built in a day, code quality improves through continuous improvement.", nil), }, }) ``` -### Create a ChatModel +### Creating a ChatModel -`ChatModel` is one of Eino’s core components, providing a unified interface across LLM providers. Eino currently supports: +ChatModel is one of the most core components in the Eino framework, providing a unified interface for interacting with various large language models. Eino currently supports the following large language model implementations: -- 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 +- OpenAI: Supports GPT-3.5/GPT-4 and other models (also supports Azure-provided OpenAI services) +- Ollama: Supports locally deployed open-source models +- Ark: Model services on Volcano Engine (e.g., ByteDance's Doubao model) +- More models are being added -> Supported models: [Eino: Ecosystem Integration](/docs/eino/ecosystem_integration) +> For supported models, see: [Eino: Ecosystem Integration](/docs/eino/ecosystem_integration) -Examples using OpenAI and Ollama: +Below we demonstrate how to create and use ChatModel using OpenAI and Ollama as examples: -#### OpenAI (use either this or Ollama below) +#### **OpenAI (choose either this or Ollama below)** ```go // eino-examples/quickstart/chat/openai.go @@ -115,16 +115,16 @@ import ( ) chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ - Model: "gpt-4o", // model version + Model: "gpt-4o", // Model version to use APIKey: os.Getenv("OPENAI_API_KEY"), // OpenAI API key }) ``` -> Details: [ChatModel — OpenAI](/docs/eino/ecosystem_integration/chat_model/chat_model_openai) +> For detailed information about OpenAI ChatModel, see: [ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) -#### Ollama (use either this or OpenAI above) +#### **Ollama (choose either this or OpenAI above)** -Ollama supports running open-source models locally — ideal for privacy-sensitive or offline scenarios. +Ollama supports running open-source models locally, suitable for scenarios with data privacy requirements or offline usage needs. ```go // eino-examples/quickstart/chat/ollama.go @@ -135,18 +135,18 @@ import ( chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{ - BaseURL: "http://localhost:11434", // Ollama server - Model: "llama2", // model name + BaseURL: "http://localhost:11434", // Ollama service address + Model: "llama2", // Model name }) ``` -> Details: [ChatModel — Ollama](/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) +> For Ollama related information, see: [ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) -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. +Eino provides a unified ChatModel abstraction for large models and offers ready-to-use implementations of various LLMs. Therefore, business code can focus on writing business logic without worrying about model implementation details. When model implementations are updated, they won't affect core business logic, meaning developers can easily switch between different models without modifying large amounts of code. -### Run the ChatModel +### Running the ChatModel -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`). +After obtaining the ChatModel input messages and the initialized ChatModel instance through the previous two steps, you can start trying to run the ChatModel. Eino ChatModel provides two running modes: output complete message (generate) and output message stream (stream): ```go // eino-examples/quickstart/chat/generate.go @@ -163,9 +163,9 @@ result, err := chatModel.Generate(ctx, messages) streamResult, err := chatModel.Stream(ctx, messages) ``` -Streaming is essential for user experience: `Stream` allows the model to emit tokens progressively like a typewriter, giving users timely feedback. +In practical applications, there are many scenarios that need streaming responses. The main scenario is "improving user experience": the stream running mode allows ChatModel to provide typewriter-like output, giving users model responses earlier. -Eino’s streaming consumption looks like this: +Here's how Eino handles streaming output: ```go // eino-examples/quickstart/chat/stream.go @@ -183,7 +183,7 @@ func reportStream(sr *schema.StreamReader[*schema.Message]) { i := 0 for { message, err := sr.Recv() - if err == io.EOF { // end of stream + if err == io.EOF { // Streaming output ended return } if err != nil { @@ -195,13 +195,13 @@ func reportStream(sr *schema.StreamReader[*schema.Message]) { } ``` -Complete implementation: [eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go) +For the complete implementation, see: [eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go) -## Summary +## **Summary** -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. +This example demonstrated how to build an LLM application using the Eino framework through a Programmer Encouragement Assistant case. From creating a ChatModel to using message templates to actual conversation implementation, you should now have 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 applications. -## Related Reading +## **Related Reading** - Quick Start - - [Agent — Give your LLM hands](/docs/eino/quick_start/agent_llm_with_tools) + - [Agent - Give LLMs Hands](/docs/eino/quick_start/agent_llm_with_tools) diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md new file mode 100644 index 00000000000..a3f0c44c9f1 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md @@ -0,0 +1,47 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.4.*-compose optimization' +weight: 4 +--- + +## Version Overview + +v0.4.0 mainly optimized Graph orchestration capabilities, removed the `GetState` method, and enabled default eager execution mode for AllPredecessor trigger mode. + +--- + +## v0.4.0 + +**Release Date**: 2025-07-25 + +### Breaking Changes + +- **Removed GetState method**: Graph no longer supports the `GetState` method, state management needs to be implemented through other mechanisms + +### New Features + +- **AllPredecessor mode defaults to Eager Execution**: Graphs using AllPredecessor trigger mode now default to eager execution strategy, improving execution efficiency + +--- + +## v0.4.1 - v0.4.8 Main Updates + +### Feature Enhancements + +- Support using JSONSchema to describe tool parameters (#402) +- `ToJSONSchema()` compatible with OpenAPIV3 to JSONSchema conversion (#418) +- React Agent added `WithTools` convenience function (#435) +- Support printing reasoning content (#436) +- Added `PromptTokenDetails` definition (#377) + +### Bug Fixes + +- Fixed subgraph saving state from parent graph incorrectly (#389) +- Fixed branch input type being interface with nil value handling (#403) +- Fixed `toolCallChecker` using wrong context in flow_react (#373) +- Fixed edge handlers only parsing when successor ready (#438) +- Fixed error reporting when end node is skipped (#411) +- Optimized stream wrapper error handling (#409) diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md new file mode 100644 index 00000000000..b8771c82219 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md @@ -0,0 +1,72 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.5.*-ADK implementation' +weight: 5 +--- + +## Version Overview + +v0.5.0 is an important milestone release that introduces the **ADK (Agent Development Kit)** framework. ADK provides a complete set of intelligent agent development tools, supporting Agent orchestration, preset Agents, session management, interrupt and resume, and other core capabilities. + +--- + +## v0.5.0 + +**Release Date**: 2025-09-10 + +### Major New Features + +#### ADK Framework (#262) + +ADK is a complete solution for intelligent agent development, mainly including: + +- **ChatModelAgent**: Basic intelligent agent implementation based on large language models + - Support MaxIterations configuration + - Support tool calling + - Streaming output support +- **Agent as Tool**: Support wrapping Agent as a tool for other Agents to call +- **Preset Multi-Agent Patterns**: + - **Supervisor**: Supervisor pattern, where a main Agent coordinates multiple sub-Agents + - **Sequential**: Sequential execution pattern, Agents execute in order and inherit context + - **Plan-Execute-Replan**: Plan-Execute-Replan pattern +- **Session Management**: + - Session event storage and management + - Session Values support + - History Rewriter capability +- **Interrupt and Resume**: + - Support Agent execution interrupt + - Support resume execution from checkpoint + - Deterministic Transfer interrupt resume support +- **Agent Run Options**: + - `WithSessionValues` supports passing session-level variables + - Agent CallOption extension + +--- + +## v0.5.1 - v0.5.15 Main Updates + +### Feature Enhancements + +- **DeepAgent preset implementation** (#540): Support deep Agent pattern +- **Agent middleware support** (#533): Allow extending Agent behavior through middleware +- **Global callback support** (#512): Built-in Agent supports global Callbacks +- **MessageRewriter configuration** (#496): React Agent supports message rewriting +- **BreakLoopAction definition** (#492): Support loop Agent interruption +- **Cancel interrupt support** (#425): Support canceling ongoing interrupt operations +- **Multimodal support**: + - Message added multimodal output content (#459) + - Default prompt template supports multimodal (#470) + - Format function supports UserInputMultiContent (#516) + +### Bug Fixes + +- Fixed ChatModelAgent max step calculation (#549) +- Fixed Session only storing events with output (#503) +- Fixed Sequential Agent exit on error issue (#484) +- Fixed Workflow only executing action on last event (#463) +- Fixed ChatModelAgent return directly tool panic (#464) +- Fixed Go 1.25 compilation error (#457) +- Fixed empty slice and empty field serialization issues (#473) diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md new file mode 100644 index 00000000000..71c61015c77 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md @@ -0,0 +1,41 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.6.*-jsonschema optimization' +weight: 6 +--- + +## Version Overview + +v0.6.0 focuses on dependency optimization, removing kin-openapi dependency and OpenAPI3.0 related definitions, simplifying the JSONSchema module implementation. + +--- + +## v0.6.0 + +**Release Date**: 2025-11-14 + +### Breaking Changes + +- **Removed kin-openapi dependency** (#544): + - Removed dependency on `kin-openapi` library + - Removed OpenAPI 3.0 related type definitions + - Simplified JSONSchema module implementation + +### Migration Guide + +If your code uses OpenAPI 3.0 related type definitions, you need to: + +1. Check if there is code directly using `kin-openapi` related types +2. Replace OpenAPI 3.0 types with standard JSONSchema types +3. Use `schema.ToJSONSchema()` method to get JSONSchema definition for tool parameters + +--- + +## v0.6.1 Main Updates + +### Bug Fixes + +- Routine bug fixes and stability improvements diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md new file mode 100644 index 00000000000..e68296ea178 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md @@ -0,0 +1,139 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.7.*-interrupt resume refactor' +weight: 7 +--- + +## Version Overview + +v0.7.0 is an **important milestone release** for the Eino framework. The core highlight is the **architecture-level refactoring of Human in the Loop (HITL) / Interrupt-Resume capabilities**, providing a more powerful and flexible interrupt-resume mechanism. It also introduces Skill middleware, ChatModel retry mechanism, multimodal tool support, and other core capabilities. + +--- + +## v0.7.0 + +**Release Date**: 2025-11-20 + +### 🔥 Core Changes: Interrupt-Resume Architecture Refactor (#563) + +v0.7.0 performed an architecture-level refactor of the interrupt-resume mechanism, with massive code changes (+7527/-1692 lines), involving major modifications to 28 files. + +#### New Core Modules + +- **compose/resume.go**: Provides type-safe resume state retrieval API + - `GetInterruptState[T]`: Get the persisted state from last interrupt + - `GetResumeContext`: Check if current component is the resume target +- **internal/core/interrupt.go**: Interrupt signal core definitions + - `InterruptSignal`: Nested interrupt signal structure support + - `InterruptState`: State and layer-specific payload support + - `InterruptConfig`: Interrupt configuration parameters +- **internal/core/address.go**: Component address system +- **internal/core/resume.go**: Core implementation of resume logic + +#### Refactored Core Modules + +- **adk/interrupt.go**: ADK layer interrupt handling refactor (+238 lines) +- **compose/interrupt.go**: Orchestration layer interrupt handling refactor (+256 lines) +- **adk/workflow.go**: Workflow Agent supports interrupt-resume (+510 lines refactored) +- **compose/graph_run.go**: Graph execution interrupt-resume support +- **compose/tool_node.go**: Tool node interrupt support + +#### New Resume Strategies + +Two resume strategies are supported: + +1. **Implicit "Resume All"**: Single "continue" button resumes all interrupt points +2. **Explicit "Targeted Resume"**: Independent resumption of specific interrupt points (recommended) + +#### Agent Tool Interrupt Improvements + +- Use `GetInterruptState` instead of manual state management +- Support `CompositeInterrupt` composite interrupts +- Agent Tool correctly propagates internal interrupt signals + +### Other Improvements + +- **ADK serialization enhancement** (#557): Fixed missing type registration for gob serialization in checkpoint +- **DeepAgent optimization** (#558): Automatically remove task tool when no sub-Agents +- **ChatModelAgent improvement** (#552): Correctly apply compose option when no tools configured +- **Plan-Execute enhancement** (#555): Correctly report error when no tool calls +- **MultiAgent fix** (#548): Fixed default summary prompt not working issue + +--- + +## v0.7.1 - v0.7.36 Main Updates + +### Interrupt-Resume Continuous Enhancement + +Building on v0.7.0's architecture refactor, subsequent versions continue to improve interrupt-resume capabilities: + +#### Tool Interrupt API (#691) + +- **New tool interrupt API**: Support triggering interrupts during tool execution +- **Extended isResumeTarget**: Support descendant target identification + +#### Nested Agent Interrupt Resume (#647, #672) + +- **Nested preset/workflow Agents**: Support interrupt-resume for arbitrary level Agent wrappers +- **Wrapped FlowAgents**: Correctly handle deterministic transfer skip + +#### Checkpoint Enhancement + +- **Node input persistence** (#634): Persist rerun node inputs in checkpoint +- **Graph resume improvement** (#695): Correctly enable ProcessState in OnStart during resume +- **Serialization fixes** (#608, #606): Fixed array/slice deserialization panic + +#### Tool Error Handling (#583) + +- Tool error handler no longer wraps interrupt error + +### Major New Features + +#### Skill Middleware (#661) + +- Encapsulate reusable capabilities as Skills +- Extend Agent capabilities through middleware +- Optimized Skill prompts (#724) + +#### ChatModel Retry Mechanism (#635) + +- ChatModelAgent supports automatic retry on call failure +- Configurable ModelRetryConfig (#648) +- Added WillRetryError for error chain checking (#707) + +#### Multimodal Tool Support (#760) + +- compose module enhanced tool interface +- Support multimodal input and output + +#### Nested Graph State Access (#584) + +- Nested Graph can access parent Graph state + +### Feature Enhancements + +- **Execute Backend & Tool** (#682) +- **OutputKey configuration** (#711): Store final answer to SessionValues +- **Nested Runner shared session** (#645) +- **Agent event emission** (#620, #791) +- **ToJSONSchema deterministic output** (#630) +- **Token usage details** (#629) + +### Bug Fixes + +- AfterChatModel returns modified message (#717, #792) +- Loop Agent BreakLoopAction interrupt (#814) +- Sub-Agent error terminates loop Agent (#813) +- Streaming execution command no output error (#790) +- DeepAgent auto instruction rendering (#726) +- Graph duplicate skip reporting (#694) +- Graph unique successors (#693) + +### Documentation & Engineering + +- Rewrote README focusing on ADK (#748, #686, #719) +- Enabled golangci-lint (#602) +- Added code style guide (#673) diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_Breaking_Changes.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_Breaking_Changes.md new file mode 100644 index 00000000000..fc3acfa0ba2 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_Breaking_Changes.md @@ -0,0 +1,238 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: Eino v0.8 Breaking Changes +weight: 1 +--- + +> This document records all breaking changes in the `v0.8.0.Beta` branch compared to the `main` branch. + +## 1. API Breaking Changes + +### 1.1 filesystem Shell Interface Renamed + +**Location**: `adk/filesystem/backend.go` **Change Description**: Shell-related interfaces have been renamed and no longer embed the `Backend` interface. **Before (main)**: + +```go +type ShellBackend interface { + Backend + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShellBackend interface { + Backend + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +**After (v0.8.0.Beta)**: + +```go +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +**Impact**: + +- `ShellBackend` renamed to `Shell` +- `StreamingShellBackend` renamed to `StreamingShell` +- Interfaces no longer embed `Backend`. If your implementation depends on the composite interface, you need to implement them separately **Migration Guide**: + +```go +// Before +type MyBackend struct {} +func (b *MyBackend) Execute(...) {...} +// MyBackend implementing ShellBackend needed to implement all Backend methods + +// After +type MyShell struct {} +func (s *MyShell) Execute(...) {...} +// MyShell only needs to implement Shell interface methods +// If you also need Backend functionality, implement both interfaces separately +``` + +--- + +## 2. Behavioral Breaking Changes + +### 2.1 AgentEvent Sending Mechanism Change + +**Location**: `adk/chatmodel.go` **Change Description**: `ChatModelAgent`'s `AgentEvent` sending mechanism changed from eino callback mechanism to Middleware mechanism. **Before (main)**: + +- `AgentEvent` was sent through eino's callback mechanism +- If users customized ChatModel or Tool Decorator/Wrapper, and the original ChatModel/Tool had embedded Callback points, `AgentEvent` would be sent **inside** the Decorator/Wrapper +- This applied to all ChatModels implemented in eino-ext, but may not apply to most user-implemented Tools and Tools provided by eino **After (v0.8.0.Beta)**: +- `AgentEvent` is sent through Middleware mechanism +- `AgentEvent` is sent **outside** user-customized Decorator/Wrapper **Impact**: +- Under normal circumstances, users won't notice this change +- If users previously implemented their own ChatModel or Tool Decorator/Wrapper, the relative position of event sending will change +- Position change may cause `AgentEvent` content to change: previous events didn't include Decorator/Wrapper modifications, current events will include them **Reason for Change**: +- In normal business scenarios, we want emitted events to include Decorator/Wrapper modifications **Migration Guide**: If you previously wrapped ChatModel or Tool through Decorator/Wrapper, you need to implement the `ChatModelAgentMiddleware` interface instead: + +```go +// Before: Wrapping ChatModel through Decorator/Wrapper +type MyModelWrapper struct { + inner model.BaseChatModel +} + +func (w *MyModelWrapper) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) { + // Custom logic + return w.inner.Generate(ctx, input, opts...) +} + +// After: Implement WrapModel method of ChatModelAgentMiddleware +type MyMiddleware struct{} + +func (m *MyMiddleware) WrapModel(ctx context.Context, chatModel model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) { + return &myWrappedModel{inner: chatModel}, nil +} + +// For Tool Wrappers, implement WrapInvokableToolCall / WrapStreamableToolCall methods instead +``` + +### 2.2 filesystem.ReadRequest.Offset Semantic Change + +**Location**: `adk/filesystem/backend.go` **Change Description**: `Offset` field changed from 0-based to 1-based. **Before (main)**: + +```go +type ReadRequest struct { + FilePath string + // Offset is the 0-based line number to start reading from. + Offset int + Limit int +} +``` + +**After (v0.8.0.Beta)**: + +```go +type ReadRequest struct { + FilePath string + // Offset specifies the starting line number (1-based) for reading. + // Line 1 is the first line of the file. + // Values < 1 will be treated as 1. + Offset int + Limit int +} +``` + +**Migration Guide**: + +```go +// Before: Read from line 0 (i.e., first line) +req := &ReadRequest{Offset: 0, Limit: 100} + +// After: Read from line 1 (i.e., first line) +req := &ReadRequest{Offset: 1, Limit: 100} + +// If you previously used Offset: 10 to mean starting from line 11 +// Now you need to use Offset: 11 +``` + +--- + +### 2.3 filesystem.FileInfo.Path Semantic Change + +**Location**: `adk/filesystem/backend.go` **Change Description**: `FileInfo.Path` field is no longer guaranteed to be an absolute path. **Before (main)**: + +```go +type FileInfo struct { + // Path is the absolute path of the file or directory. + Path string +} +``` + +**After (v0.8.0.Beta)**: + +```go +type FileInfo struct { + // Path is the path of the file or directory, which can be a filename, + // relative path, or absolute path. + Path string + // ... +} +``` + +**Impact**: + +- Code that depends on `Path` being an absolute path may have issues +- Need to check and handle relative path cases + +--- + +### 2.4 filesystem.WriteRequest Behavior Change + +**Location**: `adk/filesystem/backend.go` **Change Description**: `WriteRequest` write behavior changed from "error if file exists" to "overwrite if file exists". **Before (main)**: + +```go +// WriteRequest comment: +// The file will be created if it does not exist, or error if file exists. +``` + +**After (v0.8.0.Beta)**: + +```go +// WriteRequest comment: +// Creates the file if it does not exist, overwrites if it exists. +``` + +**Impact**: + +- Code that previously relied on "error if file exists" behavior will no longer error, but directly overwrite +- May cause unexpected data loss **Migration Guide**: +- If you need to preserve the original behavior, check if the file exists before writing + +--- + +### 2.5 GrepRequest.Pattern Semantic Change + +**Location**: `adk/filesystem/backend.go` **Change Description**: `GrepRequest.Pattern` changed from literal matching to regular expression matching. **Before (main)**: + +```go +// Pattern is the literal string to search for. This is not a regular expression. +// The search performs an exact substring match within the file's content. +``` + +**After (v0.8.0.Beta)**: + +```go +// Pattern is the search pattern, supports full regular expression syntax. +// Uses ripgrep syntax (not grep). +``` + +**Impact**: + +- Search patterns containing regex special characters will behave differently +- For example, searching for `interface{}` now needs to be escaped as `interface\{\}` **Migration Guide**: + +```go +// Before: Literal search +req := &GrepRequest{Pattern: "interface{}"} + +// After: Regex search, need to escape special characters +req := &GrepRequest{Pattern: "interface\\{\\}"} + +// Or if searching for literals containing . * + ?, also need to escape +// Before +req := &GrepRequest{Pattern: "config.json"} +// After +req := &GrepRequest{Pattern: "config\\.json"} +``` + +--- + +## Migration Recommendations + +1. **Handle compile errors first**: Type changes (like Shell interface renaming) will cause compilation failures, need to fix first +2. **Pay attention to semantic changes**: `ReadRequest.Offset` changed from 0-based to 1-based, `Pattern` changed from literal to regex - these won't cause compile errors but will change runtime behavior +3. **Check file operations**: `WriteRequest` overwrite behavior change may cause data loss, requires additional checks +4. **Migrate Decorator/Wrapper**: If you have custom ChatModel/Tool Decorator/Wrapper, change to implement `ChatModelAgentMiddleware` +5. Upgrade backend implementations as needed: If using local/ark agentkit backend provided by eino-ext, upgrade to corresponding alpha versions: [local backend v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Flocal%2Fv0.2.0-alpha.1), [ark agentkit backend v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.0-alpha.1) +6. **Test verification**: After migration, perform comprehensive testing, especially for code involving file operations and search functionality diff --git a/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md new file mode 100644 index 00000000000..2d95921246a --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md @@ -0,0 +1,279 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.8.*-adk middlewares' +weight: 8 +--- + +This document introduces the main new features and improvements in Eino ADK v0.8.*. + +> 💡 +> Currently in the v0.8.0.Beta version stage: [https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) + +## Version Highlights + +v0.8 is a significant feature enhancement release that introduces a new middleware interface architecture, adds multiple practical middlewares, and provides enhanced observability support. + +
    DimensionGraphTool
    Feature DimensionGraphTool
    InputStructured dataStructured data
    DeliverableFocused on final resultFocused on final result
    State ManagementSingle execution, statelessSingle execution, stateless
    +
    +🔧 Flexible Middleware Architecture +New ChatModelAgentMiddleware interface +📊 Enhanced Observability +Agent-level Callback support
    + +--- + +## 1. ChatModelAgentMiddleware Interface + +> 💡 +> **Core Update**: A new middleware interface providing more flexible Agent extension mechanisms + +`ChatModelAgentMiddleware` is the most important architectural update in v0.8, providing unified extension points for `ChatModelAgent` and Agents built on top of it (such as `DeepAgent`). + +**Advantages over AgentMiddleware**: + + + + + + +
    FeatureAgentMiddlewareChatModelAgentMiddleware
    ExtensibilityClosedOpen, supports custom handlers
    Context PropagationCallbacks only return errorAll methods return (ctx, ..., error)
    Configuration ManagementScattered in closuresCentralized in struct fields
    + +**Interface Methods**: + +- `BeforeAgent` - Modify configuration before Agent runs +- `BeforeModelRewriteState` - Process state before model invocation +- `AfterModelRewriteState` - Process state after model invocation +- `WrapInvokableToolCall` - Wrap synchronous tool calls +- `WrapStreamableToolCall` - Wrap streaming tool calls +- `WrapModel` - Wrap model invocation + +**Usage**: + +```go +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: model, + Handlers: []adk.ChatModelAgentMiddleware{mw1, mw2, mw3}, +}) +``` + +See [Eino ADK: ChatModelAgentMiddleware](/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware) for details + +--- + +### 1.1 Summarization Middleware + +> 💡 +> **Function**: Automatic conversation history summarization to prevent exceeding model context window limits + +📚 **Detailed Documentation**: [Middleware: Summarization](https://www.feishu.cn/wiki/KFJ9wnrMmicvI4kWuz4cymnTnfe) + +When the token count of conversation history exceeds a threshold, automatically calls LLM to generate a summary and compress the context. + +**Core Capabilities**: + +- Configurable trigger conditions (token threshold) +- Support for retaining recent user messages +- Support for recording complete conversation history to files +- Provides pre and post processing hooks + +**Quick Start**: + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: chatModel, + Trigger: &summarization.TriggerCondition{ + ContextTokens: 100000, + }, +}) +``` + +### 1.2 ToolReduction Middleware + +> 💡 +> **Function**: Tool result compression to optimize context usage efficiency + +📚 **Detailed Documentation**: [Middleware: ToolReduction](https://www.feishu.cn/wiki/C5QAwAg3QisDo5kwaWicJBo9ndd) + +Provides two-phase tool output management: + + + + + +
    PhaseTrigger TimeEffect
    TruncationAfter tool returnsTruncate overlong output, save to file
    ClearBefore model invocationClear historical tool results, free up tokens
    + +**Quick Start**: + +```go +mw, err := reduction.New(ctx, &reduction.Config{ + Backend: fsBackend, + MaxLengthForTrunc: 30000, + MaxTokensForClear: 50000, +}) +``` + +### 1.3 Filesystem Middleware + +> 💡 +> **Function**: File system operation toolset + +📚 **Detailed Documentation**: [Middleware: FileSystem](https://www.feishu.cn/wiki/NODlwh1Q9imdetkhmgKcyF0Vncd) + +**New Capabilities**: + +- **Grep Enhancement**: Support for full regular expression syntax +- **New Options**: `CaseInsensitive`, `EnableMultiline`, `FileType` filtering +- **Custom Tool Names**: All filesystem tools support custom naming + +### 1.4 Skill Middleware + +> 💡 +> **Function**: Dynamic loading and execution of Skills + +📚 **Detailed Documentation**: [Middleware: Skill](https://www.feishu.cn/wiki/NG1xwde86ig3qhkdMhQc5bh2nIm) + +**New Capabilities**: + +- **Context Modes**: Support for `fork` and `isolate` context modes +- **Custom Configuration**: Support for custom system prompts and tool descriptions +- **FrontMatter Extension**: Support for specifying agent and model via FrontMatter + +### 1.5 PlanTask Middleware + +> 💡 +> **Function**: Task planning and execution tools + +📚 **Detailed Documentation**: [Middleware: PlanTask](https://www.feishu.cn/wiki/H7dlwwO0ZiotwBk8iTpcxesKn4d) + +Supports Agent creation and management of task plans, suitable for complex task scenarios requiring step-by-step execution. + +### 1.6 ToolSearch Middleware + +> 💡 +> **Function**: Tool search with dynamic retrieval from a large number of tools + +📚 **Detailed Documentation**: [Middleware: ToolSearch](https://www.feishu.cn/wiki/GVgXww3HJiZUx0kS7O6cfOHpnZf) + +When there are many tools, dynamically selects the most relevant tools through semantic search to avoid context overload. + +### 1.7 PatchToolCalls Middleware + +> 💡 +> **Function**: Patch dangling tool calls to ensure message history completeness + +📚 **Detailed Documentation**: [Middleware: PatchToolCalls](https://www.feishu.cn/wiki/WpkUwXnwMidkUVkGTx8ckIWWnjg) + +Scans message history and inserts placeholder messages for tool calls missing responses. Suitable for scenarios where tool calls are interrupted or cancelled. + +**Quick Start**: + +```go +mw, err := patchtoolcalls.New(ctx, nil) +``` + +## 2. Agent Callback Support + +> 💡 +> **Function**: Agent-level callback mechanism for observation and tracing + +Supports registering callbacks throughout the Agent execution lifecycle for logging, tracing, monitoring, and other functions. + +**Core Types**: + +- `AgentCallbackInput` - Callback input containing Agent input or resume information +- `AgentCallbackOutput` - Callback output containing Agent event stream + +**Usage**: + +```go +agent.Run(ctx, input, adk.WithCallbacks( + callbacks.NewHandler( + callbacks.WithOnStart(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + agentInput := adk.ConvAgentCallbackInput(input) + // Handle Agent start event + return ctx + }), + callbacks.WithOnEnd(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + agentOutput := adk.ConvAgentCallbackOutput(output) + // Handle Agent completion event + return ctx + }), + ), +)) +``` + +See [Eino ADK: Agent Callback](/docs/eino/core_modules/eino_adk/adk_agent_callback) for details + +--- + +## 3. Language Setting + +> 💡 +> **Function**: Global language settings + +Supports global setting of ADK language preferences, affecting the language of built-in prompts and messages. + +**Usage**: + +```go +adk.SetLanguage(adk.LanguageChinese) // Set to Chinese +adk.SetLanguage(adk.LanguageEnglish) // Set to English (default) +``` + +--- + +## Middleware Usage Recommendations + +> 💡 +> **Recommended Combination**: The following middlewares can be combined to cover most long conversation scenarios + +```go +handlers := []adk.ChatModelAgentMiddleware{ + patchMW, // 1. Patch dangling tool calls + reductionMW, // 2. Compress tool output + summarizationMW, // 3. Summarize conversation history +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: model, + Handlers: handlers, +}) +``` + +--- + +## Breaking Changes + +> 💡 +> Before upgrading to v0.8, please review the Breaking Changes documentation for all incompatible changes + +📚 **Complete Documentation**: [Eino v0.8 Breaking Changes](/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_Breaking_Changes) + +**Change Overview**: + + + + + + + + + +
    TypeChange Item
    API Change
    ShellBackend
    Shell
    interface rename
    Behavior Change
    AgentEvent
    sending mechanism changed to Middleware
    Behavior Change
    ReadRequest.Offset
    changed from 0-based to 1-based
    Behavior Change
    FileInfo.Path
    no longer guaranteed to be absolute path
    Behavior Change
    WriteRequest
    changed from error on file exists to overwrite
    Behavior Change
    GrepRequest.Pattern
    changed from literal to regular expression
    + +## Upgrade Guide + +For detailed migration steps and code examples, please refer to: [Eino v0.8 Breaking Changes](/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_Breaking_Changes) + +**Quick Checklist**: + +1. Check if you are using `ShellBackend` / `StreamingShellBackend` interface (needs renaming) +2. Check `ReadRequest.Offset` usage (0-based → 1-based) +3. Check `GrepRequest.Pattern` usage (literal → regex, special characters need escaping) +4. Check if you depend on `WriteRequest`'s "error on file exists" behavior +5. Check if you depend on `FileInfo.Path` being absolute path +6. If you have custom ChatModel/Tool Decorator/Wrapper, consider migrating to `ChatModelAgentMiddleware` +7. Run tests to verify functionality diff --git a/content/en/docs/eino/release_notes_and_migration/_index.md b/content/en/docs/eino/release_notes_and_migration/_index.md new file mode 100644 index 00000000000..3a2fed802c3 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/_index.md @@ -0,0 +1,65 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: Release Notes & Migration Guide' +weight: 8 +--- + +# Version Management Guidelines + +Go SDK projects typically follow [Semantic Versioning](https://semver.org/) (Semver) conventions. A Semver version number consists of three parts in the format: + +> 💡 +> v{MAJOR}.{MINOR}.{PATCH} + +- **MAJOR**: Major version number, indicates significant updates or incompatible API changes. +- **MINOR**: Minor version number, indicates new features added while maintaining backward compatibility. +- **PATCH**: Patch number, indicates backward-compatible bug fixes. + +Additionally, Semantic Versioning supports pre-release versions and metadata tags for marking **pre-release**, **experimental**, and other unofficial versions. The format is: + +> 💡 +> v{MAJOR}.{MINOR}.{PATCH}-{PRERELEASE}+{BUILD} + +- **PRERELEASE**: Pre-release version identifier, such as `alpha`, `beta`, `rc` (Release Candidate). +- **BUILD**: Build metadata (optional), typically used to identify specific builds, such as CI build numbers. + +**Eino follows the semantic versioning specification above and will have the following version types:** + + + + + + +
    Version TypeVersion Number FormatVersion DescriptionNotes
    Stable ReleaseFormat:
  • v{MAJOR}.{MINOR}.{PATCH}
  • Examples:
  • v0.1.1
  • v1.2.3
  • When releasing stable versions, ensure API stability and backward compatibility.
  • Strictly follow semantic versioning: only increment the MAJOR version for significant incompatible changes, increment MINOR for new features, and increment PATCH for bug fixes only.
  • Ensure comprehensive unit tests, integration tests, and performance tests before release.
  • Provide detailed Release Notes upon release, listing important changes, fixes, features, and migration guides (if any).
  • Pre-release
  • Alpha
  • Beta
  • RC (Release Candidate)
  • Format:
  • v{MAJOR}.{MINOR}.{PATCH}-{alpha/beta/rc}.{num}
  • Examples:
  • v0.1.1-beta.1
  • v1.2.3-rc.1
  • v1.2.3-rc.2
  • Alpha: Internal test version, features may not be complete, may have many bugs, not recommended for production environments.
  • Beta: Features are mostly complete but may still have bugs, suitable for public testing, not recommended for production environments.
  • RC (Release Candidate): Release candidate, features complete and generally stable, recommended for final comprehensive testing. If no critical bugs exist, the RC version will become the stable version. Generally, the last RC version will become the stable version.
  • Canary/Experimental/DevFormat:
  • v{MAJOR}.{MINOR}.{PATCH}-{dev}.{num}
  • Examples:
  • v0.1.1-dev.1
  • v1.2.3-dev.2
  • Canary versions are highly unstable, typically used for testing early implementations of new features or architectures. These versions may contain experimental features that could be removed or significantly modified in the future.
  • Canary versions are generally developed on experimental branches
  • Generally not used internally at ByteDance, may be used in the open source community
    + +# Common Understanding About V0, V1, Vn (n>1) + + + + + +
    TitleDescriptionNotes
    Instability Within V0 Major Versions
  • v0.x.x indicates the library is still in an unstable state, may introduce incompatible changes during MINOR version iterations, API may change, no backward compatibility is guaranteed.
  • Users should expect API changes when using these versions
  • Version number increments will not strictly follow semantic versioning rules
  • The design goal of v0.x.x is rapid iteration, allowing developers to release library versions while the API is unstable and collect user feedback.
  • Stability Within V1, Vn (n>1) Major Versions
  • v1.0.0 indicates the library has reached a stable state, API design is mature, backward compatibility is guaranteed, meaning future
    v1.x.x
    versions will not introduce incompatible changes.
  • Strictly follows semantic versioning
  • Incompatible API changes will require a MAJOR version increment to release. For example, the version would need to be bumped to
    v2.0.0
    .
  • Backward compatible feature updates will be released through MINOR version increments, e.g.,
    v1.1.0
    .
  • Backward compatible bug fixes will be released through PATCH version increments, e.g.,
    v1.0.1
    .
  • + +> 💡 +> Currently, since Eino is being released for the first time, although Eino's API is initially stable, it has not been validated at large scale in production. The MAJOR version is temporarily set to V0. After validation by at least 50+ business lines, the version will be upgraded to V1. + +# Release Notes Document Structure + +- Each {MAJOR}.{MINOR} minor version has its own document + - Naming format: "Eino: v1.2.* {title description}" +- The {MAJOR}.{MINOR} minor version records all ChangeLogs for that version +- In subdirectories of minor versions, detailed descriptions of each PATCH can optionally be placed + +``` +. +├── v1.0.* +│ └── bug_fix_1_x.txt +├── v0.2.* +├── v0.1.* + ├── bug_fix_1_xxx.txt + ├── bug_fix_2_xxxxx.txt + └── bug_fix_3_xxxxxxx.txt +``` diff --git a/content/en/docs/eino/release_notes_and_migration/v01_first_release.md b/content/en/docs/eino/release_notes_and_migration/v01_first_release.md new file mode 100644 index 00000000000..e6fd3c941b8 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/v01_first_release.md @@ -0,0 +1,96 @@ +--- +Description: "" +date: "2025-01-06" +lastmod: "" +tags: [] +title: 'Eino: v0.1.*-first release' +weight: 1 +--- + +## v0.1.6 + +> Release date: 2024-11-04 + +### Features + +- NewTool, InferTool generic parameters now support struct +- Deprecated WithGraphRunOption(), added new WithRuntimeMaxSteps method +- Adjusted ToolsNode NodeName, added TooCallbackInput/Output struct +- Added MultiQuery, Router two types of Retriever in Flow +- Added document.Loader, document.Transformer two component abstractions +- Added file_url, audio_url, video_url three types to Message MultiPart + +### BugFix + +- Error handling when InputKey does not exist in the map for nodes with InputKey +- Adjusted Name, Type, ID combination method when merging Message Stream (ConcatMessage) + +## v0.1.5 + +> Release date: 2024-10-25 + +### Features + +- When ConcatMessages, validate whether nil chunks exist in the Message stream, throw error if they do, preventing stream producers from inserting nil messages +- Added Type field in Message.ToolCall to represent ToolType, and added incremental merging of ToolType in ConcatMessages + +## v0.1.4 + +> Release date: 2024-10-23 + +### Features + +- Adjusted Chain implementation, wrapped Chain based on Graph[I, O] +- When upstream output node is an interface and downstream input node is an implementation of that interface, support attempting to assert upstream output interface as downstream input instance. + - For example, now supports scenarios like: Node1[string, any] -> Node2[string, int]. In such scenarios, it previously threw an error at Compile time, now it attempts to assert any as string, and if assertion succeeds, continues execution. +- Added Type field to schema.Message + +### BugFix + +- Corrected Eino Callback RunInfo information during Tool execution + - Corrected RunInfo Component and Type fields + - ToolName used as RunInfo.Name +- Passthrough node now allows setting OutputKey + +## v0.1.3 + +> Release date: 2024-10-17 + +### BugFix + +- Fixed ToolsNode returning Message with extra ToolCalls causing model errors + - This issue was introduced in v0.1.1's react agent support for tool return directly, which extended ToolsNode returned information. This patch uses a different approach to implement tool return directly, avoiding this issue. + +## v0.1.2 + +> Release date: 2024-10-14 + +### Features + +- StreamReader.Copy() method re-optimized to Goroutine Free Copy method. Prevents Goroutine leaks when business forgets to call StreamReader.Close() +- Validation check: Passthrough nodes do not allow adding InputKey/OutputKey +- Compile Callback Option now supports InputKey, OutputKey passback + +## v0.1.1 + +> Release date: 2024-10-14 + +### Features + +- React Agent supports static configuration of Tool ReturnDirectly + +### BugFix + +- Reverted new Stream Copy logic. + - Due to implementing Goroutine Free Stream Copy, Recv could potentially hang. Expected to fix in next Patch + +## v0.1.0 + +> Release date: 2024-10-12 + +### Features + +- Support for ChatTemplate/ChatModel/Tool/LoaderAndSplitter/Indexer/Retriever/Embedding multiple component abstractions and implementations +- Support for Graph/Chain/StateGraph and other orchestration tools +- Based on whether input and output are streaming, supports 4 interaction modes with built-in Stream tools +- Flexible and extensible aspect design diff --git a/content/en/docs/eino/release_notes_and_migration/v02_second_release.md b/content/en/docs/eino/release_notes_and_migration/v02_second_release.md new file mode 100644 index 00000000000..33d390c5f97 --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/v02_second_release.md @@ -0,0 +1,124 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.2.*-second release' +weight: 2 +--- + +## v0.2.6 + +> Release Date: 2024-11-27 + +### Features + +- Added streaming Pre and Post StateHandler +- Support for StateChain +- Added MessageParser node to convert ChatModel output Message to business-customized structures + - Parse(ctx context.Context, m *Message) (T, error) +- Support for WithNodeKey() when Chain AppendNode + +### BugFix + +- Fixed the issue where the first Chunk was modified during ConcatMessage due to lack of deep Copy. +- During ConcatMessage, FinishReason now only retains the valid value from the last Chunk + +## v0.2.5 + +> Release Date: 2024-11-21 + +### BugFix + +- Fixed panic caused by disabling keywords like include in Gonja + +## v0.2.4 + +> Release Date: 2024-11-20 + +### Features + +- Added TokenUsage field in Eino Message ResponseMeta +- Eino Message ToolsCall sorted by index + +### BugFix + +## v0.2.3 + +> Release Date: 2024-11-12 + +### Features + +- Support for context Timeout and Cancel during Graph invocation + +### BugFix + +- FinishReason may be returned in any chunk, not necessarily in the last chunk +- callbacks.HandlerBuilder no longer provides a default Needed() method. This method defaults to returning false, which causes all aspect functions to fail in embedded callbacks.HandlerBuilder scenarios + +## v0.2.2 + +> Release Date: 2024-11-12 + +### Features + +- Added FinishReason field in Message +- Added GetState[T]() method to get State struct in nodes +- Lazy Init Gonja SDK + +### BugFix + +## v0.2.1 + +> Release Date: 2024-11-07 + +### BugFix + +- Fixed the SSTI vulnerability in the Jinja chat template [langchaingo has gonja template injection vulnerability](https://bytedance.larkoffice.com/docx/UvqxdlFfSoTIr1xtsQ5cIZTVn2b) + +## v0.2.0 + +> Release Date: 2024-11-07 + +### Features + +- Callback API refactoring (compatible update) + + - For component implementers: Hidden and deprecated callbacks.Manager, providing simpler utility functions for injecting callback aspects. + - For Handler implementers: Provides template methods for quick callbacks.Handler implementation, encapsulating details such as component type checking, input/output type assertion and conversion. Users only need to provide specific implementations of specific callback methods for specific components. + - Runtime mechanism: For a specific callback aspect timing during a run, additional filtering of handlers to execute is performed based on component type and specific methods implemented by Handler. +- Added Host Multi-Agent: Implemented Host mode Multi-Agent, where Host performs intent recognition and then redirects to various Specialist Agents for specific generation. +- React Agent API changes (incompatible) + + - Removed AgentCallback definition, changed to quickly inject ChatModel and Tool CallbackHandlers through BuildAgentCallback utility function. Usage: + + ```go + func BuildAgentCallback(modelHandler *model.CallbackHandler, toolHandler *tool.CallbackHandler) callbacks.Handler { + return template.NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler() + } + ``` + + - This achieves semantic alignment between AgentCallback and components, allowing ctx to be returned and using extended tool.CallbackInput, tool.CallbackOutput. + - Removed react.Option definition. React Agent now uses the common agent.Option definition for Agent, facilitating orchestration at the multi-agent level. + + - WithAgentCallback is no longer needed to inject special AgentCallback, new usage: + + ``` + agent.WithComposeOptions(compose.WithCallbacks(xxxCallbackHandler)) + ``` +- Added Document Parser interface definition: As a dependency of the Loader component, responsible for parsing io.Reader into Document, and provides ExtParser implementation for parsing based on file extension. + +### BugFix + +- Fixed potential null pointer exception caused by embedding.GetCommonOptions and indexer.GetCommonOptions not checking apply for null. +- During Graph runtime, preProcessor and postProcessor use the current ctx. + +## v0.2.0-dev.1 + +> Release Date: 2024-11-05 + +### Features + +- Initial design and support for Checkpoint mechanism, available for early trial + +### BugFix diff --git a/content/en/docs/eino/release_notes_and_migration/v03_tiny_break_change.md b/content/en/docs/eino/release_notes_and_migration/v03_tiny_break_change.md new file mode 100644 index 00000000000..0cd204a63be --- /dev/null +++ b/content/en/docs/eino/release_notes_and_migration/v03_tiny_break_change.md @@ -0,0 +1,39 @@ +--- +Description: "" +date: "2025-01-06" +lastmod: "" +tags: [] +title: 'Eino: v0.3.*-tiny break change' +weight: 3 +--- + +## v0.3.1-Internal Release + +> Release date: 2024-12-17 +> +> Going forward, only the open source version will be maintained. Migration scripts will be provided to help migrate from the internal version to the open source version + +### Features + +- Simplified concepts exposed by Eino + - Graph, Chain support State, removed StateGraph, StateChain + - Removed GraphKey concept +- Optimized MultiReader read performance during stream merging + +### BugFix + +- Fixed missing Error check when AddNode in Chain + +## v0.3.0 + +> Release date: 2024-12-09 + +### Features + +- schema.ToolInfo.ParamsOneOf changed to pointer. Supports scenarios where ParamsOneOf is nil +- Compile Callback now supports returning GenStateFn +- Support for global Compile Callback + +### BugFix + +- When CallOption DesignateNode, should not execute Graph aspects, only execute aspects of the designated node diff --git a/content/zh/docs/eino/Eino: Cookbook.md b/content/zh/docs/eino/Eino: Cookbook.md index c4acf519939..9d04a0e78d2 100644 --- a/content/zh/docs/eino/Eino: Cookbook.md +++ b/content/zh/docs/eino/Eino: Cookbook.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Cookbook' diff --git a/content/zh/docs/eino/FAQ.md b/content/zh/docs/eino/FAQ.md index b03c8bf9f5c..95d4cb92b23 100644 --- a/content/zh/docs/eino/FAQ.md +++ b/content/zh/docs/eino/FAQ.md @@ -1,10 +1,10 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: FAQ -weight: 7 +weight: 11 --- # 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 @@ -170,6 +170,38 @@ ChatModel 产生的 Tool Call 中,Argument 字段是 string。Eino 框架在 利用 `GraphCompileCallback` 机制在 `graph.Compile` 的过程中将拓扑结构导出。一个导出为 mermaid 图的代码样例:[https://github.com/cloudwego/eino-examples/tree/main/devops/visualize](https://github.com/cloudwego/eino-examples/tree/main/devops/visualize) -## Q: Eino 中使用 Flow/react Agent 场景下如何获取工具调用的 Tool Call Message 以及本次调用工具的 Tool Result 结果? +- Flow/React Agent 场景下获取中间结构参考文档 [Eino: ReAct Agent 使用手册](/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual) -- Flow/React Agent 场景下获取中间结构参考文档 [Eino: ReAct Agent 使用手册](/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual) - 此外还可以将 Flow/React Agent 替换成 ADK 的 ChatModel Agent 具体可参考 [Eino ADK: 概述](/zh/docs/eino/core_modules/eino_adk/agent_preview) +# Q: Gemini 模型报错 missing a `thought_signature` + +Gemini 模型协议不是 openai-compatible,应使用 gemini 封装 [https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini), 如果使用 ModelHub 平台的模型,使用内场 gemini 封装 [https://code.byted.org/flow/eino-byted-ext/tree/master/components/model/bytedgemini](https://code.byted.org/flow/eino-byted-ext/tree/master/components/model/bytedgemini),初始化参考代码: + +``` +cm, err := bytedgemini.NewChatModel(ctx, &bytedgemini.Config{ + ClientConfig: genai.ClientConfig{ + APIKey: apiKey, + Backend: genai.BackendGeminiAPI, + // uncomment if you want to print the actual request in CURL format + // HTTPClient: &http.Client{Transport: NewCurlLogger(http.DefaultTransport, log.Printf)}, + HTTPOptions: genai.HTTPOptions{ + // this is base URL for cn, other regions: + // - sg: gpt-i18n.byteintl.net + // - sg from office network: genai-sg-og.tiktok-row.org + // - va: search-va.byteintl.net + // - va from office network: genai-va-og.tiktok-row.org + // - Non-TT: gpt-i18n.bd.byteintl.net + // - US-TTP: gpt.tiktokd.net + // - EU-TTP, GCP: gpt.tiktoke.org + // - JP: gpt-jp.byteintl.net + // see also: https://bytedance.larkoffice.com/wiki/wikcnUPXCY2idGyg2AXKPvay4pd + BaseURL: "https://search.bytedance.net/gpt/openapi/online/multimodal/crawl/google/", + APIVersion: "v1", + }, + }, + Model: modelName, + ThinkingConfig: &genai.ThinkingConfig{ + IncludeThoughts: true, + ThinkingBudget: nil, + }, +}) +``` 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 4daec9faba6..7545799e8e1 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,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Callback 用户手册' @@ -189,7 +189,7 @@ Graph 会为内部所有的 Node 自动注入 RunInfo。机制是每个 Node 的 ### 组件实现内部触发(Component Callback) -在组件实现的代码中,调用 callbacks 包中的 `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), OnEndWithStreamOutput()`。以 Ark 的 ChatModel 实现为例,在 Generate 方法中: +在组件实现的代码中,调用 callbacks 包中的 `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), ``OnEndWithStreamOutput``()`。以 Ark 的 ChatModel 实现为例,在 Generate 方法中: ```go func (cm *ChatModel) Generate(ctx context.Context, in []*schema.Message, opts ...fmodel.Option) ( diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md index b318160ee7b..20df224340f 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/checkpoint_interrupt.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Interrupt & CheckPoint使用手册' @@ -32,9 +32,9 @@ import ( func main() { g := NewGraph[string, string]() - err := g.AddLambdaNode("node1", compose.InvokableLambda(func(ctx context._Context_, input string) (output string, err error) {/*invokable func*/}) + 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*/}) + 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 @@ -54,13 +54,13 @@ func main() { ```go // compose/checkpoint.go -type InterruptInfo struct { +**type **InterruptInfo **struct **{ State any BeforeNodes []string AfterNodes []string RerunNodes []string - RerunNodesExtra map[string]any - SubGraphs map[string]*InterruptInfo + RerunNodesExtra **map**[string]any + SubGraphs **map**[string]*InterruptInfo InterruptContexts []*InterruptCtx } @@ -102,8 +102,8 @@ CheckPointStore 是一个 key 类型为 string、value 类型为[]byte 的 KV // 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) + Get(ctx **context**._Context_, key string) (value []byte, existed bool,err error) + Set(ctx **context**._Context_, key string, value []byte) (err error) } ``` @@ -206,7 +206,7 @@ Checkpoint id 会被作为 CheckPointStore 的 key 使用,graph 运行时会 var InterruptAndRerun = errors.New("interrupt and rerun") // emit an interrupt signal with extra info -func NewInterruptAndRerunErr(extra any) error +**func **NewInterruptAndRerunErr(extra any) error ``` Eino Graph 接收到节点返回此错误后会发生 interrupt,恢复运行时,会再次运行此节点,再次运行前会调用 StateModifier 修改 state(如果已配置)。 @@ -245,7 +245,7 @@ func CompositeInterrupt(ctx context.Context, info any, state any, errs ...error) _// WithGraphInterrupt creates a context with graph cancellation support._ _// When the returned context is used to invoke a graph or workflow, calling the interrupt function will trigger an interrupt._ _// The graph will wait for current tasks to complete by default._ -func WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt func(opts ...GraphInterruptOption)) {} +**func **WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt **func**(opts ...GraphInterruptOption)) {} ``` 在主动调用 interrupt function 时,可以传递超时等参数: @@ -255,8 +255,8 @@ func WithGraphInterrupt(parent context.Context) (ctx context.Context, interrupt _// WithGraphInterruptTimeout specifies the max waiting time before generating an interrupt._ _// After the max waiting time, the graph will force an interrupt. Any unfinished tasks will be re-run when the graph is resumed._ -func WithGraphInterruptTimeout(timeout time.Duration) GraphInterruptOption { - return func(o *graphInterruptOptions) { +**func **WithGraphInterruptTimeout(timeout time.Duration) GraphInterruptOption { + **return func**(o *graphInterruptOptions) { o.timeout = &timeout } } 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 8fd99a1ff64..da6890f3d58 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,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: 编排的设计理念' 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 39ef62359a2..9c7025c0804 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md @@ -101,7 +101,7 @@ Collect 和 Transform 两种流式范式,目前只在编排场景有用到。 上面的 Concat message stream 是 Eino 框架自动提供的能力,即使不是 message,是任意的 T,只要满足特定的条件,Eino 框架都会自动去做这个 StreamReader[T] 到 T 的转化,这个条件是:**在编排中,当一个组件的上游输出是 StreamReader[T],但是组件只提供了 T 作为输入的业务接口时,框架会自动将 StreamReader[T] concat 成 T,再输入给这个组件。** > 💡 -> 框架自动将 StreamReader[T] concat 成 T 的过程,可能需要用户提供一个 Concat function。详见 [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles) 中关于“合并帧”的章节。 +> 框架自动将 StreamReader[T] concat 成 T 的过程,可能需要用户提供一个 Concat function。详见 [Eino: 编排的设计理念](/zh/docs/eino/core_modules/chain_and_graph_orchestration/orchestration_design_principles#share-FaVnd9E2foy4fAxtbTqcsgq3n5f) 中关于“合并帧”的章节。 另一方面,考虑一个相反的例子。还是 React Agent,这次是一个更完整的编排示意图: 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 b8dc003ff0a..8d00247d6b1 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,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: Workflow 编排框架' diff --git a/content/zh/docs/eino/core_modules/components/agentic_chat_model_guide.md b/content/zh/docs/eino/core_modules/components/agentic_chat_model_guide.md index cf11bc0ab73..f6b3c0d4298 100644 --- a/content/zh/docs/eino/core_modules/components/agentic_chat_model_guide.md +++ b/content/zh/docs/eino/core_modules/components/agentic_chat_model_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: AgenticModel 使用说明[Beta]' @@ -15,12 +15,22 @@ AgenticModel 是一种以 “目标驱动的自主执行” 为核心的模型 - + - +
    AgenticModelChatModel
    定位以 “目标驱动的自主执行” 为核心的模型能力抽象,是 ChatModel 的增强抽象一次性问答引擎
    定位基于 AgenticMessage 的 Model 组件抽象基于 Message 的 Model 组件抽象
    核心实体
  • AgenticMessage
  • ContentBlock
  • Message
    能力
  • 模型多轮对话生成
  • 会话缓存
  • 支持调用多种内置工具
  • 支持调用 MCP 工具
  • 更好的模型适配性
  • 模型单轮对话生成
  • 会话缓存
  • 支持调用简单的内置工具
  • 相关组件
  • AgenticModel
  • AgenticTemplate
  • AgenticToolsNode
  • ChatModel
  • ChatTemplate
  • ToolsNode
  • 相关组件
  • AgenticTemplate
  • AgenticToolsNode
  • ChatTemplate
  • ToolsNode
  • +Server-side tool(如 web_search)、MCP tool 在模型提供商得到了原生支持,继而一次接口请求响应中可能会包含多次推理-行动的结果。以使用联网搜索这个能力举例 + +- 基于 ChatModel 实现,需要预先自定义实现联网搜索工具。一次推理-行动过程如下: + 1. 模型生成 tool call 参数 + 2. 用户侧执行工具 + 3. 给模型返回 tool result +- 基于 AgenticModel 实现,可以直接配置由模型提供商提供的原生联网搜索工具。一次接口请求过程如下: + 1. 模型根据用户问题自行调用联网搜索工具,并在模型服务端完成多次工具调用,即会产生多次推理-行动结果,直到完成用户任务。 + 2. 用户侧只管接收结果。 + ## 组件定义 ### 接口定义 @@ -70,7 +80,7 @@ type AgenticModel interface { > 代码位置:[https://github.com/cloudwego/eino/tree/main/schema/agentic_message.go](https://github.com/cloudwego/eino/tree/main/schema/agentic_message.go) -`AgenticMessage` 是与模型交互的基本单元。模型的一次完整响应被封装成一个 `AgenticMessage` ,它通过包含一组有序的 `ContentBlock` 来承载复杂的复合内容,定义如下: +`AgenticMessage` 是与模型交互的基本单元。模型的一次完整响应被封装成一个 `AgenticMessage` ,它通过包含一组有序的 `ContentBlock` 来承载复杂的复合内容。`AgenticMessage` 对比 `Message` 最大的差异就是引入了 `ContentBlock` 数组的概念,能够承载 `AgenticModel` 的多次推理-行动输出。定义如下: ```go type AgenticMessage struct { 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 effd8b9b7e0..b2f16b490ce 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 @@ -4,7 +4,7 @@ date: "2026-01-20" lastmod: "" tags: [] title: 'Eino: ChatModel 使用说明' -weight: 1 +weight: 8 --- ## 基本介绍 @@ -336,9 +336,9 @@ result, err := runnable.Invoke(ctx, messages, compose.WithCallbacks(helper)) ## **已有实现** -1. OpenAI ChatModel: 使用 OpenAI 的 GPT 系列模型 [ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) -2. Ollama ChatModel: 使用 Ollama 本地模型 [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) -3. ARK ChatModel: 使用 ARK 平台的模型服务 [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark) +1. OpenAI ChatModel: 使用 OpenAI 的 GPT 系列模型 [ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) +2. Ollama ChatModel: 使用 Ollama 本地模型 [ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) +3. ARK ChatModel: 使用 ARK 平台的模型服务 [ChatModel - ARK](https://bytedance.larkoffice.com/wiki/WUzzwaX8ricGwZk1i1mcJHHNnEl) 4. 更多查看: [Eino ChatModel](https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model/) ## 自行实现参考 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 585b5198816..5cfdf7d11e1 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 @@ -4,7 +4,7 @@ date: "2025-11-20" lastmod: "" tags: [] title: 'Eino: ChatTemplate 使用说明' -weight: 2 +weight: 7 --- ## **基本介绍** 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 94b1f4b497d..63a0f8a2c6a 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 @@ -4,7 +4,7 @@ date: "2025-11-20" lastmod: "" tags: [] title: 'Eino: Document Loader 使用说明' -weight: 8 +weight: 1 --- ## **基本介绍** 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 de731cb1802..64682e05a77 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 @@ -4,7 +4,7 @@ date: "2025-07-21" lastmod: "" tags: [] title: 'Eino: Document Transformer 使用说明' -weight: 9 +weight: 3 --- ## **基本介绍** 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 cffb81f24f5..c6f22f8c85e 100644 --- a/content/zh/docs/eino/core_modules/components/embedding_guide.md +++ b/content/zh/docs/eino/core_modules/components/embedding_guide.md @@ -4,7 +4,7 @@ date: "2025-07-21" lastmod: "" tags: [] title: 'Eino: Embedding 使用说明' -weight: 7 +weight: 2 --- ## **基本介绍** 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 78a8b0db9ce..e8bd87bf1cb 100644 --- a/content/zh/docs/eino/core_modules/components/indexer_guide.md +++ b/content/zh/docs/eino/core_modules/components/indexer_guide.md @@ -4,7 +4,7 @@ date: "2026-01-20" lastmod: "" tags: [] title: 'Eino: Indexer 使用说明' -weight: 6 +weight: 5 --- ## **基本介绍** 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 769a43ddd68..e3b555fd6e9 100644 --- a/content/zh/docs/eino/core_modules/components/lambda_guide.md +++ b/content/zh/docs/eino/core_modules/components/lambda_guide.md @@ -4,7 +4,7 @@ date: "2025-11-20" lastmod: "" tags: [] title: 'Eino: Lambda 使用说明' -weight: 5 +weight: 4 --- ## **基本介绍** 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 29cf9e5aa49..f3e0eaacde4 100644 --- a/content/zh/docs/eino/core_modules/components/retriever_guide.md +++ b/content/zh/docs/eino/core_modules/components/retriever_guide.md @@ -4,7 +4,7 @@ date: "2026-01-30" lastmod: "" tags: [] title: 'Eino: Retriever 使用说明' -weight: 4 +weight: 6 --- ## **基本介绍** 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 fa7b938a336..44d94cb893b 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,10 +1,10 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: ToolsNode&Tool 使用说明' -weight: 3 +weight: 9 --- ## **基本介绍** @@ -97,9 +97,13 @@ ChatModel(LLM) 生成要调用的 []ToolCall(包含 ToolName,Argument 等) ### **接口定义** -Tool 组件提供了三个层次的接口: +Tool 组件提供了两类接口:**标准工具接口**和**增强型工具接口**。 -> 代码位置:eino/compose/tool/interface.go +> 代码位置:eino/components/tool/interface.go + +#### **标准工具接口** + +标准工具接口返回字符串类型的结果: ```go // 基础工具接口,提供工具信息 @@ -120,6 +124,88 @@ type StreamableTool interface { } ``` +#### **增强型工具接口(Enhanced Tool)** + +增强型工具接口支持返回结构化的多模态结果(`*schema.ToolResult`),可以包含文本、图片、音频、视频和文件等多种类型的内容: + +```go +// EnhancedInvokableTool 是支持返回结构化多模态结果的工具接口 +// 与返回字符串的 InvokableTool 不同,此接口返回 *schema.ToolResult +// 可以包含文本、图片、音频、视频和文件 +type EnhancedInvokableTool interface { + BaseTool + InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error) +} + +// EnhancedStreamableTool 是支持返回结构化多模态结果的流式工具接口 +// 提供流式读取器以逐步访问多模态内容 +type EnhancedStreamableTool interface { + BaseTool + StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error) +} +``` + +### **增强型工具相关数据结构** + +> 代码位置:eino/schema/message.go + +#### **ToolArgument - 工具输入参数** + +```go +// ToolArgument 包含工具调用的输入信息 +type ToolArgument struct { + // TextArgument 包含 JSON 格式的工具调用参数 + TextArgument string +} +``` + +#### **ToolResult - 工具输出结果** + +```go +// ToolResult 表示工具执行的结构化多模态输出 +// 当工具需要返回不仅仅是简单字符串时使用, +// 例如图片、文件或其他结构化数据 +type ToolResult struct { + // Parts 包含多模态输出部分。每个部分可以是不同类型的内容, + // 如文本、图片或文件 + Parts []ToolOutputPart `json:"parts,omitempty"` +} +``` + +#### **ToolOutputPart - 输出内容部分** + +```go +// ToolPartType 定义工具输出部分的内容类型 +type ToolPartType string + +const ( + ToolPartTypeText ToolPartType = "text" // 文本 + ToolPartTypeImage ToolPartType = "image" // 图片 + ToolPartTypeAudio ToolPartType = "audio" // 音频 + ToolPartTypeVideo ToolPartType = "video" // 视频 + ToolPartTypeFile ToolPartType = "file" // 文件 +) + +// ToolOutputPart 表示工具执行输出的一部分 +type ToolOutputPart struct { + Type ToolPartType `json:"type"` // 内容类型 + Text string `json:"text,omitempty"` // 文本内容 + Image *ToolOutputImage `json:"image,omitempty"` // 图片内容 + Audio *ToolOutputAudio `json:"audio,omitempty"` // 音频内容 + Video *ToolOutputVideo `json:"video,omitempty"` // 视频内容 + File *ToolOutputFile `json:"file,omitempty"` // 文件内容 + Extra map[string]any `json:"extra,omitempty"` // 扩展信息 +} + +// 多媒体内容结构体,都包含 URL 或 Base64 数据以及 MIME 类型信息 +type ToolOutputImage struct { MessagePartCommon } +type ToolOutputAudio struct { MessagePartCommon } +type ToolOutputVideo struct { MessagePartCommon } +type ToolOutputFile struct { MessagePartCommon } +``` + +### **方法说明** + #### **Info 方法** - 功能:获取工具的描述信息 @@ -129,7 +215,7 @@ type StreamableTool interface { - `*schema.ToolInfo`:工具的描述信息 - error:获取信息过程中的错误 -#### **InvokableRun 方法** +#### **InvokableRun 方法(标准工具)** - 功能:同步执行工具 - 参数: @@ -140,17 +226,39 @@ type StreamableTool interface { - string:执行结果 - error:执行过程中的错误 -#### **StreamableRun 方法** +#### **InvokableRun 方法(增强型工具)** + +- 功能:同步执行工具,返回多模态结果 +- 参数: + - ctx:上下文对象 + - `toolArgument`:包含 JSON 格式参数的 `*schema.ToolArgument` + - opts:工具执行的选项 +- 返回值: + - `*schema.ToolResult`:包含多模态内容的执行结果 + - error:执行过程中的错误 + +#### **StreamableRun 方法(标准工具)** - 功能:以流式方式执行工具 - 参数: - - ctx:上下文对象,用于传递请求级别的信息,同时也用于传递 Callback Manager + - ctx:上下文对象 - `argumentsInJSON`:JSON 格式的参数字符串 - opts:工具执行的选项 - 返回值: - `*schema.StreamReader[string]`:流式执行结果 - error:执行过程中的错误 +#### **StreamableRun 方法(增强型工具)** + +- 功能:以流式方式执行工具,返回多模态结果流 +- 参数: + - ctx:上下文对象 + - `toolArgument`:包含 JSON 格式参数的 `*schema.ToolArgument` + - opts:工具执行的选项 +- 返回值: + - `*schema.StreamReader[*schema.ToolResult]`:流式多模态执行结果 + - error:执行过程中的错误 + ### **ToolInfo 结构体** > 代码位置:eino/schema/tool.go @@ -176,6 +284,8 @@ Tool 组件使用 ToolOption 来定义可选参数, ToolsNode 没有抽象公 ## **使用方式** +### **标准工具使用** + ```go import ( "github.com/cloudwego/eino/components/tool" @@ -206,7 +316,159 @@ input := &schema.Message{ toolMessages, err := toolsNode.Invoke(ctx, input) ``` -ToolsNode 通常不会被单独使用,一般用于编排之中接在 ChatModel 之后。 +### **增强型工具使用** + +增强型工具适用于需要返回多模态内容的场景,如返回图片、音频、视频或文件等。 + +#### **方式一:使用 InferEnhancedTool 自动推断** + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +// 定义输入参数结构体 +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"description=搜索关键词"` +} + +// 创建增强型工具 +imageSearchTool, err := utils.InferEnhancedTool( + "image_search", + "搜索并返回相关图片", + func(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + // 执行图片搜索逻辑... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "找到以下图片:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil + }, +) +``` + +#### **方式二:使用 NewEnhancedTool 手动创建** + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type FileGeneratorInput struct { + FileName string `json:"file_name"` + Content string `json:"content"` +} + +// 手动定义 ToolInfo +toolInfo := &schema.ToolInfo{ + Name: "file_generator", + Desc: "生成并返回文件", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "file_name": {Type: "string", Desc: "文件名"}, + "content": {Type: "string", Desc: "文件内容"}, + }), +} + +// 创建增强型工具 +fileGenTool := utils.NewEnhancedTool[*FileGeneratorInput]( + toolInfo, + func(ctx context.Context, input *FileGeneratorInput) (*schema.ToolResult, error) { + fileURL := "https://example.com/files/" + input.FileName + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "文件已生成:" + input.FileName}, + { + Type: schema.ToolPartTypeFile, + File: &schema.ToolOutputFile{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &fileURL, + MIMEType: "text/plain", + }, + }, + }, + }, + }, nil + }, +) +``` + +#### **方式三:实现 EnhancedInvokableTool 接口** + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/schema" +) + +type MyEnhancedTool struct { + info *schema.ToolInfo +} + +func (t *MyEnhancedTool) Info(ctx context.Context) (*schema.ToolInfo, error) { + return t.info, nil +} + +func (t *MyEnhancedTool) InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.ToolResult, error) { + // 解析参数 + // toolArgument.TextArgument 包含 JSON 格式的参数 + + // 执行工具逻辑... + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "执行结果"}, + }, + }, nil +} +``` + +#### **增强型流式工具** + +```go +import ( + "context" + + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type StreamInput struct { + Query string `json:"query"` +} + +// 创建增强型流式工具 +streamTool, err := utils.InferEnhancedStreamTool( + "stream_search", + "流式搜索工具", + func(ctx context.Context, input *StreamInput) (*schema.StreamReader[*schema.ToolResult], error) { + results := []*schema.ToolResult{ + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "搜索中..."}}}, + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "找到结果"}}}, + } + return schema.StreamReaderFromArray(results), nil + }, +) +``` ### **在编排中使用** @@ -218,11 +480,13 @@ import ( ) // 创建工具节点 -toolsNode := compose.NewToolsNode([]tool.Tool{ - searchTool, // 搜索工具 - weatherTool, // 天气查询工具 - calculatorTool, // 计算器工具 -}) +toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{ + Tools: []tool.BaseTool{ + searchTool, // 搜索工具 + weatherTool, // 天气查询工具 + calculatorTool, // 计算器工具 + }, + }) // 在 Chain 中使用 chain := compose.NewChain[*schema.Message, []*schema.Message]() @@ -230,9 +494,11 @@ chain.AppendToolsNode(toolsNode) // graph 中 graph := compose.NewGraph[*schema.Message, []*schema.Message]() -graph.AddToolsNode(toolsNode) +graph.AddToolsNode("tools", toolsNode) ``` +> **注意**:当工具同时实现了标准接口和增强型接口时,ToolsNode 会优先使用增强型接口。 + ## **Option 机制** 自定义 Tool 可根据自己需要实现特定的 Option: @@ -255,6 +521,69 @@ func WithTimeout(timeout time.Duration) tool.Option { } ``` +## **Middleware 机制** + +ToolsNode 支持通过 Middleware 对工具调用进行拦截和增强。Middleware 分为四种类型: + +```go +// compose/tool_node.go + +// ToolMiddleware 组合了 invokable 和 streamable 工具调用的中间件钩子 +type ToolMiddleware struct { + // Invokable 用于非流式标准工具调用 + Invokable InvokableToolMiddleware + + // Streamable 用于流式标准工具调用 + Streamable StreamableToolMiddleware + + // EnhancedInvokable 用于非流式增强型工具调用 + EnhancedInvokable EnhancedInvokableToolMiddleware + + // EnhancedStreamable 用于流式增强型工具调用 + EnhancedStreamable EnhancedStreamableToolMiddleware +} +``` + +### **Middleware 使用示例** + +```go +import ( + "context" + "fmt" + + "github.com/cloudwego/eino/compose" + "github.com/cloudwego/eino/schema" +) + +// 创建带 Middleware 的 ToolsNode +toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{ + Tools: []tool.BaseTool{myEnhancedTool}, + ToolCallMiddlewares: []compose.ToolMiddleware{ + { + // 标准工具中间件 + Invokable: func(next compose.InvokableToolEndpoint) compose.InvokableToolEndpoint { + return func(ctx context.Context, input *compose.ToolInput) (*compose.ToolOutput, error) { + fmt.Printf("调用标准工具: %s\n", input.Name) + return next(ctx, input) + } + }, + // 增强型工具中间件 + EnhancedInvokable: func(next compose.EnhancedInvokableToolEndpoint) compose.EnhancedInvokableToolEndpoint { + return func(ctx context.Context, input *compose.ToolInput) (*compose.EnhancedInvokableToolOutput, error) { + fmt.Printf("调用增强型工具: %s\n", input.Name) + output, err := next(ctx, input) + if err != nil { + return nil, err + } + fmt.Printf("增强型工具返回 %d 个内容部分\n", len(output.Result.Parts)) + return output, nil + } + }, + }, + }, +}) +``` + ## **Option 和 Callback 使用** ### **Callback 使用示例** @@ -341,3 +670,48 @@ Eino 移除了 OpenAPI schema 3.0 相关的所有定义与方法,转为使用 升级后,部分 eino-ext module 可能报错“_undefined: schema.NewParamsOneOfByOpenAPIV3_”等问题,升级报错的 eino-ext module 到最新版本即可。 如果 schema 改造比较复杂,可以使用 [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397) 中提供的工具方法辅助转换。 + +### **v0.6.x 新增增强型工具(Enhanced Tool)** + +新增 `EnhancedInvokableTool` 和 `EnhancedStreamableTool` 接口,支持返回结构化的多模态结果。 + +**主要变更:** + +1. **新增工具接口**: + +- `EnhancedInvokableTool`:接收 `*schema.ToolArgument`,返回 `*schema.ToolResult` +- `EnhancedStreamableTool`:接收 `*schema.ToolArgument`,返回 `*schema.StreamReader[*schema.ToolResult]` + +1. **新增工具辅助函数**(`components/tool/utils/`): + +- `InferEnhancedTool`:从函数自动推断创建增强型工具 +- `InferEnhancedStreamTool`:从函数自动推断创建增强型流式工具 +- `NewEnhancedTool`:手动创建增强型工具 +- `NewEnhancedStreamTool`:手动创建增强型流式工具 + +1. **新增数据结构**(`schema/message.go`): + +- `ToolPartType`:工具输出内容类型枚举(text、image、audio、video、file) +- `ToolArgument`:工具输入参数结构体 +- `ToolResult`:工具多模态输出结果结构体 +- `ToolOutputPart`:工具输出内容部分 +- `ToolOutputImage/Audio/Video/File`:各类多媒体输出结构体 + +1. **ToolsNode 增强**: + +- 新增 `EnhancedInvokableToolMiddleware` 和 `EnhancedStreamableToolMiddleware` +- 支持增强型工具和标准工具混合使用 +- 当工具同时实现两种接口时,优先使用增强型接口 + +1. **Callback 增强**: + +- `CallbackOutput` 新增 `ToolOutput *schema.ToolResult` 字段,用于增强型工具的多模态输出 + +**使用场景:** + +增强型工具适用于需要返回富媒体内容的场景,例如: + +- 图片搜索工具返回搜索到的图片 +- 文件生成工具返回生成的文件 +- 音视频处理工具返回处理后的媒体文件 +- 多模态 AI Agent 场景 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 579cf9e8d00..b2e5d7bd692 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,22 +1,22 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 如何创建一个 tool ? weight: 1 --- -## Tool 的基本结构 +## **Tool 的基本结构** -一个 agent 要调用 tool,需要有两步: ① 大模型根据 tool 的功能和参数需求构建调用参数 ② 实际调用 tool +一个 agent 要调用 tool,需要有两步:① 大模型根据 tool 的功能和参数需求构建调用参数 ② 实际调用 tool 这两个基本步骤也就要求了 tool 需要包含两个部分: - tool 的功能介绍和调用这个 tool 所需要的参数信息 - 调用这个 tool 的接口 -在 Eino 中,BaseTool 接口要求任何一个 tool 都要有 `Info()` 接口返回 tool 信息,如下: +在 Eino 中,BaseTool 接口要求任何一个 tool 都要有 Info() 接口返回 tool 信息,如下: ```go type BaseTool interface { @@ -24,7 +24,9 @@ type BaseTool interface { } ``` -而根据一个 tool 被调用后的返回结构是否是流式的,可以分为 InvokableTool 和 StreamableTool,也同样是以接口方式定义: +### **标准工具接口** + +根据一个 tool 被调用后的返回结构是否是流式的,可以分为 InvokableTool 和 StreamableTool,也同样是以接口方式定义: ```go type InvokableTool interface { @@ -41,11 +43,67 @@ type StreamableTool interface { } ``` -## ToolInfo 的表示方式 +### **增强型工具接口(Enhanced Tool)** + +除了标准工具接口外,Eino 还提供了增强型工具接口,支持返回结构化的多模态结果。增强型工具适用于需要返回图片、音频、视频、文件等富媒体内容的场景: + +```go +// EnhancedInvokableTool 是支持返回结构化多模态结果的工具接口 +// 与返回字符串的 InvokableTool 不同,此接口返回 *schema.ToolResult +// 可以包含文本、图片、音频、视频和文件 +type EnhancedInvokableTool interface { + BaseTool + InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error) +} + +// EnhancedStreamableTool 是支持返回结构化多模态结果的流式工具接口 +type EnhancedStreamableTool interface { + BaseTool + StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error) +} +``` + +#### **增强型工具相关数据结构** + +```go +// ToolArgument 包含工具调用的输入信息 +type ToolArgument struct { + TextArgument string // JSON 格式的工具调用参数 +} + +// ToolResult 表示工具执行的结构化多模态输出 +type ToolResult struct { + Parts []ToolOutputPart `json:"parts,omitempty"` +} + +// ToolPartType 定义工具输出部分的内容类型 +type ToolPartType string -在大模型的 function call 调用过程中,由大模型生成需要调用的 function call 的参数,这就要求大模型能理解生成的参数是否符合约束。在 Eino 中,根据开发者的使用习惯和领域标准两方面因素,提供了 `params map[string]*ParameterInfo` 和 `JSON Schema` 两种参数约束的表达方式。 +const ( + ToolPartTypeText ToolPartType = "text" // 文本 + ToolPartTypeImage ToolPartType = "image" // 图片 + ToolPartTypeAudio ToolPartType = "audio" // 音频 + ToolPartTypeVideo ToolPartType = "video" // 视频 + ToolPartTypeFile ToolPartType = "file" // 文件 +) -### 方式 1 - map[string]*ParameterInfo +// ToolOutputPart 表示工具执行输出的一部分 +type ToolOutputPart struct { + Type ToolPartType `json:"type"` + Text string `json:"text,omitempty"` + Image *ToolOutputImage `json:"image,omitempty"` + Audio *ToolOutputAudio `json:"audio,omitempty"` + Video *ToolOutputVideo `json:"video,omitempty"` + File *ToolOutputFile `json:"file,omitempty"` + Extra map[string]any `json:"extra,omitempty"` +} +``` + +## **ToolInfo 的表示方式** + +在大模型的 function call 调用过程中,由大模型生成需要调用的 function call 的参数,这就要求大模型能理解生成的参数是否符合约束。在 Eino 中,根据开发者的使用习惯和领域标准两方面因素,提供了 `params map[string]*ParameterInfo` 和 `*jsonschema.Schema` 两种参数约束的表达方式。 + +### **方式 1 - map[string]*ParameterInfo** 在很多开发者的直观习惯中,对于参数的描述方式可以用一个 map 来表示,key 即为参数名,value 则是这个参数的详细约束。Eino 中定义了 ParameterInfo 来表示一个参数的描述,如下: @@ -73,7 +131,7 @@ map[string]*schema.ParameterInfo{ Type: schema.Integer, }, "gender": &schema.ParameterInfo{ - Type: schema.String, + Type: schema.String, Enum: []string{"male", "female"}, }, } @@ -81,13 +139,13 @@ map[string]*schema.ParameterInfo{ 这样的表示方式非常简单直观,当参数由开发者通过编码的方式手动维护时常用。 -### 方式 2 - JSON Schema +### **方式 2 - JSON Schema** -另一种常用于表示参数约束的方式是 `JSON schema`: [https://json-schema.org/draft/2020-12](https://json-schema.org/draft/2020-12). +另一种常用于表示参数约束的方式是 JSON Schema([https://json-schema.org/draft/2020-12](https://json-schema.org/draft/2020-12%EF%BC%89%E3%80%82)[)。](https://json-schema.org/draft/2020-12%EF%BC%89%E3%80%82) JSON Schema 的标准中对参数的约束方式非常丰富。在实际的使用中,一般不由开发者自行构建此结构体,而是使用一些方法来生成。 -#### 使用 GoStruct2ParamsOneOf 生成 +#### **使用 GoStruct2ParamsOneOf 生成** Eino 提供了在结构体中通过 go tag 描述参数约束的方式,并提供了 GoStruct2ParamsOneOf 方法来生成一个 struct 的参数约束,其函数签名如下: @@ -97,13 +155,13 @@ func GoStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error) 其中从 T 中提取参数的字段名称和描述,提取时所用的 Tag 如下: -- jsonschema_description:"xxx " [推荐]或者 jsonschema: "description=xxx" +- `jsonschema_description:"xxx"` [推荐] 或者 `jsonschema:"description=xxx"` - description 中一般会有逗号,且 tag 中逗号是不同字段的分隔符,且不可被转义,强烈推荐使用 jsonschema_description 这个单独的 Tag 标签 -- jsonschema: "enum=xxx,enum=yyy,enum=zzz" -- 字段默认为 required,json: "xxx,omitempty" => 可用 json tag 的 omitempty 代表非 required -- 使用 utils.WithSchemaModifier 实现自定义的解析方法 +- `jsonschema:"enum=xxx,enum=yyy,enum=zzz"` +- `jsonschema:"required"` +- `json:"xxx,omitempty"` => 可用 json tag 的 omitempty 代表非 required -可参考如下例子: +使用 `utils.WithSchemaModifier` 实现自定义的解析方法,可参考如下例子: ```go package main @@ -114,7 +172,7 @@ import ( ) type User struct { - Name string `json:"name,omitempty" jsonschema_description=the name 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"` } @@ -124,13 +182,17 @@ func main() { } ``` -这个方法一般不由开发者调用,往往直接使用 `utils.GoStruct2ToolInfo()` 来构建 ToolInfo,或者直接用 `utils.InferTool()` 直接构建 tool,可详见下方把 “本地函数转为 tool” 部分。 +这个方法一般不由开发者调用,往往直接使用 `utils.GoStruct2ToolInfo()` 来构建 ToolInfo,或者直接用 `utils.InferTool()` 直接构建 tool,可详见下方把 "本地函数转为 tool" 部分。 + +## **实现 Tool 的方式** + +### **方式 1 - 直接实现接口** -## 实现 Tool 的方式 +由于 tool 的定义都是接口,因此最直接实现一个 tool 的方式即实现接口。 -### 方式 1 - 直接实现接口 +#### **实现标准工具接口** -由于 tool 的定义都是接口,因此最直接实现一个 tool 的方式即实现接口,以 InvokableTool 为例: +以 InvokableTool 为例: ```go type AddUser struct{} @@ -157,17 +219,65 @@ func (t *AddUser) InvokableRun(_ context.Context, argumentsInJSON string, _ ...t 由于大模型给出的 function call 参数始终是一个 string,对应到 Eino 框架中,tool 的调用参数入参也就是一个序列化成 string 的 json。因此,这种方式需要开发者自行处理参数的反序列化,并且调用的结果也用 string 的方式返回。 -### 方式 2 - 把本地函数转为 tool +#### **实现增强型工具接口** -在开发过程中,我们经常需要把一个本地函数封装成 Eino 的 tool,比如我们代码中本身已经有了一个 `AddUser` 的方法,但为了让大模型可以自主决策如何调用这个方法,我们要把这个方法变成一个 tool 并 bind 到大模型上。 +当需要返回多模态内容(如图片、音频、视频、文件等)时,可以实现 EnhancedInvokableTool 接口: -Eino 中提供了 `NewTool` 的方法来把一个函数转成 tool,同时,针对为参数约束通过结构体的 tag 来表示的场景提供了 InferTool 的方法,让构建的过程更加简单。 +```go +type ImageSearchTool struct{} -> 下方方法的示例可以参考 [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 也有对应的构建方法。 +func (t *ImageSearchTool) Info(_ context.Context) (*schema.ToolInfo, error) { + return &schema.ToolInfo{ + Name: "image_search", + Desc: "搜索并返回相关图片", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": { + Type: schema.String, + Desc: "搜索关键词", + Required: true, + }, + }), + }, nil +} -#### 使用 NewTool 方法 +func (t *ImageSearchTool) InvokableRun(_ context.Context, toolArgument *schema.ToolArgument, _ ...tool.Option) (*schema.ToolResult, error) { + // 1. 解析参数(toolArgument.TextArgument 包含 JSON 格式的参数) + var input struct { + Query string `json:"query"` + } + json.Unmarshal([]byte(toolArgument.TextArgument), &input) + + // 2. 执行搜索逻辑... + imageURL := "https://example.com/image.png" + + // 3. 返回多模态结果 + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "找到以下图片:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} +``` + +### **方式 2 - 把本地函数转为 tool** + +在开发过程中,我们经常需要把一个本地函数封装成 Eino 的 tool,比如我们代码中本身已经有了一个 AddUser 的方法,但为了让大模型可以自主决策如何调用这个方法,我们要把这个方法变成一个 tool 并 bind 到大模型上。 + +Eino 中提供了 NewTool 的方法来把一个函数转成 tool,同时,针对为参数约束通过结构体的 tag 来表示的场景提供了 InferTool 的方法,让构建的过程更加简单。 + +下方方法的示例可以参考 `cloudwego/eino/components/tool/utils/invokable_func_test.go` 和 `cloudwego/eino/components/tool/utils/streamable_func_test.go` 中的单元测试。 -当一个函数满足下面这种函数签名时,就可以用 NewTool 把其变成一个 InvokableTool : +#### **标准工具:使用 NewTool 方法** + +当一个函数满足下面这种函数签名时,就可以用 NewTool 把其变成一个 InvokableTool: ```go type InvokeFunc[T, D any] func(ctx context.Context, input T) (output D, err error) @@ -180,7 +290,7 @@ NewTool 的方法如下: func NewTool[T, D any](desc *schema.ToolInfo, i InvokeFunc[T, D], opts ...Option) tool.InvokableTool ``` -> 同理 NewStreamTool 可创建 StreamableTool +同理 NewStreamTool 可创建 StreamableTool。 以 AddUser 为例,就可以用如下的方式构建: @@ -219,7 +329,7 @@ func createTool() tool.InvokableTool { Type: schema.Integer, }, "gender": &schema.ParameterInfo{ - Type: schema.String, + Type: schema.String, Enum: []string{"male", "female"}, }, }, @@ -230,11 +340,11 @@ func createTool() tool.InvokableTool { } ``` -#### 使用 InferTool 方法 +#### **标准工具:使用 InferTool 方法** -从 NewTool 中可以看出,构建一个 tool 的过程需要分别传入 ToolInfo 和 InvokeFunc ,其中,ToolInfo 中包含 ParamsOneOf 的部分,这代表着函数的入参约束,同时,InvokeFunc 的函数签名中也有 input 的参数,这就意味着: ParamsOneOf 的部分和 InvokeFunc 的 input 参数需要保持一致。 +从 NewTool 中可以看出,构建一个 tool 的过程需要分别传入 ToolInfo 和 InvokeFunc,其中,ToolInfo 中包含 ParamsOneOf 的部分,这代表着函数的入参约束,同时,InvokeFunc 的函数签名中也有 input 的参数,这就意味着:ParamsOneOf 的部分和 InvokeFunc 的 input 参数需要保持一致。 -当一个函数完全由开发者自行实现的时候,就需要开发者手动维护 input 参数和 ParamsOneOf 以保持一致。更优雅的解决方法是 “参数约束直接维护在 input 参数类型定义中”,可参考上方 `GoStruct2ParamsOneOf` 的介绍。 +当一个函数完全由开发者自行实现的时候,就需要开发者手动维护 input 参数和 ParamsOneOf 以保持一致。更优雅的解决方法是 "参数约束直接维护在 input 参数类型定义中",可参考上方 GoStruct2ParamsOneOf 的介绍。 当参数约束信息包含在 input 参数类型定义中时,就可以使用 InferTool 来实现,函数签名如下: @@ -252,9 +362,9 @@ import ( ) type User struct { - Name string `json:"name" jsonschema:"description=the name of the user"` - Age int `json:"age,omitempty" jsonschema:"description=the age of the user"` - Gender string `json:"gender,omitempty" jsonschema:"enum=male,enum=female"` + Name string `json:"name" jsonschema:"required,description=the name of the user"` + Age int `json:"age" jsonschema:"description=the age of the user"` + Gender string `json:"gender" jsonschema:"enum=male,enum=female"` } type Result struct { @@ -270,9 +380,199 @@ func createTool() (tool.InvokableTool, error) { } ``` -#### 使用 InferOptionableTool 方法 +#### **增强型工具:使用 NewEnhancedTool 方法** + +当需要返回多模态结果时,可以使用 NewEnhancedTool 方法: + +```go +type EnhancedInvokeFunc[T any] func(ctx context.Context, input T) (output *schema.ToolResult, err error) + +func NewEnhancedTool[T any](desc *schema.ToolInfo, i EnhancedInvokeFunc[T], opts ...Option) tool.EnhancedInvokableTool +``` + +示例: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchInput struct { + Query string `json:"query"` +} + +func searchImages(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + // 执行图片搜索逻辑... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "找到以下图片:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} + +func createEnhancedTool() tool.EnhancedInvokableTool { + return utils.NewEnhancedTool(&schema.ToolInfo{ + Name: "image_search", + Desc: "搜索并返回相关图片", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": {Type: schema.String, Desc: "搜索关键词", Required: true}, + }), + }, searchImages) +} +``` + +#### **增强型工具:使用 InferEnhancedTool 方法** + +类似于 InferTool,InferEnhancedTool 可以从函数签名自动推断参数约束: + +```go +func InferEnhancedTool[T any](toolName, toolDesc string, i EnhancedInvokeFunc[T], opts ...Option) (tool.EnhancedInvokableTool, error) +``` -Option 机制是 Eino 提供的一种在运行时传递动态参数的机制,详情可以参考 [Eino: CallOption 能力与规范](/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities),这套机制在自定义 tool 中同样适用。 +示例: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"required" jsonschema_description:"搜索关键词"` +} + +func searchImages(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) { + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: "找到以下图片:"}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{ + URL: &imageURL, + }, + }, + }, + }, + }, nil +} + +func createEnhancedTool() (tool.EnhancedInvokableTool, error) { + return utils.InferEnhancedTool("image_search", "搜索并返回相关图片", searchImages) +} +``` + +#### **增强型流式工具:使用 InferEnhancedStreamTool 方法** + +对于需要流式返回多模态内容的场景,可以使用 InferEnhancedStreamTool: + +```go +func InferEnhancedStreamTool[T any](toolName, toolDesc string, s EnhancedStreamFunc[T], opts ...Option) (tool.EnhancedStreamableTool, error) +``` + +示例: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type StreamSearchInput struct { + Query string `json:"query" jsonschema:"required"` +} + +func streamSearch(ctx context.Context, input *StreamSearchInput) (*schema.StreamReader[*schema.ToolResult], error) { + results := []*schema.ToolResult{ + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "搜索中..."}}}, + {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "找到结果"}}}, + } + return schema.StreamReaderFromArray(results), nil +} + +func createEnhancedStreamTool() (tool.EnhancedStreamableTool, error) { + return utils.InferEnhancedStreamTool("stream_search", "流式搜索工具", streamSearch) +} +``` + +#### **增强型工具:使用 InferOptionableEnhancedTool 方法** + +当需要自定义 option 参数时,可以使用 InferOptionableEnhancedTool: + +```go +func InferOptionableEnhancedTool[T any](toolName, toolDesc string, i OptionableEnhancedInvokeFunc[T], opts ...Option) (tool.EnhancedInvokableTool, error) +``` + +示例: + +```go +import ( + "context" + "github.com/cloudwego/eino/components/tool" + "github.com/cloudwego/eino/components/tool/utils" + "github.com/cloudwego/eino/schema" +) + +type ImageSearchOption struct { + MaxResults int + Quality string +} + +func WithMaxResults(n int) tool.Option { + return tool.WrapImplSpecificOptFn(func(o *ImageSearchOption) { + o.MaxResults = n + }) +} + +type ImageSearchInput struct { + Query string `json:"query" jsonschema:"required"` +} + +func searchImagesWithOption(ctx context.Context, input *ImageSearchInput, opts ...tool.Option) (*schema.ToolResult, error) { + baseOption := &ImageSearchOption{MaxResults: 10, Quality: "high"} + option := tool.GetImplSpecificOptions(baseOption, opts...) + + // 使用 option.MaxResults 和 option.Quality 执行搜索... + imageURL := "https://example.com/image.png" + + return &schema.ToolResult{ + Parts: []schema.ToolOutputPart{ + {Type: schema.ToolPartTypeText, Text: fmt.Sprintf("返回 %d 张图片:", option.MaxResults)}, + { + Type: schema.ToolPartTypeImage, + Image: &schema.ToolOutputImage{ + MessagePartCommon: schema.MessagePartCommon{URL: &imageURL}, + }, + }, + }, + }, nil +} + +func createOptionableEnhancedTool() (tool.EnhancedInvokableTool, error) { + return utils.InferOptionableEnhancedTool("image_search", "搜索图片", searchImagesWithOption) +} +``` + +#### **使用 InferOptionableTool 方法(标准工具)** + +Option 机制是 Eino 提供的一种在运行时传递动态参数的机制,详情可以参考 Eino: CallOption 能力与规范,这套机制在自定义 tool 中同样适用。 当开发者要实现一个需要自定义 option 参数时则可使用 InferOptionableTool 这个方法,相比于 InferTool 对函数签名的要求,这个方法的签名增加了一个 option 参数,签名如下: @@ -280,7 +580,7 @@ Option 机制是 Eino 提供的一种在运行时传递动态参数的机制, func InferOptionableTool[T, D any](toolName, toolDesc string, i OptionableInvokeFunc[T, D], opts ...Option) (tool.InvokableTool, error) ``` -示例如下(改编自 [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/invokable_func_test.go`): ```go import ( @@ -322,11 +622,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) 中的各种实现。 +除了自定义的各种 tool 需要自行实现外,eino-ext 项目中还有很多通用的 tool 实现,可以实现开箱即用,比如 Tool - Googlesearch、Tool - DuckDuckGoSearch、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 的种类。 @@ -372,4 +672,8 @@ func getMCPTool(ctx context.Context) []tool.BaseTool { } ``` -> 代码参考: [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) +代码参考:[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) + +## **工具类型选择指南** + +> **注意**:当工具同时实现了标准接口和增强型接口时,ToolsNode 会优先使用增强型接口。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md new file mode 100644 index 00000000000..b98ad356bc5 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_Ark_Agentkit_Sandbox.md @@ -0,0 +1,205 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Backend: Ark Agentkit Sandbox' +weight: 1 +--- + +## Agentkit Sandbox Backend + +Package: `github.com/cloudwego/eino-ext/adk/backend/agentkit` + +注意:如果 eino 版本是 v0.8.0 及以上,需要使用 ark agentkit backend 的 [v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.0-alpha.1) 版本。 + +### 概述 + +Agentkit Sandbox Backend 是 EINO ADK FileSystem 的远程沙箱实现,通过火山引擎 Agentkit 服务在隔离的云端环境中执行文件系统操作。 + +#### 核心特性 + +- 安全隔离 - 所有操作在远程沙箱环境中执行 +- 会话管理 - 支持会话隔离与可配置的 TTL +- 请求签名 - 自动使用 AK/SK 进行火山引擎 API 认证 + +### 安装 + +```bash +go get github.com/cloudwego/eino-ext/adk/backend/agentkit +``` + +### 配置 + +#### 环境变量 + +```bash +export VOLC_ACCESS_KEY_ID="your_access_key" +export VOLC_SECRET_ACCESS_KEY="your_secret_key" +export VOLC_TOOL_ID="your_tool_id" +``` + +#### Config 结构 + +```go +type Config struct { + // 必填 + AccessKeyID string // 访问密钥 ID + SecretAccessKey string // 访问密钥 Secret + ToolID string // 沙箱工具 ID + + // 可选 + UserSessionID string // 用户会话 ID,用于隔离 + Region Region // 区域,默认 cn-beijing + SessionTTL int // 会话 TTL(60-86400 秒) + ExecutionTimeout int // 命令执行超时 + HTTPClient *http.Client // 自定义 HTTP 客户端 +} +``` + +### 快速开始 + +#### 基本用法 + +```go +import ( + "context" + "os" + "time" + + "github.com/cloudwego/eino-ext/adk/backend/agentkit" + "github.com/cloudwego/eino/adk/filesystem" +) + +func main() { + ctx := context.Background() + + backend, err := agentkit.NewSandboxToolBackend(&agentkit.Config{ + AccessKeyID: os.Getenv("VOLC_ACCESS_KEY_ID"), + SecretAccessKey: os.Getenv("VOLC_SECRET_ACCESS_KEY"), + ToolID: os.Getenv("VOLC_TOOL_ID"), + UserSessionID: "session-" + time.Now().Format("20060102-150405"), + Region: agentkit.RegionOfBeijing, + }) + if err != nil { + panic(err) + } + + // 写入文件 + err = backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/home/gem/hello.txt", + Content: "Hello, Sandbox!", + }) + + // 读取文件 + content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/home/gem/hello.txt", + }) +} +``` + +#### 与 Agent 集成 + +```go +import ( + "github.com/cloudwego/eino/adk" + fsMiddleware "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +// 创建 Backend +backend, _ := agentkit.NewSandboxToolBackend(config) + +// 创建 Middleware +middleware, _ := fsMiddleware.New(ctx, &fsMiddleware.Config{ + Backend: backend, + Shell: backend, +}) + +// 创建 Agent +agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "SandboxAgent", + Description: "具有安全文件系统访问能力的 AI Agent", + Model: chatModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### API 参考 + + + + + + + + + + +
    方法描述
    LsInfo列出目录内容
    Read读取文件内容(支持分页)
    Write创建新文件(已存在则报错)
    Edit替换文件内容
    GrepRaw搜索文件内容
    GlobInfo按模式查找文件
    Execute执行 shell 命令
    + +#### 示例 + +```go +// 列出目录 +files, _ := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/home/gem", +}) + +// 读取文件(分页) +content, _ := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/home/gem/file.txt", + Offset: 0, + Limit: 100, +}) + +// 搜索内容 +matches, _ := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Path: "/home/gem", + Pattern: "keyword", + Glob: "*.txt", +}) + +// 查找文件 +files, _ := backend.GlobInfo(ctx, &filesystem.GlobInfoRequest{ + Path: "/home/gem", + Pattern: "**/*.txt", +}) + +// 编辑文件 +backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/home/gem/file.txt", + OldString: "old", + NewString: "new", + ReplaceAll: true, +}) + +// 执行命令 +result, _ := backend.Execute(ctx, &filesystem.ExecuteRequest{ + Command: "ls -la /home/gem", +}) +``` + +### 与 Local Backend 对比 + + + + + + + + +
    特性AgentkitLocal
    执行模型远程沙箱本地直接
    网络依赖需要不需要
    配置复杂度需要凭证零配置
    安全模型隔离沙箱OS 权限
    适用场景多租户/生产环境开发/本地环境
    + +### 常见问题 + +**Q: Write 返回 "file already exists" 错误** + +这是安全特性,使用不同文件名或用 Edit 修改现有文件。 + +**Q: 认证失败** + +检查环境变量、AK/SK 是否匹配、账户是否有 Ark Sandbox 权限。 + +**Q: 请求超时** + +增加 ExecutionTimeout 或 HTTPClient.Timeout 配置。 diff --git "a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" "b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" new file mode 100644 index 00000000000..0edc7855997 --- /dev/null +++ "b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/Backend_\346\234\254\345\234\260\346\226\207\344\273\266\347\263\273\347\273\237.md" @@ -0,0 +1,234 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Backend: 本地文件系统' +weight: 2 +--- + +## Local Backend + +Package: `github.com/cloudwego/eino-ext/adk/backend/local` + +注意:如果 eino 版本是 v0.8.0 及以上,需要使用 local backend 的 [v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Flocal%2Fv0.2.0-alpha.1) 版本。 + +### 概述 + +Local Backend 是 EINO ADK FileSystem 的本地文件系统实现,直接操作本机文件系统,提供原生性能和零配置体验。 + +#### 核心特性 + +- 零配置 - 开箱即用 +- 原生性能 - 直接文件系统访问,无网络开销 +- 路径安全 - 强制使用绝对路径 +- 流式执行 - 支持命令输出实时流 +- 命令验证 - 可选的安全验证钩子 + +### 安装 + +```bash +go get github.com/cloudwego/eino-ext/adk/backend/local +``` + +### 配置 + +```go +type Config struct { + // 可选: 命令验证函数,用于 Execute() 安全控制 + ValidateCommand func(string) error +} +``` + +### 快速开始 + +#### 基本用法 + +```go +import ( + "context" + + "github.com/cloudwego/eino-ext/adk/backend/local" + "github.com/cloudwego/eino/adk/filesystem" +) + +func main() { + ctx := context.Background() + + backend, err := local.NewBackend(ctx, &local.Config{}) + if err != nil { + panic(err) + } + + // 写入文件(必须是绝对路径) + err = backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/tmp/hello.txt", + Content: "Hello, Local Backend!", + }) + + // 读取文件 + content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/tmp/hello.txt", + }) +} +``` + +#### 带命令验证 + +```go +func validateCommand(cmd string) error { + allowed := map[string]bool{"ls": true, "cat": true, "grep": true} + parts := strings.Fields(cmd) + if len(parts) == 0 || !allowed[parts[0]] { + return fmt.Errorf("command not allowed: %s", parts[0]) + } + return nil +} + +backend, _ := local.NewBackend(ctx, &local.Config{ + ValidateCommand: validateCommand, +}) +``` + +#### 与 Agent 集成 + +```go +import ( + "github.com/cloudwego/eino/adk" + fsMiddleware "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +// 创建 Backend +backend, _ := local.NewBackend(ctx, &local.Config{}) + +// 创建 Middleware +middleware, _ := fsMiddleware.New(ctx, &fsMiddleware.Config{ + Backend: backend, + StreamingShell: backend, +}) + +// 创建 Agent +agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "LocalFileAgent", + Description: "具有本地文件系统访问能力的 AI Agent", + Model: chatModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### API 参考 + + + + + + + + + + + +
    方法描述
    LsInfo列出目录内容
    Read读取文件内容(支持分页,默认 200 行)
    Write创建新文件(已存在则报错)
    Edit替换文件内容
    GrepRaw搜索文件内容(字面量匹配)
    GlobInfo按模式查找文件
    Execute执行 shell 命令
    ExecuteStreaming流式执行命令
    + +#### 示例 + +```go +// 列出目录 +files, _ := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/home/user", +}) + +// 读取文件(分页) +content, _ := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/path/to/file.txt", + Offset: 0, + Limit: 50, +}) + +// 搜索内容(字面量匹配,非正则) +matches, _ := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Path: "/home/user/project", + Pattern: "TODO", + Glob: "*.go", +}) + +// 查找文件 +files, _ := backend.GlobInfo(ctx, &filesystem.GlobInfoRequest{ + Path: "/home/user", + Pattern: "**/*.go", +}) + +// 编辑文件 +backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/tmp/file.txt", + OldString: "old", + NewString: "new", + ReplaceAll: true, +}) + +// 执行命令 +result, _ := backend.Execute(ctx, &filesystem.ExecuteRequest{ + Command: "ls -la /tmp", +}) + +// 流式执行 +reader, _ := backend.ExecuteStreaming(ctx, &filesystem.ExecuteRequest{ + Command: "tail -f /var/log/app.log", +}) +for { + resp, err := reader.Recv() + if err == io.EOF { + break + } + fmt.Print(resp.Stdout) +} +``` + +### 路径要求 + +所有路径必须是绝对路径(以 `/` 开头): + +```go +// 正确 +backend.Read(ctx, &filesystem.ReadRequest{FilePath: "/home/user/file.txt"}) + +// 错误 +backend.Read(ctx, &filesystem.ReadRequest{FilePath: "./file.txt"}) +``` + +转换相对路径: + +```go +absPath, _ := filepath.Abs("./relative/path") +``` + +### 与 Agentkit Backend 对比 + + + + + + + + + + +
    特性LocalAgentkit
    执行模型本地直接远程沙箱
    网络依赖需要
    配置复杂度零配置需要凭证
    安全模型OS 权限隔离沙箱
    流式输出支持不支持
    平台支持Unix/Linux/macOS任意
    适用场景开发/本地环境多租户/生产环境
    + +### 常见问题 + +**Q: 为什么需要绝对路径?** + +防止目录遍历攻击,使用 `filepath.Abs()` 转换。 + +**Q: Write 失败** + +文件已存在(安全特性)、路径非绝对路径、或权限不足。 + +**Q: GrepRaw 支持正则吗?** + +不支持,使用字面量匹配。需要正则可用 Execute 调用系统 grep。 + +**Q: Windows 支持吗?** + +不支持,依赖 `/bin/sh`。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md new file mode 100644 index 00000000000..003489c442f --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem/_index.md @@ -0,0 +1,237 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: FileSystem' +weight: 1 +--- + +> 💡 +> Package: [https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem](https://github.com/cloudwego/eino/tree/main/adk/middlewares/filesystem) + +# 概述 + +FileSystem Middleware 为 Agent 提供文件系统访问与大体积工具结果卸载能力。 +它定义了一套统一的文件系统 Backend 接口,用户可直接使用默认的内存实现,或根据自身需求扩展自定义后端。 + +核心功能包括: + +- 访问虚拟文件系统(ls/read/write/edit/glob/grep) +- 将超大工具结果自动卸载到文件系统,并在 Agent 上下文中保留摘要,按需加载内容 + +# Backend 接口 + +FileSystem Middleware 通过 `github.com/cloudwego/eino/adk/filesystem` 中的 Backend 接口操作文件系统: + +```go +type Backend interface { + // 列出指定路径下的文件(结构化信息) + LsInfo(path string) ([]FileInfo, error) + // 按 offset 和 limit 读取文件内容(返回适合 LLM 的文本) + Read(ctx context.Context, filePath string, offset, limit int) (string, error) + // 在指定路径中 grep pattern,返回匹配列表 + GrepRaw(ctx context.Context, pattern string, path, glob *string) ([]GrepMatch, error) + // 根据 pattern 和 path 进行 glob 匹配 + GlobInfo(ctx context.Context, pattern, path string) ([]FileInfo, error) + // 写入或更新文件 + Write(ctx context.Context, filePath, content string) error + // 替换文件中的字符串 + Edit(ctx context.Context, filePath, oldString, newString string, replaceAll bool) error +} +``` + +### **扩展接口** + +```go +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +`InMemoryBackend` 是 `Backend` 接口的内存实现,将文件存储在 map 中,支持并发安全访问。 + +```go +import "github.com/cloudwego/eino/adk/filesystem" + +ctx := context.Background() +backend := filesystem.NewInMemoryBackend() + +// 写入文件 +err := backend.Write(ctx, &filesystem.WriteRequest{ + FilePath: "/example/test.txt", + Content: "Hello, World!\nLine 2\nLine 3", +}) + +// 读取文件 +content, err := backend.Read(ctx, &filesystem.ReadRequest{ + FilePath: "/example/test.txt", + Offset: 1, + Limit: 10, +}) + +// 列出目录 +files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{ + Path: "/example", +}) + +// 搜索内容 +matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{ + Pattern: "Hello", + Path: "/example", +}) + +// 编辑文件 +err = backend.Edit(ctx, &filesystem.EditRequest{ + FilePath: "/example/test.txt", + OldString: "Hello", + NewString: "Hi", + ReplaceAll: false, +}) +``` + +其他 Backend 实现: + +# Filesystem Middleware + +Middleware 会自动向 Agent 注入一组工具及对应的 system prompt,使其能够直接操作文件系统。 + +### **创建中间件** + +推荐使用 `New` 函数创建中间件(返回 `ChatModelAgentMiddleware`): + +```go +import "github.com/cloudwego/eino/adk/middlewares/filesystem" + +middleware, err := filesystem.New(ctx, &filesystem.MiddlewareConfig{ + Backend: myBackend, + // 如果需要 shell 命令执行能力,设置 Shell 或 StreamingShell + Shell: myShell, +}) +if err != nil { + // handle error +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### **Config 配置项** + +```go +type MiddlewareConfig struct { + // Backend 提供文件系统操作,必填 + Backend filesystem.Backend + + // Shell 提供 shell 命令执行能力 + // 如果设置,会注册 execute 工具 + // 可选,与 StreamingShell 互斥 + Shell filesystem.Shell + + // StreamingShell 提供流式 shell 命令执行能力 + // 如果设置,会注册流式 execute 工具 + // 可选,与 Shell 互斥 + StreamingShell filesystem.StreamingShell + + // CustomSystemPrompt 覆盖默认的系统提示词 + // 可选,默认 ToolsSystemPrompt + CustomSystemPrompt *string + + // 以下为各工具的自定义名称,均为可选 + CustomLsToolName *string // 默认 "ls" + CustomReadFileToolName *string // 默认 "read_file" + CustomWriteFileToolName *string // 默认 "write_file" + CustomEditFileToolName *string // 默认 "edit_file" + CustomGlobToolName *string // 默认 "glob" + CustomGrepToolName *string // 默认 "grep" + CustomExecuteToolName *string // 默认 "execute" + + // 以下为各工具的自定义描述,均为可选 + CustomLsToolDesc *string + CustomReadFileToolDesc *string + CustomGrepToolDesc *string + CustomGlobToolDesc *string + CustomWriteFileToolDesc *string + CustomEditToolDesc *string + CustomExecuteToolDesc *string +} +``` + +> 💡 +> `New` 函数返回 `ChatModelAgentMiddleware`,提供更好的上下文传播能力。对于需要大型工具结果卸载的场景,请使用 `NewMiddleware` 函数或配合 ToolReduction middleware 使用。 + +注入的工具: + +这些工具均附带默认的英文描述(description)与内置提示词(system prompt)。如需切换为中文,可通过 `adk.SetLanguage()` 设置: + +``` +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // 切换为中文 +adk.SetLanguage(adk.LanguageEnglish) // 切换为英文(默认) +``` + +你也可以通过 `Config` 自定义说明文本和工具名称: + +```go +type MiddlewareConfig struct { + // 覆盖默认 System Prompt(可选) + CustomSystemPrompt *string + + // 覆盖各工具名称(可选) + CustomLsToolName *string + CustomReadFileToolName *string + // ... + + // 覆盖各工具 description(可选) + CustomLsToolDesc *string + CustomReadFileToolDesc *string + // ... +} +``` + +# [deprecated]工具结果卸载 + +> 💡 +> 该功能即将在 0.8.0 中 deprecate。迁移到 [Middleware: ToolReduction](/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction) + +当工具调用结果过大(例如读取大文件、grep 命中大量内容),如果继续将完整结果放入对话上下文,会导致: + +- token 急剧增加 +- Agent 历史上下文污染 +- 推理效率变差 + +为此,Middleware 提供了 自动卸载机制: + +- 当结果大小超过阈值(默认 20,000 tokens)时 + → 不直接返回全部内容给 LLM +- 实际结果会保存到文件系统 +- 上下文中仅包含: + - 摘要 + - 文件路径(agent 可再次调用工具读取) + + + +该功能默认启用,可通过配置调整行为: + +```go +type Config struct { + // other config... + + // 关闭自动卸载 + WithoutLargeToolResultOffloading bool + + // 自定义触发阈值(默认 20000 tokens) + LargeToolResultOffloadingTokenLimit int + + // 自定义卸载文件生成路径 + // 默认路径格式: /large_tool_result/{ToolCallID} + LargeToolResultOffloadingPathGen func(ctx context.Context, input *compose.ToolInput) (string, error) +} +``` diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md new file mode 100644 index 00000000000..3ea95bb80ca --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PatchToolCalls.md @@ -0,0 +1,159 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: PatchToolCalls' +weight: 7 +--- + +adk/middlewares/patchtoolcalls + +> 💡 +> PatchToolCalls 中间件用于修复消息历史中「悬空的工具调用」(dangling tool calls)问题。本中间件在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 概述 + +在多轮对话场景中,可能会出现 Assistant 消息包含工具调用(ToolCalls),但对话历史中缺少对应的 Tool 消息响应的情况。这种「悬空的工具调用」会导致某些模型 API 报错或产生异常行为。 + +**常见场景:** + +- 用户在工具执行完成前发送了新消息,导致工具调用被中断 +- 会话恢复时,部分工具调用结果丢失 +- Human-in-the-loop 场景下,用户取消了工具执行 + +PatchToolCalls 中间件会在每次模型调用前扫描消息历史,为缺少响应的工具调用自动插入占位符消息。 + +## 快速开始 + +```go +import ( + "context" + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/adk/middlewares/patchtoolcalls" +) + +// 使用默认配置创建中间件 +mw, err := patchtoolcalls.New(ctx, nil) +if err != nil { + // 处理错误 +} + +// 与 ChatModelAgent 一起使用 +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{mw}, +}) +``` + +## 配置项 + +```go +type Config struct { + // PatchedContentGenerator 自定义生成占位符消息内容的函数 + // 可选,不设置时使用默认消息 + PatchedContentGenerator func(ctx context.Context, toolName, toolCallID string) (string, error) +} +``` + + + + +
    字段类型必填说明
    PatchedContentGenerator
    func(ctx, toolName, toolCallID string) (string, error)
    自定义生成占位符消息内容的函数。参数包含工具名和调用 ID,返回要填充的内容
    + +### 默认占位符消息 + +如果不设置 `PatchedContentGenerator`,中间件会使用默认的占位符消息: + +**英文(默认):** + +``` +Tool call {toolName} with id {toolCallID} was cancelled - another message came in before it could be completed. +``` + +**中文:** + +``` +工具调用 {toolName}(ID 为 {toolCallID})已被取消——在其完成之前收到了另一条消息。 +``` + +可通过 `adk.SetLanguage()` 切换语言。 + +## 使用示例 + +### 自定义占位符消息 + +```go +mw, err := patchtoolcalls.New(ctx, &patchtoolcalls.Config{ + PatchedContentGenerator: func(ctx context.Context, toolName, toolCallID string) (string, error) { + return fmt.Sprintf("[系统提示] 工具 %s 的执行被跳过(调用ID: %s)", toolName, toolCallID), nil + }, +}) +``` + +### 结合其他中间件使用 + +```go +// PatchToolCalls 通常应该放在中间件链的前面 +// 确保在其他中间件处理消息之前修复悬空的工具调用 +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{ + patchToolCallsMiddleware, // 先修复消息 + summarizationMiddleware, // 再进行摘要 + reductionMiddleware, // 最后进行裁剪 + }, +}) +``` + +## 工作原理 + + + +**处理逻辑:** + +1. 在 `BeforeModelRewriteState` 钩子中执行 +2. 遍历所有消息,查找包含 `ToolCalls` 的 Assistant 消息 +3. 对于每个 ToolCall,检查后续消息中是否存在对应的 Tool 消息(通过 `ToolCallID` 匹配) +4. 如果找不到对应的 Tool 消息,则插入一个占位符消息 +5. 返回修复后的消息列表 + +## 示例场景 + +### 修复前的消息历史 + +``` +[User] "帮我查询天气" +[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}] +[Tool] "call_1: 晴天,25°C" +[User] "不用查位置了,直接告诉我北京的天气" <- 用户中断 +``` + +### 修复后的消息历史 + +``` +[User] "帮我查询天气" +[Assistant] ToolCalls: [{id: "call_1", name: "get_weather"}, {id: "call_2", name: "get_location"}] +[Tool] "call_1: 晴天,25°C" +[Tool] "call_2: 工具调用 get_location(ID 为 call_2)已被取消..." <- 自动插入 +[User] "不用查位置了,直接告诉我北京的天气" +``` + +## 多语言支持 + +占位符消息支持中英文,通过 `adk.SetLanguage()` 切换: + +```go +import "github.com/cloudwego/eino/adk" + +adk.SetLanguage(adk.LanguageChinese) // 中文 +adk.SetLanguage(adk.LanguageEnglish) // 英文(默认) +``` + +## 注意事项 + +> 💡 +> 此中间件仅在 `BeforeModelRewriteState` 钩子中修改本次运行的历史消息,不会影响实际存储的消息历史。修复只是临时的,仅用于本轮 agent 调用。 + +- 建议将此中间件放在中间件链的**前面**,确保其他中间件处理的是完整的消息历史 +- 如果你的场景需要持久化修复后的消息,请在 `PatchedContentGenerator` 中实现相应逻辑 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md new file mode 100644 index 00000000000..fb271214a58 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_PlanTask.md @@ -0,0 +1,292 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: PlanTask' +weight: 4 +--- + +# PlanTask 中间件 + +adk/middlewares/plantask + +> 💡 +> 本中间件在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 概述 + +`plantask` 是一个任务管理中间件,让 Agent 可以创建和管理任务列表。中间件通过 `BeforeAgent` 钩子注入四个工具: + +- **TaskCreate**: 创建任务 +- **TaskGet**: 查看任务详情 +- **TaskUpdate**: 更新任务 +- **TaskList**: 列出所有任务 + +主要用途: + +- 跟踪复杂任务的进度 +- 把大任务拆成小步骤 +- 管理任务间的依赖关系 + +--- + +## 架构 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Agent │ +│ │ +│ ┌───────────────────────────────────────────────────────────────────┐ │ +│ │ BeforeAgent: 注入任务工具 │ │ +│ │ - TaskCreate │ │ +│ │ - TaskGet │ │ +│ │ - TaskUpdate │ │ +│ │ - TaskList │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ Backend │ +│ │ +│ 存储结构: │ +│ baseDir/ │ +│ ├── .highwatermark # ID 计数器 │ +│ ├── 1.json # 任务 #1 │ +│ ├── 2.json # 任务 #2 │ +│ └── ... │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 配置 + +```go +type Config struct { + Backend Backend // 存储后端,必填 + BaseDir string // 任务文件目录,必填 +} +``` + +- 注意这个 Backend 的实现,应该是 session 维度隔离的,不同的 session 对应不同的 Backend(任务列表) + +--- + +## Backend 接口 + +```go +type Backend interface { + LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error) + Read(ctx context.Context, req *ReadRequest) (string, error) + Write(ctx context.Context, req *WriteRequest) error + Delete(ctx context.Context, req *DeleteRequest) error +} +``` + +--- + +## 任务结构 + +```go +type task struct { + ID string `json:"id"` // 任务 ID + Subject string `json:"subject"` // 标题 + Description string `json:"description"` // 描述 + Status string `json:"status"` // 状态 + Blocks []string `json:"blocks"` // 阻塞哪些任务 + BlockedBy []string `json:"blockedBy"` // 被哪些任务阻塞 + ActiveForm string `json:"activeForm"` // 进行时文案 + Owner string `json:"owner"` // 负责 agent + Metadata map[string]any `json:"metadata"` // 自定义数据 +} +``` + +### 状态 + + + + + + + +
    状态说明
    pending
    待处理(默认)
    in_progress
    进行中
    completed
    已完成
    deleted
    删除(会删掉文件)
    + +状态流转:`pending` → `in_progress` → `completed`,任何状态都可以直接 `deleted`。 + +--- + +## 工具 + +### TaskCreate + +创建任务。 + + + + + + + +
    参数类型必填说明
    subject
    string标题
    description
    string描述
    activeForm
    string进行时文案,比如"正在运行测试"
    metadata
    object自定义数据
    + +什么时候用: + +- 任务比较复杂,有 3 步以上 +- 用户给了一堆事情要做 +- 需要让用户看到进度 + +什么时候不用: + +- 就一个简单任务 +- 三两下就能搞定的事 + +### TaskGet + +查看任务详情。 + + + + +
    参数类型必填说明
    taskId
    string任务 ID
    + +返回任务的完整信息:标题、描述、状态、依赖关系等。 + +### TaskUpdate + +更新任务。 + + + + + + + + + + + + +
    参数类型必填说明
    taskId
    string任务 ID
    subject
    string新标题
    description
    string新描述
    activeForm
    string新的进行时文案
    status
    string新状态
    addBlocks
    []string添加被阻塞的任务
    addBlockedBy
    []string添加阻塞自己的任务
    owner
    string负责 agent
    metadata
    object自定义数据(设 null 删除)
    + +注意: + +- `status: "deleted"` 会直接删掉任务文件 +- 加依赖时会检查循环依赖 +- 所有任务都完成后会自动清理 + +### TaskList + +列出所有任务,不需要参数。 + +返回每个任务的摘要:ID、状态、标题、负责 agent、依赖关系。 + +--- + +## 使用示例 + +```go +ctx := context.Background() + +// plantask middleware 正常情况下应该 session 维度的 +// 不同的 session 对应不同的任务列表 +middleware, err := plantask.New(ctx, &plantask.Config{ + Backend: myBackend, + BaseDir: "/tasks", +}) +if err != nil { + return err +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: myModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### 典型流程 + +``` +1. 收到复杂任务 + │ + ▼ +2. TaskCreate 创建任务 + - #1: 分析需求 + - #2: 写代码 + │ + ▼ +3. TaskUpdate 设置依赖 + - #2 依赖 #1 + - #3 依赖 #2 + │ + ▼ +4. TaskList 看看有啥任务 + │ + ▼ +5. TaskUpdate 开始干活 + - #1 改成 in_progress + │ + ▼ +6. 干完了 TaskUpdate + - #1 改成 completed + │ + ▼ +7. 循环 4-6 直到全部完成 + │ + ▼ +8. 自动清理 +``` + +--- + +## 依赖管理 + +- **blocks**: 我完成了,这些任务才能开始 +- **blockedBy**: 这些任务完成了,我才能开始 + +``` +Task #1 (blocks: ["2"]) ────► Task #2 (blockedBy: ["1"]) + +#1 完成后 #2 才能开始 +``` + +循环依赖会报错: + +``` +#1 blocks #2 +#2 blocks #1 ← 不行,循环了 +``` + +--- + +## 自动清理 + +所有任务都 `completed` 后,会自动把任务文件都删掉。 + +--- + +## 注意事项 + +- 任务文件以 JSON 格式存储在 `BaseDir` 目录下,文件名为 `{id}.json` +- `.highwatermark` 文件用于记录已分配的最大任务 ID,确保 ID 不重复 +- 所有工具操作都有互斥锁保护,并发安全 +- 工具的 description 里已经包含了详细的使用指南,Agent 会根据这些指南来使用工具 + +--- + +## 多语言支持 + +工具的 description 支持中英文切换,通过 `adk.SetLanguage()` 设置: + +```go +// 使用中文 description +adk.SetLanguage(adk.LanguageChinese) + +// 使用英文 description(默认) +adk.SetLanguage(adk.LanguageEnglish) +``` + +这个设置是全局的,会影响所有 ADK 内置的 prompt 和工具 description。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md new file mode 100644 index 00000000000..a5dc9947a06 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Skill.md @@ -0,0 +1,503 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: Skill' +weight: 2 +--- + +Skill Middleware 为 Eino ADK Agent 提供了 Skill 支持,使 Agent 能够动态发现和使用预定义的技能来更准确、高效地完成任务。 + +# 什么是 Skill + +Skill 是包含指令、脚本和资源的文件夹,Agent 可以按需发现和使用这些 Skill 来扩展自身能力。 Skill 的核心是一个 `SKILL.md` 文件,包含元数据(至少需要 name 和 description)和指导 Agent 执行特定任务的说明。 + +``` +my-skill/ +├── SKILL.md # 必需:指令 + 元数据 +├── scripts/ # 可选:可执行代码 +├── references/ # 可选:参考文档 +└── assets/ # 可选:模板、资源 +``` + +Skill 使用**渐进式展示(Progressive Disclosure)**来高效管理上下文: + +1. **发现(Discovery)**:启动时,Agent 仅加载每个可用 Skill 的名称和描述,足以判断何时可能需要使用该 Skill +2. **激活(Activation)**:当任务匹配某个 Skill 的描述时,Agent 将完整的 `SKILL.md` 内容读入上下文 +3. **执行(Execution)**:Agent 遵循指令执行任务,也可以根据需要加载其他文件或执行捆绑的代码这种方式让 Agent 保持快速响应,同时能够按需访问更多上下文。 + +> 💡 +> Ref: [https://agentskills.io/home](https://agentskills.io/home) + +# 接口介绍 + +## FrontMatter + +Skill 的元数据结构,用于在发现阶段快速展示 Skill 信息,避免加载完整内容: + +```go +type FrontMatter struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Context ContextMode `yaml:"context"` + Agent string `yaml:"agent"` + Model string `yaml:"model"` +} +``` + + + + + + + + +
    字段类型说明
    Name
    string
    Skill 的唯一标识符。Agent 通过此名称调用 Skill ,建议使用简短、有意义的名称(如
    pdf-processing
    web-research
    )。对应 SKILL.md 中 frontmatter 的
    name
    字段
    Description
    string
    Skill 的功能描述。这是 Agent 判断是否使用该 Skill 的关键依据,应清晰说明技 Skill 能适用的场景和能力。对应 SKILL.md 中 frontmatter 的
    description
    字段
    Context
    ContextMode
    上下文模式。可选值:
    fork
    (复制历史消息创建新 Agent 执行)、
    isolate
    (隔离上下文创建新 Agent 执行)。留空表示内联模式(直接返回 Skill 内容)
    Agent
    string
    指定使用的 Agent 名称。配合
    Context
    字段使用,通过
    AgentHub
    获取对应的 Agent 工厂函数。留空时使用默认 Agent
    Model
    string
    指定使用的模型名称。通过
    ModelHub
    获取对应的模型实例。在 Context 模式下传递给 Agent 工厂;在内联模式下切换后续 ChatModel 调用使用的模型
    + +### ContextMode 上下文模式 + +```go +const ( + ContextModeFork ContextMode = "fork" // 复制历史消息 + ContextModeIsolate ContextMode = "isolate" // 隔离上下文 +) +``` + + + + + + +
    模式说明
    内联(默认)Skill 内容直接作为工具结果返回,由当前 Agent 继续处理
    Fork创建新 Agent,复制当前对话历史,独立执行 Skill 任务后返回结果
    Isolate创建新 Agent,使用隔离的上下文(仅包含 Skill 内容),独立执行后返回结果
    + +## Skill + +完整的 Skill 结构,包含元数据和实际指令内容: + +```go +type Skill struct { + FrontMatter + Content string + BaseDirectory string +} +``` + + + + + + +
    字段类型说明
    FrontMatter
    FrontMatter
    嵌入的元数据结构,包含
    Name
    Description
    Context
    Agent
    Model
    Content
    string
    SKILL.md 文件中 frontmatter 之后的正文内容。包含 Skill 的详细指令、工作流程、示例等,Agent 激活 Skill 后会读取此内容
    BaseDirectory
    string
    Skill 目录的绝对路径。Agent 可以使用此路径访问 Skill 目录中的其他资源文件(如脚本、模板、参考文档等)
    + +## Backend + +Skill 后端接口,定义了技能的检索方式。Backend 接口将技能的存储与使用解耦,提供以下优势: + +- **灵活的存储方式**:技能可以存储在本地文件系统、数据库、远程服务、云存储等任意位置 +- **可扩展性**:团队可以根据需求实现自定义 Backend,如从 Git 仓库动态加载、从配置中心获取等 +- **测试友好**:可以轻松创建 Mock Backend 进行单元测试 + +```go +type Backend interface { + List(ctx context.Context) ([]FrontMatter, error) + Get(ctx context.Context, name string) (Skill, error) +} +``` + + + + + +
    方法说明
    List
    列出所有可用技能的元数据。在 Agent 启动时调用,用于构建技能工具的描述信息,让 Agent 知道有哪些技能可用
    Get
    根据名称获取完整的技能内容。当 Agent 决定使用某个技能时调用,返回包含详细指令的完整 Skill 结构
    + +## AgentHub 和 ModelHub + +当 Skill 使用 Context 模式(fork/isolate)时,需要配置 AgentHub 和 ModelHub: + +```go +// AgentFactory 用于创建 Agent 实例 +type AgentFactory func(ctx context.Context, m model.ToolCallingChatModel) (adk.Agent, error) + +// AgentHub 提供 Agent 工厂函数 +type AgentHub interface { + Get(ctx context.Context, name string) (AgentFactory, error) +} + +// ModelHub 提供模型实例 +type ModelHub interface { + Get(ctx context.Context, name string) (model.ToolCallingChatModel, error) +} +``` + +### **NewBackendFromFilesystem** + +基于 `filesystem.Backend` 接口的后端实现,在指定的目录下读取技能: + +```go +type BackendFromFilesystemConfig struct { + Backend filesystem.Backend + BaseDir string +} + +func NewBackendFromFilesystem(ctx context.Context, config *BackendFromFilesystemConfig) (Backend, error) +``` + + + + + +
    字段类型必需说明
    Backend
    filesystem.Backend
    文件系统后端实现,用于文件操作
    BaseDir
    string
    技能根目录的路径。会扫描此目录下的所有一级子目录,查找包含
    SKILL.md
    文件的目录作为技能
    + +工作方式: + +- 扫描 `BaseDir` 下的一级子目录 +- 查找每个子目录中的 `SKILL.md` 文件 +- 解析 YAML frontmatter 获取元数据 +- 深层嵌套的 `SKILL.md` 文件会被忽略 + +### **filesystem.Backend 实现** + +`filesystem.Backend` 接口有以下两种实现可供选择: + +#### **Local Backend(本地文件系统)** + +基于本地文件系统的实现,适用于 Unix/MacOS 环境: + +```go +import "github.com/cloudwego/eino-ext/adk/backend/local" + +type Config struct { + ValidateCommand func(string) error // optional +} + +func NewBackend(ctx context.Context, cfg *Config) (filesystem.Backend, error) +``` + + + + +
    字段类型必需说明
    ValidateCommand
    func(string) error
    命令验证函数,用于在执行命令前进行安全校验
    + +> **注意**:仅支持 Unix/MacOS,不支持 Windows。 + +#### **Sandbox Backend(沙箱环境)** + +基于火山引擎 AgentKit 沙箱工具的实现,适用于需要隔离执行环境的场景: + +```go +import "github.com/cloudwego/eino-ext/adk/backend/agentkit" + +type Config struct { + AccessKeyID string + SecretAccessKey string + Region Region // 可选,默认 cn-beijing + ToolID string + SessionID string // 与 UserSessionID 至少提供一个 + UserSessionID string // 与 SessionID 至少提供一个 + SessionTTL int // 可选,默认 1800 秒 + ExecutionTimeout int // 可选 + HTTPClient *http.Client // 可选 +} + +func NewSandboxToolBackend(config *Config) (filesystem.Backend, error) +``` + + + + + + + + + + + +
    字段类型必需说明
    AccessKeyID
    string
    火山引擎 Access Key ID
    SecretAccessKey
    string
    火山引擎 Secret Access Key
    Region
    Region
    区域,支持
    cn-beijing
    (默认)和
    cn-shanghai
    ToolID
    string
    沙箱工具 ID
    SessionID
    string
    会话 ID,与 UserSessionID 至少提供一个
    UserSessionID
    string
    用户会话 ID,与 SessionID 至少提供一个
    SessionTTL
    int
    会话存活时间(秒),范围 60-86400,默认 1800
    ExecutionTimeout
    int
    代码执行超时时间(秒)
    + +> 更多信息请参考:[火山引擎 AgentKit 文档](https://www.volcengine.com/docs/86681/1847934) + +**LocalBackend** 内置的本地文件系统后端实现,在指定的目录下读取技能: + +```go +type LocalBackendConfig struct { + BaseDir string +} + +func NewLocalBackend(config *LocalBackendConfig) (*LocalBackend, error) +``` + + + + +
    字段类型必需说明
    BaseDir
    string
    技能根目录的路径。LocalBackend 会扫描此目录下的所有子目录,查找包含
    SKILL.md
    文件的目录作为技能
    + +工作方式: + +- 扫描 `BaseDir` 下的一级子目录 +- 查找每个子目录中的 `SKILL.md` 文件 +- 解析 YAML frontmatter 获取元数据 + +## 初始化 + +创建 Skill Middleware(推荐使用 `NewChatModelAgentMiddleware`): + +```go +func NewChatModelAgentMiddleware(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error) +``` + +Config 中配置为: + +```go +type Config struct { + // Backend 技能后端实现,必填 + Backend Backend + + // SkillToolName 技能工具名称,默认 "skill" + SkillToolName *string + + // AgentHub 提供 Agent 工厂函数,用于 Context 模式 + // 当 Skill 使用 "context: fork" 或 "context: isolate" 时必填 + AgentHub AgentHub + + // ModelHub 提供模型实例,用于 Skill 指定模型 + ModelHub ModelHub + + // CustomSystemPrompt 自定义系统提示词 + CustomSystemPrompt SystemPromptFunc + + // CustomToolDescription 自定义工具描述 + CustomToolDescription ToolDescriptionFunc +} +``` + + + + + + + + + +
    字段类型必需默认值说明
    Backend
    Backend
  • 技能后端实现。负责技能的存储和检索,可使用内置的
    LocalBackend
    或自定义实现
    SkillToolName
    *string
    "skill"
    技能工具的名称。Agent 通过此名称调用技能工具。如果你的 Agent 已有同名工具,可以通过此字段自定义名称避免冲突
    AgentHub
    AgentHub
  • 提供 Agent 工厂函数。当 Skill 使用
    context: fork
    context: isolate
    时必填
    ModelHub
    ModelHub
  • 提供模型实例。当 Skill 指定
    model
    字段时使用
    CustomSystemPrompt
    SystemPromptFunc
    内置提示词自定义系统提示词函数
    CustomToolDescription
    ToolDescriptionFunc
    内置描述自定义工具描述函数
    + +# 快速开始 + +以从本地加载 pdf skill 为例, 完整代码见 [https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill](https://github.com/cloudwego/eino-examples/tree/alpha/08/adk/middlewares/skill)。 + +- 在工作目录中创建 skills 目录: + +```go +workdir/ +├── skills/ +│ └── pdf/ +│ ├── scripts +│ │ └── analyze.py +│ └── SKILL.md +└── other files +``` + +- 创建本地 filesystem backend,基于 backend 创建 Skill middleware: + +```go +import ( + "github.com/cloudwego/eino/adk/middlewares/skill" + "github.com/cloudwego/eino-ext/adk/backend/local" +) + + +be, err := local.NewBackend(ctx, &local.Config{}) +if err != nil { + log.Fatal(err) +} + +wd, _ := os.Getwd() +workDir := filepath.Join(wd, "adk", "middlewares", "skill", "workdir") +skillBackend, err := skill.NewBackendFromFilesystem(ctx, &skill.BackendFromFilesystemConfig{ + Backend: be, + BaseDir: skillsDir, +}) +``` + +- 基于 backend 创建本地 Filesystem Middleware,供 agent 读取 skill 其他文件以及执行脚本: + +```go +import ( + "github.com/cloudwego/eino/adk/middlewares/filesystem" +) + +fsm, err := filesystem.NewMiddleware(ctx, &filesystem.Config{ + Backend: be, + WithoutLargeToolResultOffloading: true, +}) +``` + +- 创建 Agent 并配置 middlewares + +```go +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Name: "LogAnalysisAgent", + Description: "An agent that can analyze logs", + Instruction: "You are a helpful assistant.", + Model: cm, + Middlewares: []adk.ChatModelAgentMiddleware{fsm, skillMiddleware}, +}) +``` + +- 调用 Agent,观察结果 + +```go +runner := adk.NewRunner(ctx, adk.RunnerConfig{ + Agent: agent, +}) + +input := fmt.Sprintf("Analyze the %s file", filepath.Join(workDir, "test.log")) +log.Println("User: ", input) + +iterator := runner.Query(ctx, input) +for { + event, ok := iterator.Next() + if !ok { + break + } + if event.Err != nil { + log.Printf("Error: %v\n", event.Err) + break + } + + prints.Event(event) +} +``` + +agent 输出: + +```yaml +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool name: skill +arguments: {"skill":"log_analyzer"} + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool response: Launching skill: log_analyzer +Base directory for this skill: /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/skills/log_analyzer +# SKILL.md content + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool name: execute +arguments: {"command": "python3 /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/skills/log_analyzer/scripts/analyze.py /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/test.log"} + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +tool response: Analysis Result for /Users/bytedance/go/src/github.com/cloudwego/eino-examples/adk/middlewares/skill/workdir/test.log: +Total Errors: 2 +Total Warnings: 1 + +Error Details: +Line 3: [2024-05-20 10:02:15] ERROR: Database connection failed. +Line 5: [2024-05-20 10:03:05] ERROR: Connection timed out. + +Warning Details: +Line 2: [2024-05-20 10:01:23] WARNING: High memory usage detected. + + +name: LogAnalysisAgent +path: [{LogAnalysisAgent}] +answer: Here's the analysis result of the log file: + +### Summary +- **Total Errors**: 2 +- **Total Warnings**: 1 + +### Detailed Entries +#### Errors: +1. Line 3: [2024-05-20 10:02:15] ERROR: Database connection failed. +2. Line5: [2024-05-2010:03:05] ERROR: Connection timed out. + +#### Warnings: +1. Line2: [2024-05-2010:01:23] WARNING: High memory usage detected. + +The log file contains critical issues related to database connectivity and a warning about memory usage. Let me know if you need further analysis! +``` + +# 原理 + +Skill middleware 向 Agent 增加 system prompt 与 skill tool,system prompt 内容如下,{tool_name} 为 skill 工具的工具名: + +```python +# Skills System + +**How to Use Skills (Progressive Disclosure):** + +Skills follow a **progressive disclosure** pattern - you see their name and description above, but only read full instructions when needed: + +1. **Recognize when a skill applies**: Check if the user's task matches a skill's description +2. **Read the skill's full instructions**: Use the '{tool_name}' tool to load skill +3. **Follow the skill's instructions**: tool result contains step-by-step workflows, best practices, and examples +4. **Access supporting files**: Skills may include helper scripts, configs, or reference docs - use absolute paths + +**When to Use Skills:** +- User's request matches a skill's domain (e.g., "research X" -> web-research skill) +- You need specialized knowledge or structured workflows +- A skill provides proven patterns for complex tasks + +**Executing Skill Scripts:** +Skills may contain Python scripts or other executable files. Always use absolute paths. + +**Example Workflow:** + +User: "Can you research the latest developments in quantum computing?" + +1. Check available skills -> See "web-research" skill +2. Call '{tool_name}' tool to read the full skill instructions +3. Follow the skill's research workflow (search -> organize -> synthesize) +4. Use any helper scripts with absolute paths + +Remember: Skills make you more capable and consistent. When in doubt, check if a skill exists for the task! +``` + +Skill 工具接收需要加载 skill name,返回对应 SKILL.md 中的完整内容,在工具描述中告知 agent 所有可使用的 skill 的 name 和 description: + +```sql +Execute a skill within the main conversation + + +When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge. + +How to invoke: +- Use this tool with the skill name only (no arguments) +- Examples: + - `skill: pdf` - invoke the pdf skill + - `skill: xlsx` - invoke the xlsx skill + - `skill: ms-office-suite:pdf` - invoke using fully qualified name + +Important: +- When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action +- NEVER just announce or mention a skill in your text response without actually calling this tool +- This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task +- Only use skills listed in below +- Do not invoke a skill that is already running +- Do not use this tool for built-in CLI commands (like /help, /clear, etc.) + + + +{{- range .Matters }} + + +{{ .Name }} + + +{{ .Description }} + + +{{- end }} + +``` + +运行举例: + + + +> 💡 +> Skill Middleware 仅提供了如上图所示的加载 SKILL.md 能力,如果 Skill 本身需要读取文件、执行脚本等,需要用户另外为 agent 配置相关能力。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md new file mode 100644 index 00000000000..00fcd2f7c14 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_Summarization.md @@ -0,0 +1,210 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: Summarization' +weight: 3 +--- + +## 概述 + +Summarization 中间件会在对话的 token 数量超过配置阈值时,自动压缩对话历史。这有助于在长对话中保持上下文连续性,同时控制在模型的 token 限制范围内。 + +> 💡 +> 本中间件在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 快速开始 + +```go +import ( + "context" + "github.com/cloudwego/eino/adk/middlewares/summarization" +) + +// 使用最小配置创建中间件 +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, // 必填:用于生成摘要的模型 +}) +if err != nil { + // 处理错误 +} + +// 与 ChatModelAgent 一起使用 +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{mw}, +}) +``` + +## 配置项 + + + + + + + + + + + + + + +
    字段类型必填默认值说明
    Modelmodel.BaseChatModel
  • 用于生成摘要的聊天模型
    ModelOptions[]model.Option
  • 传递给模型生成摘要时的选项
    TokenCounterTokenCounterFunc约 4 字符/token自定义 token 计数函数
    Trigger*TriggerCondition190,000 tokens触发摘要的条件
    Instructionstring内置 prompt自定义摘要指令
    TranscriptFilePathstring
  • 完整对话记录文件路径
    PreparePrepareFunc
  • 自定义摘要生成前的预处理函数
    FinalizeFinalizeFunc
  • 自定义最终消息的后处理函数
    CallbackCallbackFunc
  • 在 Finalize 之后调用,用于观察状态变化(只读)
    EmitInternalEventsboolfalse是否发送内部事件
    PreserveUserMessages*PreserveUserMessagesEnabled: true是否在摘要中保留原始用户消息
    + +### TriggerCondition 结构 + +```go +type TriggerCondition struct { + // ContextTokens 当总 token 数量超过此阈值时触发摘要 + ContextTokens int +} +``` + +### PreserveUserMessages 结构 + +```go +type PreserveUserMessages struct { + // Enabled 是否启用保留用户消息功能 + Enabled bool + + // MaxTokens 保留用户消息的最大 token 数 + // 只保留最近的用户消息,直到达到此限制 + // 默认为 TriggerCondition.ContextTokens 的 1/3 + MaxTokens int +} +``` + +### 配置示例 + +**自定义 Token 阈值** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Trigger: &summarization.TriggerCondition{ + ContextTokens: 100000, // 在 100k tokens 时触发 + }, +}) +``` + +**自定义 Token 计数器** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + TokenCounter: func(ctx context.Context, input *summarization.TokenCounterInput) (int, error) { + // 使用你的 tokenizer + return yourTokenizer.Count(input.Messages) + }, +}) +``` + +**设置对话记录文件路径** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + TranscriptFilePath: "/path/to/transcript.txt", +}) +``` + +**自定义 Finalize 函数** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Finalize: func(ctx context.Context, originalMessages []adk.Message, summary adk.Message) ([]adk.Message, error) { + // 自定义逻辑构建最终消息 + return []adk.Message{ + schema.SystemMessage("你的系统提示词"), + summary, + }, nil + }, +}) +``` + +**使用 Callback 观察状态变化****/存储** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + Callback: func(ctx context.Context, before, after adk.ChatModelAgentState) error { + log.Printf("Summarization completed: %d messages -> %d messages", + len(before.Messages), len(after.Messages)) + return nil + }, +}) +``` + +**控制用户消息保留** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + PreserveUserMessages: &summarization.PreserveUserMessages{ + Enabled: true, + MaxTokens: 50000, // 保留最多 50k tokens 的用户消息 + }, +}) +``` + +## 工作原理 + +```mermaid +flowchart TD + A[BeforeModelRewriteState] --> B{Token 数量超过阈值?} + B -->|否| C[返回原始状态] + B -->|是| D[发送 BeforeSummary 事件] + D --> E{有自定义 Prepare?} + E -->|是| F[调用 Prepare] + E -->|否| G[调用模型生成摘要] + F --> G + G --> H{有自定义 Finalize?} + H -->|是| I[调用 Finalize] + H -->|否| L{有自定义 Callback?} + I --> L + L -->|是| M[调用 Callback] + L -->|否| J[发送 AfterSummary 事件] + M --> J + J --> K[返回新状态] + + style A fill:#e3f2fd + style G fill:#fff3e0 + style D fill:#e8f5e9 + style J fill:#e8f5e9 + style K fill:#c8e6c9 + style C fill:#f5f5f5 + style M fill:#fce4ec + style F fill:#fff3e0 + style I fill:#fff3e0 +``` + +## 内部事件 + +当 EmitInternalEvents 设置为 true 时,中间件会在关键节点发送事件: + + + + + +
    事件类型触发时机携带数据
    ActionTypeBeforeSummary生成摘要之前原始消息列表
    ActionTypeAfterSummary完成总结之后最终消息列表
    + +**使用示例** + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: yourChatModel, + EmitInternalEvents: true, +}) + +// 在你的事件处理器中监听事件 +``` + +## 最佳实践 + +1. **设置 TranscriptFilePath**:建议始终提供对话记录文件路径,以便模型在需要时可以参考原始对话。 +2. **调整 Token 阈值**:根据模型的上下文窗口大小调整 `Trigger.MaxTokens`。一般建议设置为模型限制的 80-90%。 +3. **自定义 Token 计数器**:在生产环境中,建议实现与模型 tokenizer 匹配的自定义 `TokenCounter`,以获得准确的计数。 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md new file mode 100644 index 00000000000..34b2cebbcbe --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolReduction.md @@ -0,0 +1,317 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: ToolReduction' +weight: 6 +--- + +# ToolReduction 中间件 + +adk/middlewares/reduction + +> 💡 +> 本中间件在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 概述 + +`reduction` 中间件用来控制工具结果占用的 token 数量,提供两种策略: + +1. **截断 (Truncation)**:工具返回时立即截断过长的输出,将完整内容保存到 Backend +2. **清理 (Clear)**:总 token 超过阈值时,把旧的工具结果存到文件系统 + +--- + +## 架构 + +``` +Tool 调用返回结果 + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ WrapInvokableToolCall / WrapStreamableToolCall │ +│ │ +│ Truncation 策略(可跳过) │ +│ 结果长度 > MaxLengthForTrunc? │ +│ 是 → 截断内容,完整内容存到 Backend │ +│ 否 → 原样返回 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ + 结果加入 Messages + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ BeforeModelRewriteState │ +│ │ +│ Clear 策略(可跳过) │ +│ 总 token > MaxTokensForClear? │ +│ 是 → 把旧的工具结果存到 Backend,替换成文件路径 │ +│ 否 → 不处理 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ + 调用 Model +``` + +--- + +## 配置 + +### Config 主配置 + +```go +type Config struct { + // Backend 存储后端,用于保存截断/清理的内容 + // 当 SkipTruncation 为 false 时必填 + Backend Backend + + // SkipTruncation 跳过截断阶段 + SkipTruncation bool + + // SkipClear 跳过清理阶段 + SkipClear bool + + // ReadFileToolName 读取文件的工具名 + // 内容卸载到文件后,agent 需要使用此工具读取 + // 默认 "read_file" + ReadFileToolName string + + // RootDir 保存内容的根目录 + // 默认 "/tmp" + // 截断内容保存到 {RootDir}/trunc/{tool_call_id} + // 清理内容保存到 {RootDir}/clear/{tool_call_id} + RootDir string + + // MaxLengthForTrunc 触发截断的最大长度 + // 默认 50000 + MaxLengthForTrunc int + + // TokenCounter token 计数器 + // 用于判断是否需要触发清理 + // 默认使用 字符数/4 估算 + TokenCounter func(ctx context.Context, msg []adk.Message, tools []*schema.ToolInfo) (int64, error) + + // MaxTokensForClear 触发清理的 token 阈值 + // 默认 30000 + MaxTokensForClear int64 + + // ClearRetentionSuffixLimit 保留最近多少轮对话不清理 + // 默认 1 + ClearRetentionSuffixLimit int + + // ClearPostProcess 清理完成后的回调 + // 可用于保存或通知当前状态 + ClearPostProcess func(ctx context.Context, state *adk.ChatModelAgentState) context.Context + + // ToolConfig 针对特定工具的配置 + // 优先级高于全局配置 + ToolConfig map[string]*ToolReductionConfig +} +``` + +### ToolReductionConfig 工具级配置 + +```go +type ToolReductionConfig struct { + // Backend 此工具使用的存储后端 + Backend Backend + + // SkipTruncation 跳过此工具的截断 + SkipTruncation bool + + // TruncHandler 自定义截断处理器 + // 不设置时使用默认处理器 + TruncHandler func(ctx context.Context, detail *ToolDetail) (*TruncResult, error) + + // SkipClear 跳过此工具的清理 + SkipClear bool + + // ClearHandler 自定义清理处理器 + // 不设置时使用默认处理器 + ClearHandler func(ctx context.Context, detail *ToolDetail) (*ClearResult, error) +} +``` + +### ToolDetail 工具详情 + +```go +type ToolDetail struct { + // ToolContext 工具元信息(工具名、调用 ID) + ToolContext *adk.ToolContext + + // ToolArgument 输入参数 + ToolArgument *schema.ToolArgument + + // ToolResult 输出结果 + ToolResult *schema.ToolResult +} +``` + +### TruncResult 截断结果 + +```go +type TruncResult struct { + // NeedTrunc 是否需要截断 + NeedTrunc bool + + // ToolResult 截断后的工具结果 + // NeedTrunc 为 true 时必填 + ToolResult *schema.ToolResult + + // NeedOffload 是否需要卸载到存储 + NeedOffload bool + + // OffloadFilePath 卸载文件路径 + // NeedOffload 为 true 时必填 + OffloadFilePath string + + // OffloadContent 卸载内容 + // NeedOffload 为 true 时必填 + OffloadContent string +} +``` + +### ClearResult 清理结果 + +```go +type ClearResult struct { + // NeedClear 是否需要清理 + NeedClear bool + + // ToolArgument 清理后的工具参数 + // NeedClear 为 true 时必填 + ToolArgument *schema.ToolArgument + + // ToolResult 清理后的工具结果 + // NeedClear 为 true 时必填 + ToolResult *schema.ToolResult + + // NeedOffload 是否需要卸载到存储 + NeedOffload bool + + // OffloadFilePath 卸载文件路径 + // NeedOffload 为 true 时必填 + OffloadFilePath string + + // OffloadContent 卸载内容 + // NeedOffload 为 true 时必填 + OffloadContent string +} +``` + +--- + +## 创建中间件 + +### 基本用法 + +```go +import ( + "context" + "github.com/cloudwego/eino/adk/middlewares/reduction" +) + +// 使用默认配置 +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, // 必填:存储后端 +}) + +// 与 ChatModelAgent 一起使用 +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: yourChatModel, + Middlewares: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +### 自定义配置 + +```go +config := &reduction.Config{ + Backend: myBackend, + RootDir: "/data/agent", + MaxLengthForTrunc: 30000, + MaxTokensForClear: 100000, + ClearRetentionSuffixLimit: 2, + TokenCounter: myTokenCounter, + ClearPostProcess: func(ctx context.Context, state *adk.ChatModelAgentState) context.Context { + log.Printf("Clear completed, messages: %d", len(state.Messages)) + return ctx + }, + ToolConfig: map[string]*reduction.ToolReductionConfig{ + "grep": { + Backend: grepBackend, + SkipTruncation: false, + }, + "read_file": { + Backend: readFileBackend, + SkipClear: true, // 读文件工具不需要清理 + }, + }, +} + +middleware, err := reduction.New(ctx, config) +``` + +### 仅使用截断策略 + +```go +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, + SkipClear: true, // 跳过清理阶段 +}) +``` + +### 仅使用清理策略 + +```go +middleware, err := reduction.New(ctx, &reduction.Config{ + Backend: myBackend, + SkipTruncation: true, // 跳过截断阶段 +}) +``` + +--- + +## 工作原理 + +### Truncation(截断) + +在 `WrapInvokableToolCall` / `WrapStreamableToolCall` 中处理: + +1. 工具返回结果 +2. 调用 TruncHandler 判断是否需要截断 +3. 如需截断,将完整内容存到 Backend +4. 返回截断后的内容,包含提示文字告知 agent 完整内容的位置 + +### Clear(清理) + +在 `BeforeModelRewriteState` 中处理: + +1. 用 TokenCounter 计算总 token +2. 超过 MaxTokensForClear 才处理 +3. 从旧消息开始遍历,跳过已处理的和最近 ClearRetentionSuffixLimit 轮 +4. 对范围内的每个工具调用,调用 ClearHandler +5. 需要清理的,写入 Backend,把消息里的结果替换成文件路径 +6. 调用 ClearPostProcess 回调 + +--- + +## 多语言支持 + +截断和清理的提示文字支持中英文,通过 `adk.SetLanguage()` 切换: + +```go +adk.SetLanguage(adk.LanguageChinese) // 中文 +adk.SetLanguage(adk.LanguageEnglish) // 英文(默认) +``` + +--- + +## 注意事项 + +- 当 `SkipTruncation` 为 false 时,`Backend` 必须设置 +- 默认 TokenCounter 用 `字符数 / 4` 估算,对于中文不精准,建议使用 `github.com/tiktoken-go/tokenizer` 替换 +- 已处理过的消息会打标记,不会重复处理 +- `ToolConfig` 中的配置优先级高于全局配置 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md new file mode 100644 index 00000000000..c6946aa7880 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_ToolSearch.md @@ -0,0 +1,145 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Middleware: ToolSearch' +weight: 5 +--- + +# ToolSearch 中间件 + +adk/middlewares/dynamictool/toolsearch + +> 💡 +> 本中间件在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 概述 + +`toolsearch` 中间件实现动态工具选择。当工具库很大时,把所有工具都传给模型会撑爆上下文。这个中间件的做法是: + +1. 添加一个 `tool_search` 元工具,接受正则表达式搜索工具名 +2. 初始时隐藏所有动态工具 +3. 模型调用 `tool_search` 后,匹配的工具才会出现在后续调用中 + +--- + +## 架构 + +``` +Agent 初始化 + │ + ▼ +┌───────────────────────────────────────────┐ +│ BeforeAgent │ +│ - 注入 tool_search 工具 │ +│ - 把 DynamicTools 加到 Tools 列表 │ +└───────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────┐ +│ WrapModel │ +│ 每次 Model 调用前: │ +│ 1. 扫描消息历史,找到历史中所有 tool_search 的返回结果。 │ +│ 2. 全量 Tools 减去未被选中的 DynamicTools,作为本次 Model 调用的工具列表。 │ +└────────────────────────────────────────────┘ + │ + ▼ + Model 调用 +``` + +--- + +## 配置 + +```go +type Config struct { + // 可动态搜索和加载的工具列表 + DynamicTools []tool.BaseTool +} +``` + +--- + +## tool_search 工具 + +中间件注入的工具。 + +**参数:** + + + + +
    参数类型必填说明
    regex_pattern
    string匹配工具名的正则表达式
    + +**返回:** + +```json +{ + "selectedTools": ["tool_a", "tool_b"] +} +``` + +--- + +## 使用示例 + +```go +middleware, err := toolsearch.New(ctx, &toolsearch.Config{ + DynamicTools: []tool.BaseTool{ + weatherTool, + stockTool, + currencyTool, + // ... 很多工具 + }, +}) +if err != nil { + return err +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: myModel, + Handlers: []adk.ChatModelAgentMiddleware{middleware}, +}) +``` + +--- + +## 工作原理 + +### BeforeAgent + +1. 获取所有 DynamicTool +2. 使用 DynamicTools 创建 `tool_search` 工具 +3. 把 `tool_search` 和所有 DynamicTools 加到 `runCtx.Tools`,此时 Agent 中的 Tools 为全量 + +### WrapModel + +每次 Model 调用前: + +1. 遍历消息历史,找所有 `tool_search` 的返回结果 +2. 收集已选中的工具名 +3. 从全量工具中过滤掉未选中的 DynamicTools +4. 用过滤后的工具列表调用 Model + +### 工具选择流程 + +``` +第一轮: + Model 只能看到 tool_search + Model 调用 tool_search(regex_pattern="weather.*") + 返回 {"selectedTools": ["weather_forecast", "weather_history"]} + +第二轮: + Model 能看到 tool_search + weather_forecast + weather_history + Model 调用 weather_forecast(...) +``` + +--- + +## 注意事项 + +- DynamicTools 不能为空 +- 正则匹配的是工具名,不是描述 +- 选中的工具会一直保持可用,除非 tool_search 调用结果被删除或修改 +- 可以多次调用 tool_search,结果会累加 diff --git a/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md new file mode 100644 index 00000000000..35d45628e5a --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/_index.md @@ -0,0 +1,521 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino ADK: ChatModelAgentMiddleware' +weight: 8 +--- + +## 概述 + +## ChatModelAgentMiddleware 接口 + +`ChatModelAgentMiddleware` 定义了自定义 `ChatModelAgent` 行为的接口。 + +**重要说明:** 此接口专为 `ChatModelAgent` 及基于它构建的 Agent(如 `DeepAgent`)设计。 + +> 💡 +> ChatModelAgentMiddleware 接口在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入 + +### 为什么使用 ChatModelAgentMiddleware 而非 AgentMiddleware? + + + + + + +
    特性AgentMiddleware (结构体)ChatModelAgentMiddleware (接口)
    扩展性封闭,用户无法添加新方法开放,用户可实现自定义 handler
    Context 传播回调只返回 error所有方法返回
    (context.Context, ..., error)
    配置管理分散在闭包中集中在结构体字段中
    + +### 接口定义 + +```go +type ChatModelAgentMiddleware interface { + // BeforeAgent 在每次 agent 运行前调用,允许修改 instruction 和 tools 配置 + BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) + + // BeforeModelRewriteState 在每次模型调用前调用 + // 返回的 state 会被持久化到 agent 内部状态并传递给模型 + // 返回的 context 会传播到模型调用和后续 handler + BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + + // AfterModelRewriteState 在每次模型调用后调用 + // 输入的 state 包含模型响应作为最后一条消息 + AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + + // WrapInvokableToolCall 用自定义行为包装工具的同步执行 + // 如果不需要包装,返回原始 endpoint 和 nil error + // 仅对实现了 InvokableTool 的工具调用此方法 + WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error) + + // WrapStreamableToolCall 用自定义行为包装工具的流式执行 + // 如果不需要包装,返回原始 endpoint 和 nil error + // 仅对实现了 StreamableTool 的工具调用此方法 + WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error) + + // WrapEnhancedInvokableToolCall 用自定义行为包装增强型工具的同步执行 + WrapEnhancedInvokableToolCall(ctx context.Context, endpoint EnhancedInvokableToolCallEndpoint, tCtx *ToolContext) (EnhancedInvokableToolCallEndpoint, error) + + // WrapEnhancedStreamableToolCall 用自定义行为包装增强型工具的流式执行 + WrapEnhancedStreamableToolCall(ctx context.Context, endpoint EnhancedStreamableToolCallEndpoint, tCtx *ToolContext) (EnhancedStreamableToolCallEndpoint, error) + + // WrapModel 用自定义行为包装聊天模型 + // 如果不需要包装,返回原始 model 和 nil error + // 在请求时调用,每次模型调用前都会执行 + WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) +} +``` + +### 使用 BaseChatModelAgentMiddleware + +嵌入 `*BaseChatModelAgentMiddleware` 以获得默认的空操作实现: + +```go +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) BeforeModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + return ctx, state, nil +} +``` + +--- + +## 工具调用端点类型 + +工具包装使用函数类型而非接口,更清晰地表达了包装的意图: + +```go +// InvokableToolCallEndpoint 是同步工具调用的函数签名 +type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) + +// StreamableToolCallEndpoint 是流式工具调用的函数签名 +type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error) + +// EnhancedInvokableToolCallEndpoint 是增强型同步工具调用的函数签名 +type EnhancedInvokableToolCallEndpoint func(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.ToolResult, error) + +// EnhancedStreamableToolCallEndpoint 是增强型流式工具调用的函数签名 +type EnhancedStreamableToolCallEndpoint func(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.StreamReader[*schema.ToolResult], error) +``` + +### 为什么使用分离的端点类型? + +之前的 `ToolCall` 接口同时包含 `InvokableRun` 和 `StreamableRun`,但大多数工具只实现其中一个。 +分离的端点类型使得: + +- 只有当工具实现相应接口时才调用对应的包装方法 +- wrapper 作者更清晰的契约 +- 关于实现哪个方法没有歧义 + +--- + +## ChatModelAgentContext + +`ChatModelAgentContext` 包含在每次 `ChatModelAgent` 运行前传递给 handler 的运行时信息。 + +```go +type ChatModelAgentContext struct { + // Instruction 是当前 Agent 执行的指令 + // 包括 agent 配置的指令、框架和 AgentMiddleware 追加的额外指令, + // 以及之前 BeforeAgent handler 应用的修改 + Instruction string + + // Tools 是当前为 Agent 执行配置的原始工具(无任何 wrapper 或 tool middleware) + // 包括 AgentConfig 中传入的工具、框架隐式添加的工具(如 transfer/exit 工具), + // 以及 middleware 已添加的其他工具 + Tools []tool.BaseTool + + // ReturnDirectly 是当前配置为使 Agent 直接返回的工具名称集合 + ReturnDirectly map[string]bool +} +``` + +--- + +## ChatModelAgentState + +`ChatModelAgentState` 表示对话过程中聊天模型 agent 的状态。这是 `ChatModelAgentMiddleware` 和 `AgentMiddleware` 回调的主要状态类型。 + +```go +type ChatModelAgentState struct { + // Messages 包含当前对话会话中的所有消息 + Messages []Message +} +``` + +--- + +## ToolContext + +`ToolContext` 提供被包装工具的元数据。在请求时创建,包含当前工具调用的信息。 + +```go +type ToolContext struct { + // Name 是工具名称 + Name string + + // CallID 是此特定工具调用的唯一标识符 + CallID string +} +``` + +### 使用示例:工具调用包装 + +```go +func (h *MyHandler) WrapInvokableToolCall(ctx context.Context, endpoint adk.InvokableToolCallEndpoint, tCtx *adk.ToolContext) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + log.Printf("Tool %s (call %s) starting with args: %s", tCtx.Name, tCtx.CallID, argumentsInJSON) + + result, err := endpoint(ctx, argumentsInJSON, opts...) + + if err != nil { + log.Printf("Tool %s failed: %v", tCtx.Name, err) + return "", err + } + + log.Printf("Tool %s completed with result: %s", tCtx.Name, result) + return result, nil + }, nil +} +``` + +--- + +## ModelContext + +`ModelContext` 包含传递给 `WrapModel` 的上下文信息。在请求时创建,包含当前模型调用的工具配置。 + +```go +type ModelContext struct { + // Tools 是当前配置给 agent 的工具列表 + // 在请求时填充,包含将发送给模型的工具 + Tools []*schema.ToolInfo + + // ModelRetryConfig 包含模型的重试配置 + // 在请求时从 agent 的 ModelRetryConfig 填充 + // 用于 EventSenderModelWrapper 适当地包装流错误 + ModelRetryConfig *ModelRetryConfig +} +``` + +### 使用示例:模型包装 + +```go +func (h *MyHandler) WrapModel(ctx context.Context, m model.BaseChatModel, mc *adk.ModelContext) (model.BaseChatModel, error) { + return &myModelWrapper{ + inner: m, + tools: mc.Tools, + }, nil +} + +type myModelWrapper struct { + inner model.BaseChatModel + tools []*schema.ToolInfo +} + +func (w *myModelWrapper) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) { + log.Printf("Model called with %d tools", len(w.tools)) + return w.inner.Generate(ctx, msgs, opts...) +} + +func (w *myModelWrapper) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { + return w.inner.Stream(ctx, msgs, opts...) +} +``` + +--- + +## 运行时本地存储 API + +`SetRunLocalValue`、`GetRunLocalValue` 和 `DeleteRunLocalValue` 提供在当前 agent Run() 调用期间存储、获取和删除值的能力。 + +```go +// SetRunLocalValue 设置一个在当前 agent Run() 调用期间持久化的键值对 +// 值的作用域限于此特定执行,不会在不同的 Run() 调用或 agent 实例之间共享 +// +// 存储在这里的值与中断/恢复周期兼容 - 它们会被序列化并在 agent 恢复时还原 +// 对于自定义类型,必须在 init() 函数中使用 schema.RegisterName[T]() 注册以确保正确序列化 +// +// 此函数只能在 agent 执行期间从 ChatModelAgentMiddleware 内部调用 +// 如果在 agent 执行上下文之外调用,返回错误 +func SetRunLocalValue(ctx context.Context, key string, value any) error + +// GetRunLocalValue 获取在当前 agent Run() 调用期间设置的值 +// 值的作用域限于此特定执行,不会在不同的 Run() 调用或 agent 实例之间共享 +// +// 通过 SetRunLocalValue 存储的值与中断/恢复周期兼容 - 它们会被序列化并在 agent 恢复时还原 +// 对于自定义类型,必须在 init() 函数中使用 schema.RegisterName[T]() 注册以确保正确序列化 +// +// 此函数只能在 agent 执行期间从 ChatModelAgentMiddleware 内部调用 +// 如果找到值返回 (value, true, nil),如果未找到返回 (nil, false, nil), +// 如果在 agent 执行上下文之外调用返回错误 +func GetRunLocalValue(ctx context.Context, key string) (any, bool, error) + +// DeleteRunLocalValue 删除在当前 agent Run() 调用期间设置的值 +// +// 此函数只能在 agent 执行期间从 ChatModelAgentMiddleware 内部调用 +// 如果在 agent 执行上下文之外调用,返回错误 +func DeleteRunLocalValue(ctx context.Context, key string) error +``` + +### 使用示例:跨 handler 点共享数据 + +```go +func init() { + schema.RegisterName[*MyCustomData]("my_package.MyCustomData") +} + +type MyCustomData struct { + Count int + Name string +} + +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) WrapInvokableToolCall(ctx context.Context, endpoint adk.InvokableToolCallEndpoint, tCtx *adk.ToolContext) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + result, err := endpoint(ctx, argumentsInJSON, opts...) + + data := &MyCustomData{Count: 1, Name: tCtx.Name} + if err := adk.SetRunLocalValue(ctx, "my_handler.last_tool", data); err != nil { + log.Printf("Failed to set run local value: %v", err) + } + + return result, err + }, nil +} + +func (h *MyHandler) AfterModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + if val, found, err := adk.GetRunLocalValue(ctx, "my_handler.last_tool"); err == nil && found { + if data, ok := val.(*MyCustomData); ok { + log.Printf("Last tool was: %s (count: %d)", data.Name, data.Count) + } + } + return ctx, state, nil +} +``` + +--- + +## SendEvent API + +`SendEvent` 允许在 agent 执行期间向事件流发送自定义 `AgentEvent`。 + +```go +// SendEvent 在 agent 执行期间向事件流发送自定义 AgentEvent +// 允许 ChatModelAgentMiddleware 实现发出自定义事件, +// 这些事件将被遍历 agent 事件流的调用者接收 +// +// 此函数只能在 agent 执行期间从 ChatModelAgentMiddleware 内部调用 +// 如果在 agent 执行上下文之外调用,返回错误 +func SendEvent(ctx context.Context, event *AgentEvent) error +``` + +--- + +## State 类型(即将弃用) + +`State` 保存 agent 运行时状态,包括消息和用户可扩展存储。 + +**⚠️ 弃用警告:** 此类型将在 v1.0.0 中设为未导出。请在 `ChatModelAgentMiddleware` 和 `AgentMiddleware` 回调中使用 `ChatModelAgentState`。不建议直接使用 `compose.ProcessState[*State]`,该用法将在 v1.0.0 中停止工作;请使用 handler API。 + +```go +type State struct { + Messages []Message + extra map[string]any // 未导出,通过 SetRunLocalValue/GetRunLocalValue 访问 + + // 以下为内部字段 - 请勿直接访问 + // 为与现有 checkpoint 向后兼容而保持导出 + ReturnDirectlyToolCallID string + ToolGenActions map[string]*AgentAction + AgentName string + RemainingIterations int + + internals map[string]any +} +``` + +--- + +## 架构图 + +下图展示了 `ChatModelAgentMiddleware` 在 `ChatModelAgent` 执行过程中的工作原理: + +``` +Agent.Run(input) + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ BeforeAgent(ctx, *ChatModelAgentContext) │ +│ 输入: 当前 Instruction、Tools 等 Agent 运行环境 │ +│ 输出: 修改后的 Agent 运行环境 │ +│ 作用: Run 开始时调用一次,修改整个 Run 生命周期的配置 │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ ReAct Loop │ +│ ┌───────────────────────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ BeforeModelRewriteState(ctx, *ChatModelAgentState, *MC) │ │ │ +│ │ │ 输入: 消息历史等持久化状态,以及 Model 运行环境 │ │ │ +│ │ │ 输出: 修改后的持久化状态,返回新 ctx │ │ │ +│ │ │ 作用: 修改跨 iteration 的持久化状态(主要是消息列表) │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ WrapModel(ctx, BaseChatModel, *ModelContext) │ │ │ +│ │ │ 输入: 被 wrap 的 ChatModel,以及 Model 运行环境 │ │ │ +│ │ │ 输出: 包装后的 Model (洋葱模型) │ │ │ +│ │ │ 作用: 修改单次 Model 请求的输入、输出和配置 │ │ │ +│ │ │ │ │ │ │ +│ │ │ ▼ │ │ │ +│ │ │ ┌───────────────┐ │ │ │ +│ │ │ │ Model │ │ │ │ +│ │ │ │ Generate/Stream│ │ │ │ +│ │ │ └───────────────┘ │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────────────────────────────────────────────────────┐ │ │ +│ │ │ AfterModelRewriteState(ctx, *ChatModelAgentState, *MC) │ │ │ +│ │ │ 输入: 消息历史等持久化状态(含 Model 响应), │ │ │ +│ │ │ 以及 Model 运行环境 │ │ │ +│ │ │ 输出: 修改后的持久化状态 │ │ │ +│ │ │ 作用: 修改跨 iteration 的持久化状态(主要是消息列表) │ │ │ +│ │ └─────────────────────────────────────────────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌──────────────────┐ │ │ +│ │ │ Model 返回内容? │ │ │ +│ │ └──────────────────┘ │ │ +│ │ │ │ │ │ +│ │ 最终响应 │ │ ToolCalls │ │ +│ │ │ ▼ │ │ +│ │ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ │ WrapInvokableToolCall / WrapStream │ │ │ +│ │ │ │ ableToolCall(ctx, endpoint, *TC) │ │ │ +│ │ │ │ 输入: 被 wrap 的 Tool 以及 │ │ │ +│ │ │ │ Tool 运行环境 │ │ │ +│ │ │ │ 输出: 包装后的 endpoint (洋葱模型)│ │ │ +│ │ │ │ 作用: 修改单次 Tool 请求的 │ │ │ +│ │ │ │ 输入、输出和配置 │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ ▼ │ │ │ +│ │ │ │ ┌─────────────┐ │ │ │ +│ │ │ │ │ Tool.Run() │ │ │ │ +│ │ │ │ └─────────────┘ │ │ │ +│ │ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ │ │ +│ │ │ │ (结果加入 Messages) │ │ +│ │ │ │ │ │ +│ │ │ ┌─────────┘ │ │ +│ │ │ │ │ │ +│ │ │ └──────────► 继续循环 │ │ +│ │ │ │ │ +│ └─────────────────────┼─────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ 循环直到完成或达到 maxIterations │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ + Agent.Run() 结束 +``` + +### Handler 方法说明 + + + + + + + + + +
    方法输入输出作用范围
    BeforeAgent
    Agent 运行环境 (
    *ChatModelAgentContext
    )
    修改后的 Agent 运行环境整个 Run 生命周期,仅调用一次
    BeforeModelRewriteState
    持久化状态 + Model 运行环境修改后的持久化状态跨 iteration 的持久化状态(消息列表)
    WrapModel
    被 wrap 的 ChatModel + Model 运行环境包装后的 Model单次 Model 请求的输入、输出和配置
    AfterModelRewriteState
    持久化状态(含响应)+ Model 运行环境修改后的持久化状态跨 iteration 的持久化状态(消息列表)
    WrapInvokableToolCall
    被 wrap 的 Tool + Tool 运行环境包装后的 endpoint单次 Tool 请求的输入、输出和配置
    WrapStreamableToolCall
    被 wrap 的 Tool + Tool 运行环境包装后的 endpoint单次 Tool 请求的输入、输出和配置
    + +--- + +## 执行顺序 + +### Model 调用生命周期(从外到内的 wrapper 链) + +1. `AgentMiddleware.BeforeChatModel`(hook,在模型调用前运行) +2. `ChatModelAgentMiddleware.BeforeModelRewriteState`(hook,可在模型调用前修改状态) +3. `retryModelWrapper`(内部 - 失败时重试,如已配置) +4. `eventSenderModelWrapper` 预处理(内部 - 准备事件发送) +5. `ChatModelAgentMiddleware.WrapModel` 预处理(wrapper,在请求时包装,先注册的先运行) +6. `callbackInjectionModelWrapper`(内部 - 如未启用则注入回调) +7. `Model.Generate/Stream` +8. `callbackInjectionModelWrapper` 后处理 +9. `ChatModelAgentMiddleware.WrapModel` 后处理(wrapper,先注册的后运行) +10. `eventSenderModelWrapper` 后处理(内部 - 发送模型响应事件) +11. `retryModelWrapper` 后处理(内部 - 处理重试逻辑) +12. `ChatModelAgentMiddleware.AfterModelRewriteState`(hook,可在模型调用后修改状态) +13. `AgentMiddleware.AfterChatModel`(hook,在模型调用后运行) + +### Tool 调用生命周期(从外到内) + +1. `eventSenderToolHandler`(内部 ToolMiddleware - 在所有处理后发送工具结果事件) +2. `ToolsConfig.ToolCallMiddlewares`(ToolMiddleware) +3. `AgentMiddleware.WrapToolCall`(ToolMiddleware) +4. `ChatModelAgentMiddleware.WrapInvokableToolCall/WrapStreamableToolCall`(在请求时包装,先注册的在最外层) +5. `Tool.InvokableRun/StreamableRun` + +--- + +## 迁移指南 + +### 从 AgentMiddleware 迁移到 ChatModelAgentMiddleware + +**之前(AgentMiddleware):** + +```go +middleware := adk.AgentMiddleware{ + BeforeChatModel: func(ctx context.Context, state *adk.ChatModelAgentState) error { + return nil + }, +} +``` + +**之后(ChatModelAgentMiddleware):** + +```go +type MyHandler struct { + *adk.BaseChatModelAgentMiddleware +} + +func (h *MyHandler) BeforeModelRewriteState(ctx context.Context, state *adk.ChatModelAgentState, mc *adk.ModelContext) (context.Context, *adk.ChatModelAgentState, error) { + newCtx := context.WithValue(ctx, myKey, myValue) + return newCtx, state, nil +} +``` + +### 从 compose.ProcessState[*State] 迁移 + +**之前:** + +```go +compose.ProcessState(ctx, func(_ context.Context, st *adk.State) error { + st.Extra["myKey"] = myValue + return nil +}) +``` + +**之后(使用 SetRunLocalValue/GetRunLocalValue):** + +```go +if err := adk.SetRunLocalValue(ctx, "myKey", myValue); err != nil { + return ctx, state, err +} + +if val, found, err := adk.GetRunLocalValue(ctx, "myKey"); err == nil && found { +} +``` diff --git a/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md b/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md new file mode 100644 index 00000000000..0e72be90ff3 --- /dev/null +++ b/content/zh/docs/eino/core_modules/eino_adk/adk_agent_callback.md @@ -0,0 +1,345 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino ADK: Agent Callback' +weight: 9 +--- + +此功能为 ADK Agent 添加了回调(Callback)支持,类似于 compose 包中的回调机制。通过回调,用户可以观测 Agent 的执行生命周期,实现日志记录、追踪、监控等功能。 + +## 概述 + +ADK Agent Callback 机制与 Eino compose 中的回调系统共享相同的基础设施: + +- 使用相同的 `callbacks.Handler` 接口 +- 使用相同的 `callbacks.RunInfo` 结构 +- 可以与其他组件回调(如 ChatModel、Tool 等)组合使用 + +> 💡 +> 通过 Agent Callback,你可以在 Agent 执行的关键节点介入,实现 tracing、logging、metrics 等可观测性能力。本能力在 [v0.8.0.Beta](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) 版本引入。 + +## 核心类型 + +### ComponentOfAgent + +组件类型标识符,用于在回调中识别 Agent 相关事件: + +```go +const ComponentOfAgent components.Component = "Agent" +``` + +在 `callbacks.RunInfo.Component` 中使用,用于过滤仅与 Agent 相关的回调事件。 + +### AgentCallbackInput + +Agent 回调的输入类型,在 `OnStart` 回调中传递: + +```go +type AgentCallbackInput struct { + // Input 包含新运行的 Agent 输入。恢复执行时为 nil。 + Input *AgentInput + // ResumeInfo 包含从中断恢复时的信息。新运行时为 nil。 + ResumeInfo *ResumeInfo +} +``` + + + + + +
    调用方式字段值
    Agent.Run()
    Input
    字段有值,
    ResumeInfo
    为 nil
    Agent.Resume()
    ResumeInfo
    字段有值,
    Input
    为 nil
    + +### AgentCallbackOutput + +Agent 回调的输出类型,在 `OnEnd` 回调中传递: + +```go +type AgentCallbackOutput struct { + // Events 提供 Agent 事件流。每个 handler 接收独立的副本。 + Events *AsyncIterator[*AgentEvent] +} +``` + +> 💡 +> **重要**:`Events` 迭代器应**异步消费**,以避免阻塞 Agent 执行。每个回调 handler 接收独立的事件流副本,互不干扰。 + +## API 使用 + +### WithCallbacks + +添加回调 handler 以接收 Agent 生命周期事件的运行选项: + +```go +func WithCallbacks(handlers ...callbacks.Handler) AgentRunOption +``` + +### 类型转换函数 + +将通用回调类型转换为 Agent 专用类型: + +```go +// 转换输入类型 +func ConvAgentCallbackInput(input callbacks.CallbackInput) *AgentCallbackInput + +// 转换输出类型 +func ConvAgentCallbackOutput(output callbacks.CallbackOutput) *AgentCallbackOutput +``` + +如果类型不匹配,函数返回 nil。 + +## 使用示例 + +### 方式一:使用 HandlerBuilder + +使用 `callbacks.NewHandlerBuilder()` 构建通用的 callback handler: + +```go +import ( + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/callbacks" +) + +handler := callbacks.NewHandlerBuilder(). + OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + if info.Component == adk.ComponentOfAgent { + agentInput := adk.ConvAgentCallbackInput(input) + if agentInput.Input != nil { + fmt.Printf("Agent %s started with new run\n", info.Name) + } else { + fmt.Printf("Agent %s resumed from interrupt\n", info.Name) + } + } + return ctx + }). + OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + if info.Component == adk.ComponentOfAgent { + agentOutput := adk.ConvAgentCallbackOutput(output) + // 异步消费事件流 + go func() { + for { + event, ok := agentOutput.Events.Next() + if !ok { + break + } + // 处理事件... + fmt.Printf("Event from %s: %+v\n", event.AgentName, event) + } + }() + } + return ctx + }). + Build() + +iter := agent.Run(ctx, input, adk.WithCallbacks(handler)) +``` + +### 方式二:使用 HandlerHelper(推荐) + +使用 `template.HandlerHelper` 可以更方便地处理类型转换: + +```go +import ( + "github.com/cloudwego/eino/adk" + "github.com/cloudwego/eino/callbacks" + template "github.com/cloudwego/eino/utils/callbacks" +) + +helper := template.NewHandlerHelper(). + Agent(&template.AgentCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *adk.AgentCallbackInput) context.Context { + if input.Input != nil { + fmt.Printf("Agent %s started with input\n", info.Name) + } else { + fmt.Printf("Agent %s resumed\n", info.Name) + } + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + // 异步消费事件 + go func() { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // 处理事件... + } + }() + return ctx + }, + }). + Handler() + +iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +``` + +> 💡 +> `HandlerHelper` 会自动进行类型转换,代码更简洁。同时支持组合多种组件的回调处理器。 + +## Tracing 场景应用 + +Agent Callback 最常见的应用场景是实现分布式追踪(Tracing)。以下是使用 OpenTelemetry 实现 tracing 的示例: + +```go +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +tracer := otel.Tracer("my-agent-tracer") + +handler := callbacks.NewHandlerBuilder(). + OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + // 创建 span + ctx, span := tracer.Start(ctx, info.Name, + trace.WithAttributes( + attribute.String("component", string(info.Component)), + attribute.String("type", info.Type), + )) + + // Agent 特定的属性 + if info.Component == adk.ComponentOfAgent { + agentInput := adk.ConvAgentCallbackInput(input) + if agentInput != nil && agentInput.Input != nil { + span.SetAttributes(attribute.Bool("is_new_run", true)) + } else { + span.SetAttributes(attribute.Bool("is_resume", true)) + } + } + + return ctx + }). + OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + span := trace.SpanFromContext(ctx) + span.End() + return ctx + }). + OnErrorFn(func(ctx context.Context, info *callbacks.RunInfo, err error) context.Context { + span := trace.SpanFromContext(ctx) + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + span.End() + return ctx + }). + Build() +``` + +### 与 compose 回调组合 + +由于 ADK Agent 回调与 compose 回调共享相同的基础设施,你可以使用同一个 handler 同时处理 Agent 和其他组件(如 ChatModel、Tool)的回调: + +```go +helper := template.NewHandlerHelper(). + // Agent 回调 + Agent(&template.AgentCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *adk.AgentCallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "agent:"+info.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + // ChatModel 回调 + ChatModel(&template.ModelCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *model.CallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "model:"+info.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *model.CallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + // Tool 回调 + Tool(&template.ToolCallbackHandler{ + OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context { + ctx, _ = tracer.Start(ctx, "tool:"+input.Name) + return ctx + }, + OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context { + trace.SpanFromContext(ctx).End() + return ctx + }, + }). + Handler() + +// 使用组合的 handler +iter := agent.Run(ctx, input, adk.WithCallbacks(helper)) +``` + +> 💡 +> **提示**:cozeloop 的 adk trace 版本见 [https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1](https://github.com/cloudwego/eino-ext/releases/tag/callbacks%2Fcozeloop%2Fv0.2.0-alpha.1) + +## Agent 类型标识 + +内置 Agent 实现了 `components.Typer` 接口,返回其类型标识,该信息会填充到 `callbacks.RunInfo.Type` 字段中: + + + + + + + + +
    Agent 类型GetType() 返回值
    ChatModelAgent
    "ChatModel"
    workflowAgent (Sequential)
    "Sequential"
    workflowAgent (Parallel)
    "Parallel"
    workflowAgent (Loop)
    "Loop"
    DeterministicTransfer Agent
    "DeterministicTransfer"
    + +## 回调行为说明 + +### 回调调用时机 + + +
    +Run 方法1. 初始化回调上下文2. 处理输入3. 调用
    OnStart
    4. 执行 Agent 逻辑5. 注册
    OnEnd
    (在事件流创建时)
    +Resume 方法1. 构建 ResumeInfo2. 初始化回调上下文3. 调用
    OnStart
    4. 恢复 Agent 执行5. 注册
    OnEnd
    (在事件流创建时)
    + +### OnEnd 调用时机 + +`OnEnd` 回调在**迭代器创建时**注册,而非在生成器关闭时。这允许 handler 在事件流式传输时消费事件。 + +## 注意事项 + +### 1. 异步消费事件流 + +回调 handler 中的 `AgentCallbackOutput.Events` **必须**异步消费,否则会阻塞 Agent 执行: + +```go +// ✅ 正确 +OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + go func() { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // 处理事件 + } + }() + return ctx +} + +// ❌ 错误 - 会导致死锁 +OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *adk.AgentCallbackOutput) context.Context { + for { + event, ok := output.Events.Next() + if !ok { + break + } + // 处理事件 + } + return ctx +} +``` + +### 2. 无 OnError 回调 + +由于 `Agent.Run()` 和 `Agent.Resume()` 方法签名不返回 error,Agent 回调**不支持** `OnError`。错误信息通过 `AgentEvent.Err` 字段在事件流中传递。 + +### 3. 事件流复制机制 + +当有多个回调 handler 时,每个 handler 接收独立的事件流副本,互不干扰。最后一个 handler 接收原始事件以减少内存分配。 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 56864afd9e8..278ba5edc9e 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,6 +1,6 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino ADK: Agent 协作' 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 87413947772..9f25ffeb44e 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 @@ -4,7 +4,7 @@ date: "2025-11-20" lastmod: "" tags: [] title: 'Eino ADK: Agent Runner 与扩展' -weight: 3 +weight: 6 --- # Agent Runner 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 64a8651061b..d168a043a83 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,10 +1,10 @@ --- Description: "" -date: "2026-01-23" +date: "2026-03-02" lastmod: "" tags: [] title: Eino human-in-the-loop框架:技术架构指南 -weight: 6 +weight: 7 --- ## 概述 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 3fdd2b396ad..7ad70cdb79c 100644 --- a/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md +++ b/content/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino ADK: ChatModelAgent' @@ -259,6 +259,237 @@ a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ }) ``` +## ChatModelAgent Middleware + +`ChatModelAgentMiddleware` 是 `ChatModelAgent` 的扩展机制,允许开发者在 Agent 执行的各个阶段注入自定义逻辑: + + + +`ChatModelAgentMiddleware` 定义为 interface,开发者可以实现此 interface 并通过配置到 `ChatModelAgentConfig` 使其在 `ChatModelAgent` 中生效: + +```go +type ChatModelAgentMiddleware interface { + // ... +} + +a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + // ... + Handlers: []adk.ChatModelAgentMiddleware{ + &MyMiddleware{}, + }, +}) +``` + +**使用 BaseChatModelAgentMiddleware** + +`BaseChatModelAgentMiddleware` 提供所有方法的默认空实现。通过嵌入它,可以只覆盖需要的方法: + +```go +type MyMiddleware struct { + *adk.BaseChatModelAgentMiddleware + // 自定义字段 + logger *log.Logger +} + +// 只需覆盖需要的方法 +func (m *MyMiddleware) BeforeModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + m.logger.Printf("Messages count: %d", len(state.Messages)) + return ctx, state, nil +} +``` + +### BeforeAgent + +在每次 Agent 运行前调用,可用于修改指令和工具配置。ChatModelAgentContext 定义了 BeforeAgent 中可读写的内容: + +```go +type ChatModelAgentContext struct { + // InstructionAgent 是当前 Agent 的指令 + Instruction string + // Tools 是当前配置的原始工具列表 + Tools []tool.BaseTool + // ReturnDirectly 配置调用后直接返回的工具名称集合 + ReturnDirectly map[string]bool +} + +type ChatModelAgentMiddleware interface { + // ... + BeforeAgent(ctx context.Context, runCtx *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) + // ... +} +``` + +例子: + +```go +func (m *MyMiddleware) BeforeAgent( + ctx context.Context, + runCtx *adk.ChatModelAgentContext, +) (context.Context, *adk.ChatModelAgentContext, error) { + // 拷贝 runCtx,避免修改输入 + nRunCtx := *runCtx + + // 修改指令 + nRunCtx.Instruction += "\n\n请始终使用中文回复。" + + // 添加工具 + nRunCtx.Tools = append(runCtx.Tools, myCustomTool) + + // 设置工具直接返回 + nRunCtx.ReturnDirectly["my_tool"] = true + + return ctx, &nRunCtx, nil +} +``` + +### BeforeModelRewriteState / AfterModelRewriteState + +在每次模型调用前/后调用,可用于检查和修改消息历史。ModelContext 定义了只读内容,ChatModelAgentState 定义了可读写内容: + +```go +type ModelContext struct { + // Tools 包含当前配置给 Agent 的工具列表 + // 在请求时填充,包含将要发送给模型的工具信息 + Tools []*schema.ToolInfo + + // ModelRetryConfig 包含模型的重试配置 + // 从 Agent 的 ModelRetryConfig 填充 + ModelRetryConfig *ModelRetryConfig +} + +type ChatModelAgentState struct { + // Messages 包含当前会话中的所有消息 + Messages []Message +} + +type ChatModelAgentMiddleware interface { + BeforeModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) + AfterModelRewriteState(ctx context.Context, state *ChatModelAgentState, mc *ModelContext) (context.Context, *ChatModelAgentState, error) +} +``` + +例子: + +```go +func (m *MyMiddleware) BeforeModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + // 拷贝 state,避免修改入参 + nState := *state + + // 检查消息历史 + if len(state.Messages) > 50 { + // 截断过旧的消息 + nState.Messages = state.Messages[len(state.Messages)-50:] + } + return ctx, &nState, nil +} + +func (m *MyMiddleware) AfterModelRewriteState( + ctx context.Context, + state *adk.ChatModelAgentState, + mc *adk.ModelContext, +) (context.Context, *adk.ChatModelAgentState, error) { + // 模型响应是最后一条消息 + lastMsg := state.Messages[len(state.Messages)-1] + m.logger.Printf("Model response: %s", lastMsg.Content) + return ctx, state, nil +} +``` + +### WrapModel + +包装模型调用,可用于拦截和修改模型的输入输出: + +```go +type ChatModelAgentMiddleware interface { + WrapModel(ctx context.Context, m model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) +} +``` + +例子: + +```go +func (m *MyMiddleware) WrapModel( + ctx context.Context, + chatModel model.BaseChatModel, + mc *adk.ModelContext, +) (model.BaseChatModel, error) { + return &loggingModel{ + inner: chatModel, + logger: m.logger, + }, nil +} + +type loggingModel struct { + inner model.BaseChatModel + logger *log.Logger +} + +func (m *loggingModel) Generate(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.Message, error) { + m.logger.Printf("Input messages: %d", len(msgs)) + resp, err := m.inner.Generate(ctx, msgs, opts...) + m.logger.Printf("Output: %v, error: %v", resp != nil, err) + return resp, err +} + +func (m *loggingModel) Stream(ctx context.Context, msgs []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { + return m.inner.Stream(ctx, msgs, opts...) +} +``` + +### WrapInvokableToolCall / WrapStreamableToolCall + +包装工具调用,可用于拦截和修改工具的输入输出: + +```go +// InvokableToolCallEndpoint 是工具调用的函数签名。 +// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。 +type InvokableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) + +// StreamableToolCallEndpoint 是流式工具调用的函数签名。 +// Middleware 开发者围绕这个 Endpoint 添加自定义逻辑。 +type StreamableToolCallEndpoint func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (*schema.StreamReader[string], error) + +type ToolContext struct { + // Name 说明了本次调用工具的名称 + Name string + // CallID 说明了本次调用工具的 ToolCallID + CallID string +} + +type ChatModelAgentMiddleware interface { + WrapInvokableToolCall(ctx context.Context, endpoint InvokableToolCallEndpoint, tCtx *ToolContext) (InvokableToolCallEndpoint, error) + WrapStreamableToolCall(ctx context.Context, endpoint StreamableToolCallEndpoint, tCtx *ToolContext) (StreamableToolCallEndpoint, error) +} +``` + +例子: + +```go +func (m *MyMiddleware) WrapInvokableToolCall( + ctx context.Context, + endpoint adk.InvokableToolCallEndpoint, + tCtx *adk.ToolContext, +) (adk.InvokableToolCallEndpoint, error) { + return func(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) { + m.logger.Printf("Calling tool: %s (ID: %s)", tCtx.Name, tCtx.CallID) + start := time.Now() + + result, err := endpoint(ctx, argumentsInJSON, opts...) + + m.logger.Printf("Tool %s completed in %v", tCtx.Name, time.Since(start)) + return result, err + }, nil +} +``` + # ChatModelAgent 使用示例 ## 场景说明 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 070a2003cfa..b4471bb8c2e 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,17 +1,25 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: DeepAgents' +title: 'Eino ADK: DeepAgents' weight: 5 --- ## DeepAgents 概述 -Eino ADK 中的 DeepAgents 是一个高级的多智能体(Multi-Agent)协调器,其设计哲学与 LangChain 的 Deep Agents 概念高度一致。您可以将其理解为 Eino ADK 对 LangChain Deep Agents 思想的具体工程实现,旨在通过一个“指挥官”智能体(主 Agent)来规划、拆解、委派并监督一系列复杂的任务,这些任务最终由专门的子智能体(Sub-Agents)或工具(Tools)执行。 +DeepAgents 是在 ChatModelAgent (详见:[Eino ADK: ChatModelAgent](/zh/docs/eino/core_modules/eino_adk/agent_implementation/chat_model))的基础上实现的一种开箱即用的 agent 方案。你无需自己去拼装提示词、工具或上下文管理,就可以立即获得一个可运行的 agent,并仍可使用 ChatModelAgent 的扩展能力来为 agent 增加业务功能,如添加自定义 tools 和 middleware 等。 -DeepAgent 的核心价值在于自动化处理需要多步骤、多角色协作的复杂工作流。它不仅仅是单一功能的执行者,更是一个具备深度思考、规划和动态调整能力的“项目经理”。 +**包含内容:** + +- **规划能力** —— 通过 `write_todos` 进行任务拆解与进度跟踪 +- **文件系统** —— 提供 `read_file`、`write_file`、`edit_file`、`ls`、`glob`、`grep`,用于读取和写入上下文 +- **Shell 访问** —— 使用 `execute` 运行命令 +- **子 Agent** —— 通过 `task` 将工作委派给拥有独立上下文窗口的子智能体 +- **智能默认配置** —— 内置 Prompt,教模型如何高效使用这些工具 +- **上下文管理** —— 长对话历史自动摘要,大体量输出自动保存到文件 + - SummarizationMiddleware、ReductionMiddleware 正在建设中 ### ImportPath @@ -25,22 +33,50 @@ agent, err := deep.New(ctx, &deep.Config{}) ### DeepAgents 结构 -DeepAgents 是一种多智能体(Multi-Agent)架构,其核心思想在于通过一个主代理(MainAgent)来协调、规划和委派任务。主代理本身不直接执行所有操作,而是利用其内置的大模型和一系列工具来与外部世界交互或将复杂任务分解给专门的子代理(SubAgents)。 +DeepAgents 核心思想在于通过一个主 agent(MainAgent)来协调、规划、委派或自主执行任务。主 agent 利用其内置的 ChatModel 和一系列工具来与外部世界交互或将复杂任务分解给专门的子 agents(SubAgents)。 - + 上图展示了 DeepAgents 的核心组件与它们之间的调用关系: -- MainAgent: 系统的入口和总指挥,接收初始任务,以 ReAct 方式调用工具完成任务并负责最终结果的呈现。 +- 主 Agent: 系统的入口和总指挥,接收初始任务,以 ReAct 方式调用工具完成任务并负责最终结果的呈现。 - ChatModel (ToolCallingChatModel): 通常是一个具备工具调用能力的大语言模型,负责理解任务、推理、选择并调用工具。 - Tools: MainAgent 可用的一系列能力的集合,包括: - WriteTodos: 内置的规划工具,用于将复杂任务拆解为结构化的待办事项列表。 - TaskTool: 一个特殊的工具,作为调用子 Agent 的统一入口。 - - CustomTools: 用户根据业务需求自定义的各类工具。 + - BuiltinTools、CustomTools: DeepAgents 内置的通用工具以及用户根据业务需求自定义的各类工具。 - SubAgents: 负责执行具体、独立的子任务,与 MainAgent 上下文独立。 - GeneralPurpose: 通用子 Agent,具有与 MainAgent 相同的 Tools(除了 TaskTool),用于在“干净”的上下文中执行子任务。 - CustomSubAgents: 用户根据业务需求自定义的各种子 Agent。 +### 内置能力 + +#### Filesystem + +> 💡 +> 目前处于 alpha 状态 + +创建 DeepAgents 时配置相关 Backend,DeepAgents 会自动加载相应工具: + +``` +type Config struct { + // ... + Backend filesystem.Backend + Shell filesystem.Shell + StreamingShell filesystem.StreamingShell + // ... +} +``` + + + + + + +
    配置功能添加工具
    Backend提供文件系统访问能力,可选read_file, write_file, edit_file, glob, grep
    Shell提供 Shell 能力,可选,与 StreamShell 互斥 execute
    StreamingShell提供可以流式返回结果的 Shell 能力,可选,与 Shell 互斥execute(streaming)
    + +DeepAgents 内引用 filesystem middleware 来实现内置 filesystem,此 middleware 更详细的能力说明见:[Middleware: FileSystem](/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware/Middleware_FileSystem) + ### 任务拆解与规划 WriteTodos 的 Description 描述了任务拆解、规划的原则,主 Agent 通过调用 WriteTodos 工具,在上下文中添加子任务列表来启发后续推理、执行过程: @@ -64,6 +100,8 @@ WriteTodos 的 Description 描述了任务拆解、规划的原则,主 Agent 所有子 Agent 会被绑定到 TaskTool 上,当主 Agent 分配子任务给子 Agent 处理时,它会调用 TaskTool,并指明需要哪个子代理及执行的任务。TaskTool 随后将任务路由到指定的子代理,并在其执行完毕后,将结果返回给主 Agent。TaskTool 的默认 Description 会说明调用子 Agent 的通用规则并拼接每个子 Agent 的 Description,开发者可以通过配置 `TaskToolDescriptionGenerator` 来自定义 TaskTool 的 Description。 +> 当用户配置了 Config.SubAgents 时,这些 Agent 会基于 ChatModelAgent AgentAsTool 的能力绑定到 TaskTool 上 + **上下文隔离** Agent 之间的上下文隔离: @@ -75,9 +113,9 @@ Agent 之间的上下文隔离: DeepAgents 会默认增加一个子 Agent:general-purpose。general-purpose 具有和主 Agent 相同的 system prompt 和工具(除了 TaskTool),当任务没有专门的子 Agent 来解决时,主 Agent 可以调用 general-purpose 来隔离上下文。开发者可以通过配置 `WithoutGeneralSubAgent=true` 去掉此 Agent。 -### 与其他 MultiAgent 对比 +### 与其他 Agent 对比 -- 对比 Supervisor(ReAct) +- 对比 ReAct Agent - 优势:DeepAgents 通过内置 WriteTodos 强化任务拆解与规划;同时隔离多 Agents 上下文,在大规模、多步骤任务中通常效果更优。 - 劣势:制定计划与调用子 Agent 会带来额外的模型请求,增加耗时与 token 成本;若任务拆分不合理,可能对效果产生反作用。 @@ -86,7 +124,7 @@ DeepAgents 会默认增加一个子 Agent:general-purpose。general-purpose - 优势:DeepAgents 将 Plan/RePlan 作为工具供主 Agent 自由调用,可以在任务中跳过不必要的规划,整体上减少模型调用次数、降低耗时与成本。 - 劣势:任务规划与委派由一次模型调用完成,对模型能力要求更高,提示词调优也相对更困难。 -## DeepAgent 使用示例 +## DeepAgents 使用示例 ### 场景说明 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 561476180cc..367c05dcb7b 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,10 +1,10 @@ --- Description: "" -date: "2025-12-03" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: Plan-Execute Agent' -weight: 3 +title: 'Eino ADK: Plan-Execute Agent' +weight: 4 --- ## Plan-Execute Agent 概述 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 798630e28e5..e3081f5f0ed 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,10 +1,10 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] -title: 'Eino ADK MultiAgent: Supervisor Agent' -weight: 4 +title: 'Eino ADK: Supervisor Agent' +weight: 3 --- ## Supervisor 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 cccf8e7432a..179360380a6 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,6 +1,6 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino ADK: Agent 抽象' @@ -333,3 +333,58 @@ h.Send(&AgentEvent{AgentName: h.agentName, Action: &AgentAction{ ``` 4. 中止循环:当 LoopAgent 的一个子 Agent 发出 BreakLoopAction 时,对应的 LoopAgent 会停止循环并正常退出。 + +# 语言设置 + +ADK 提供了 `SetLanguage` 函数用于设置内置提示词(prompt)的语言。这影响所有 ADK 内置组件和中间件生成的提示词语言。本能力在 [alpha/08](https://github.com/cloudwego/eino/releases/tag/v0.8.0-alpha.13) 版本引入。 + +## API + +```go +// Language 表示 ADK 内置提示词的语言设置 +type Language uint8 + +const ( + // LanguageEnglish 表示英文(默认) + LanguageEnglish Language = iota + // LanguageChinese 表示中文 + LanguageChinese +) + +// SetLanguage 设置 ADK 内置提示词的语言 +// 默认语言是英文(如果未显式设置) +func SetLanguage(lang Language) error +``` + +## 使用示例 + +```go +import "github.com/cloudwego/eino/adk" + +// 设置为中文 +err := adk.SetLanguage(adk.LanguageChinese) +if err != nil { + // 处理错误 +} + +// 设置为英文(默认) +err = adk.SetLanguage(adk.LanguageEnglish) +``` + +## 影响范围 + +语言设置会影响以下组件的内置提示词: + + + + + + + +
    组件/中间件影响的提示词
    FileSystem Middleware文件系统工具描述、系统提示词、执行工具提示词
    Reduction Middleware工具结果截断/清理的提示文字
    Skill Middleware技能系统提示词、技能工具描述
    ChatModelAgent内置系统提示词
    + +> 💡 +> 建议在程序初始化时设置语言,因为语言设置是全局生效的。在运行时更改语言可能导致同一会话中出现混合语言的提示词。 + +> 💡 +> 语言设置仅影响 ADK 内置的提示词。你自定义的提示词(如 Agent 的 Instruction)需要自行处理国际化。 diff --git a/content/zh/docs/eino/ecosystem_integration/_index.md b/content/zh/docs/eino/ecosystem_integration/_index.md index de11b724869..2c371b558a9 100644 --- a/content/zh/docs/eino/ecosystem_integration/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/_index.md @@ -11,9 +11,9 @@ weight: 6 ### ChatModel -- openai: [ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) -- ark: [ChatModel - ARK](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark) -- ollama: [ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) +- openai: [ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) +- ark: [ChatModel - ARK](https://bytedance.larkoffice.com/wiki/WUzzwaX8ricGwZk1i1mcJHHNnEl) +- ollama: [ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) ### Document diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md b/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md index 73476897614..9ccb9cac63e 100644 --- a/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md @@ -1,10 +1,26 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Callbacks -weight: 0 +weight: 5 --- +# Callbacks 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + + +
    组件名称中文文档English Docs
    APMPlusREADME_zh.mdREADME.md
    CozeLoopREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md deleted file mode 100644 index 21e7df34784..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_apmplus.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -Description: "" -date: "2025-03-04" -lastmod: "" -tags: [] -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)),使用示例如下: - -```go -package main - -import ( - "context" - "log" - - "github.com/cloudwego/eino-ext/callbacks/apmplus" - "github.com/cloudwego/eino/callbacks" -) - -func main() { - ctx := context.Background() - // 创建apmplus handler - cbh, showdown, err := apmplus.NewApmplusHandler(&apmplus.Config{ - Host: "apmplus-cn-beijing.volces.com:4317", - AppKey: "xxx", - ServiceName: "eino-app", - Release: "release/v0.0.1", - }) - if err != nil { - log.Fatal(err) - } - - // 设置apmplus为全局callback - callbacks.AppendGlobalHandlers(cbh) - - g := NewGraph[string,string]() - /* - * compose and run graph - */ - - // 等待所有trace和metrics上报完成后退出 - showdown(ctx) -} -``` - -可以在 [APMPlus](https://console.volcengine.com/apmplus-server) 中查看 trace 和 metrics: - - diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md deleted file mode 100644 index 381a2ffe9b8..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_cozeloop.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Callback - CozeLoop -weight: 0 ---- - -# **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" - - 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 文档](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 deleted file mode 100644 index 3809d1f3e6d..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -Description: "" -date: "2025-03-18" -lastmod: "" -tags: [] -title: Callback - Langfuse -weight: 0 ---- - -Eino 基于 [graph callback](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callback_manual) 能力封装了 langfuse 的 trace 能力(参见 [https://langfuse.com/docs/get-started](https://langfuse.com/docs/get-started)),使用示例如下: - -```go -package main - -import ( - "github.com/cloudwego/eino-ext/callbacks/langfuse" - "github.com/cloudwego/eino/callbacks" -) - -func main() { - cbh, flusher := NewLangfuseHandler(&Config{ - Host: "https://cloud.langfuse.com", - PublicKey: "pk-xxx", - SecretKey: "sk-xxx", - }) - - callbacks.AppendGlobalHandlers(cbh) // 设置langfuse为全局callback - - g := NewGraph[string,string]() - /* - * compose and run graph - */ - - flusher() // 等待所有trace上报完成后退出 -} -``` - -可以在 Langfuse project 中查看 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 4818a55eba8..429009520f3 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_model/_index.md @@ -1,10 +1,33 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: ChatModel -weight: 0 +weight: 1 --- +# ChatModel 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + + + + +
    组件名称中文文档English Docs
    ARKREADME_zh.mdREADME.md
    ARKBotREADME_zh.mdREADME.md
    ClaudeREADME_zh.mdREADME.md
    DeepSeekREADME_zh.mdREADME.md
    GeminiREADME_zh.mdREADME.md
    OllamaREADME_zh.mdREADME.md
    OpenAIREADME_zh.mdREADME.md
    QianfanREADME_zh.mdREADME.md
    QwenREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md b/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md index 64ea526a3be..a82ee7cd937 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_ark.md @@ -1,14 +1,17 @@ --- Description: "" -date: "2026-01-22" +date: "2026-03-02" lastmod: "" tags: [] title: AgenticModel - ARK -weight: 0 +weight: 2 --- 基于 [Eino](https://github.com/cloudwego/eino) 的火山引擎 Ark 模型实现,实现了 `AgenticModel` 组件接口。这使得该模型能够无缝集成到 Eino 的 Agent 能力中,提供增强的自然语言处理和生成功能。 +> 💡 +> 本组件在 alpha/09 版本引入 + ## 功能特性 - 实现了 `github.com/cloudwego/eino/components/model.AgenticModel` 接口 diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md b/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md index 0475618433a..df544d85879 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_model/agentic_model_openai.md @@ -1,14 +1,17 @@ --- Description: "" -date: "2026-01-22" +date: "2026-03-02" lastmod: "" tags: [] title: AgenticModel - OpenAI -weight: 0 +weight: 1 --- 基于 [Eino](https://github.com/cloudwego/eino) 的 OpenAI 模型实现,实现了 `AgenticModel` 组件接口。这使得该模型能够无缝集成到 Eino 的 Agent 能力中,提供增强的自然语言处理和生成功能。 +> 💡 +> 本组件在 alpha/09 版本引入。 + ## 功能特性 - 实现了 `github.com/cloudwego/eino/components/model.AgenticModel` 接口 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 deleted file mode 100644 index bcfd60db078..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark.md +++ /dev/null @@ -1,1634 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: ChatModel - ARK -weight: 0 ---- - -## **ARK 模型** - -一个 [Eino](https://github.com/cloudwego/eino) 的 Volcengine Ark 模型实现,实现了 `ToolCallingChatModel` 接口。这使得与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -该软件包提供了两种不同的模型: - -- **ChatModel**:用于基于文本和多模态的聊天补全。 -- **ImageGenerationModel**:用于从文本提示或图像生成图像。 -- **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 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全、图像生成和响应 API -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## 快速安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/ark@latest -``` - -### 快速开始 - -以下是如何使用 `ChatModel` 的快速示例: - -```go -package main - -import ( - "context" - "encoding/json" - "errors" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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", ark.GetArkRequestID(msg)) - respBody, _ = json.MarshalIndent(msg, " ", " ") - log.Printf("body: %s \n", string(respBody)) -} -``` - -### 配置 - -`ChatModel` 可以使用 `ark.ChatModelConfig` 结构进行配置: - -```go -type ChatModelConfig struct { - // Timeout specifies the maximum duration to wait for API responses - // Optional. Default: 10 minutes - Timeout *time.Duration `json:"timeout"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following three fields are about authentication - either APIKey or AccessKey/SecretKey pair is required - // For authentication details, see: https://www.volcengine.com/docs/82379/1298459 - // APIKey takes precedence if both are provided - APIKey string `json:"api_key"` - AccessKey string `json:"access_key"` - SecretKey string `json:"secret_key"` - - // The following fields correspond to Ark's chat completion API parameters - // Ref: https://www.volcengine.com/docs/82379/1298454 - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // MaxTokens limits the maximum number of tokens that can be generated in the chat completion and the range of values is [0, 4096] - // 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 to 1.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: 0.7 - 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"` - - // 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"` - - // 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"` - - // CustomHeader the http header passed to model when requesting model - CustomHeader map[string]string `json:"custom_header"` -} -``` - -## 图像生成 - -### 快速开始 - -以下是如何使用 `ImageGenerationModel` 的快速示例: - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "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++ - } -} -``` - -### 配置 - -`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 - // Required - APIKey string `json:"api_key"` - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // Timeout specifies the maximum duration to wait for API responses - // If HTTPClient is set, Timeout will not be used. - // Optional. Default: 10 minutes - 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"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following fields correspond to Ark's image generation API parameters - // Ref: https://www.volcengine.com/docs/82379/1541523 - - // Size specifies the dimensions of the generated image. - // It can be a resolution keyword (e.g., "1K", "2K", "4K") or a custom resolution - // in "{width}x{height}" format (e.g., "1920x1080"). - // When using custom resolutions, the total pixels must be between 1280x720 and 4096x4096, - // and the aspect ratio (width/height) must be between 1/16 and 16. - // Optional. Defaults to "2048x2048". - Size string `json:"size"` - - // SequentialImageGeneration determines if the model should generate a sequence of images. - // Possible values: - // - "auto": The model decides whether to generate multiple images based on the prompt. - // - "disabled": Only a single image is generated. - // Optional. Defaults to "disabled". - SequentialImageGeneration SequentialImageGeneration `json:"sequential_image_generation"` - - // SequentialImageGenerationOption sets the maximum number of images to generate when - // SequentialImageGeneration is set to "auto". - // The value must be between 1 and 15. - // Optional. Defaults to 15. - SequentialImageGenerationOption *model.SequentialImageGenerationOptions `json:"sequential_image_generation_option"` - - // ResponseFormat specifies how the generated image data is returned. - // Possible values: - // - "url": A temporary URL to download the image (valid for 24 hours). - // - "b64_json": The image data encoded as a Base64 string in the response. - // Optional. Defaults to "url". - ResponseFormat ImageResponseFormat `json:"response_format"` - - // DisableWatermark, if set to true, removes the "AI Generated" watermark - // from the bottom-right corner of the image. - // Optional. Defaults to false. - DisableWatermark bool `json:"disable_watermark"` -} -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "context" - "encoding/json" - "errors" - "io" - "log" - "os" - - "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) - } - - 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" - - "github.com/cloudwego/eino/components/prompt" - "github.com/cloudwego/eino/compose" - "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) - } - - 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" -) - -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) -} -``` - -### 工具调用[ ](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" - - "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("BindTools 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" - - "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() - - // 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 前缀缓存[ ](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" - - "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) - } - - 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 前缀缓存[ ](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" - "errors" - "io" - "log" - "os" - - "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"), - 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 - -} -``` - -当针对已经被缓存的消息,不想使用缓存时,可调用 `InvalidateMessageCaches(messages []*schema.Message) error` 清楚消息中的缓存标记。这样 ARK SDK 在构造 Responses API 的请求时,便无法根据缓存标记找到对应的 ResponseID,从而不再给 Responses API 中的 PreviousResponseID 赋值 - -### 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" - "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"), - 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) - } -} -``` - -当针对已经被缓存的消息,不想使用缓存时,可调用 `InvalidateMessageCaches(messages []*schema.Message) error` 清楚消息中的缓存标记。这样 ARK SDK 在构造 Responses API 的请求时,便无法根据缓存标记找到对应的 ResponseID,从而不再给 Responses API 中的 PreviousResponseID 赋值 - -## **使用方式** - -### 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, - }, - }, - }, - }, - } -} -``` - -#### **工具调用** - -支持绑定工具: - -```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" - "time" - - "github.com/cloudwego/eino-ext/components/model/ark" - "github.com/cloudwego/eino/schema" -) - -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) - } -} -``` - -##### **流式对话** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/model/ark" - "github.com/cloudwego/eino/schema" -) - -func main() { - 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: 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 deleted file mode 100644 index 35d7143e8e9..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_arkbot.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -Description: "" -date: "2025-12-11" -lastmod: "" -tags: [] -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 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/model/arkbot@latest -``` - -## **快速开始** - -以下是如何使用 Ark Bot 的快速示例: - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} -``` - -## **配置** - -可以使用 `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. - // Optional. Default: 10 minutes - 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"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Ark service - // Optional. Default: "https://ark.cn-beijing.volces.com/api/v3" - BaseURL string `json:"base_url"` - // Region specifies the region where Ark service is located - // Optional. Default: "cn-beijing" - Region string `json:"region"` - - // The following three fields are about authentication - either APIKey or AccessKey/SecretKey pair is required - // For authentication details, see: https://www.volcengine.com/docs/82379/1298459 - // APIKey takes precedence if both are provided - APIKey string `json:"api_key"` - AccessKey string `json:"access_key"` - SecretKey string `json:"secret_key"` - - // The following fields correspond to Ark's chat completion API parameters - // Ref: https://www.volcengine.com/docs/82379/1298454 - - // Model specifies the ID of endpoint on ark platform - // Required - Model string `json:"model"` - - // MaxTokens limits the maximum number of tokens that can be generated in the chat completion. - // 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 to 1.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: 0.7 - 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"` - - // 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"` - - // 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"` - - // CustomHeader the http header passed to model when requesting model - CustomHeader map[string]string `json:"custom_header"` - - // 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"` - - // ResponseFormat specifies the format that the model must output. - ResponseFormat *ResponseFormat `json:"response_format,omitempty"` - } -``` - -## **请求选项** - -Ark 模型支持各种请求选项以自定义 API 调用的行为。以下是可用的选项: - -```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 {} -``` - -## **示例** - -### **文本生成** - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} -``` - -### **流式生成** - -```go -package main - -import ( - "context" - "encoding/json" - "fmt" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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)) -} -``` - -## **更多信息** - -- [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 deleted file mode 100644 index eb107e445e2..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_claude.md +++ /dev/null @@ -1,1142 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - Claude -weight: 0 ---- - -# Claude 模型 - -一个为 [Eino](https://github.com/cloudwego/eino) 实现的 Claude 模型,它实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## 特性 - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/claude@latest -``` - -## 快速开始 - -以下是如何使用 Claude 模型的快速示例: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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 - } - - // 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) - } -} -``` - -## 配置 - -可以使用 `claude.ChatModelConfig` 结构体配置模型: - -```go -type Config struct { - // ByBedrock indicates whether to use Bedrock Service - // Required for Bedrock - ByBedrock bool - - // AccessKey is your Bedrock API Access key - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - AccessKey string - - // SecretAccessKey is your Bedrock API Secret Access key - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - SecretAccessKey string - - // SessionToken is your Bedrock API Session Token - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - SessionToken string - - // Profile is your Bedrock API AWS profile - // This parameter is ignored if AccessKey and SecretAccessKey are provided - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - Profile string - - // Region is your Bedrock API region - // Obtain from: https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html - // Optional for Bedrock - Region string - - // BaseURL is the custom API endpoint URL - // Use this to specify a different API endpoint, e.g., for proxies or enterprise setups - // Optional. Example: "https://custom-claude-api.example.com" - BaseURL *string - - // APIKey is your Anthropic API key - // Obtain from: https://console.anthropic.com/account/keys - // Required - APIKey string - - // Model specifies which Claude model to use - // Required - Model string - - // MaxTokens limits the maximum number of tokens in the response - // Range: 1 to model's context length - // Required. Example: 2000 for a medium-length response - 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: float32(0.7) - Temperature *float32 - - // TopP controls diversity via nucleus sampling - // Range: [0.0, 1.0], where 1.0 disables nucleus sampling - // Optional. Example: float32(0.95) - TopP *float32 - - // TopK controls diversity by limiting the top K tokens to sample from - // Optional. Example: int32(40) - TopK *int32 - - // StopSequences specifies custom stop sequences - // The model will stop generating when it encounters any of these sequences - // Optional. Example: []string{"\n\nHuman:", "\n\nAssistant:"} - StopSequences []string - - Thinking *Thinking - - // HTTPClient specifies the client to send HTTP requests. - HTTPClient *http.Client `json:"http_client"` - - DisableParallelToolUse *bool `json:"disable_parallel_tool_use"` -} -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "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 - } - - // 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) - } -} -``` - -### 多模态支持(图片理解) - -```go -package main - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - - "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 - } - - // 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 -} -``` - -### 流式生成 - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "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 - } - - // 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----------") -} -``` - -### 工具调用 - -```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" -) - -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) - } -} -``` - -### Claude 提示词缓存 - -```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" -) - -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) - } -} - -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) - } -} - -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 模型是 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: 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 deleted file mode 100644 index d408ecddfe2..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_deepseek.md +++ /dev/null @@ -1,470 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: ChatModel - DeepSeek -weight: 0 ---- - -一个针对 [Eino](https://github.com/cloudwego/eino) 的 DeepSeek 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## **特性** - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/model/deepseek@latest -``` - -## **快速开始** - -以下是如何使用 DeepSeek 模型的快速示例: - -```go -package main - -import ( - "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) - } - -} -``` - -## **配置** - -可以使用 `deepseek.ChatModelConfig` 结构体配置模型: - -```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"` -} -``` - -## **示例** - -### **文本生成** - -```go -package main - -import ( - "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) - } - -} -``` - -### **带前缀文本生成** - -```go -package main - -import ( - "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) - -} -``` - -### **流式生成** - -```go -package main - -import ( - "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) - } - } -} -``` - -### **工具调用** - -```go -package main - -import ( - "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) - -} -``` - -## **更多信息** - -- [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 deleted file mode 100644 index 933f42a459d..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_gemini.md +++ /dev/null @@ -1,1143 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - Gemini -weight: 0 ---- - -# Google Gemini - -一个针对 [Eino](https://github.com/cloudwego/eino) 的 Google Gemini 实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## 特性 - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 -- 支持对生成的响应进行缓存 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/gemini@latest -``` - -## 快速开始 - -以下是如何使用 Gemini 模型的快速示例: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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), - // 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) - } -} -``` - -## 配置 - -可以使用 `gemini.Config` 结构体配置模型: - -```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-2.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 []gemini.GeminiResponseModality - - 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"` -} -``` - -## 缓存 - -该组件支持两种缓存策略以提高延迟并减少 API 调用: - -- 显式缓存(前缀缓存):从系统指令、工具和消息中构建可重用的上下文。使用 `CreatePrefixCache` 创建缓存,并在后续请求中使用 `gemini.WithCachedContentName(...)` 传递其名称。通过 `CacheConfig`(`TTL`、`ExpireTime`)配置 TTL 和绝对到期时间。当使用缓存内容时,请求会省略系统指令和工具,并依赖于缓存的前缀。 -- 隐式缓存:由 Gemini 自身管理。服务可能会自动重用先前的请求或响应。到期和重用由 Gemini 控制,无法配置。 - -下面的示例展示了如何创建前缀缓存并在后续调用中重用它。 - -```go -toolInfoList := []*schema.ToolInfo{ - { - 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)) - - -msg, err := cm.Generate(ctx, []*schema.Message{ - { - Role: schema.User, - Content: "give a very short summary about this transcript", - }, - }, gemini.WithCachedContentName(cacheInfo.Name)) -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) - } -} -``` - -### 多模态支持(图片理解) - -```go -package main - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) -} -``` - -### 携带前缀缓存文本生成 - -```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" -) - -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. -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)) -} -``` - -### 流式生成 - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "google.golang.org/genai" - - "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() -} -``` - -### 工具调用 - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "google.golang.org/genai" - - "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) -} -``` - -### 图片生成 - -```go -package main - -import ( - "context" - "encoding/json" - "log" - "os" - - "google.golang.org/genai" - - "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)) -} -``` - -### 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" -) - -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)) - } -} -``` - -## **基本介绍** - -~~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: []gemini.GeminiResponseModality{} // 指定模型返回的模态 支持 "TEXT", "IMAGE", "AUDIO" -}) -``` - -### **生成对话** - -对话生成支持普通模式和流式模式: - -```go -// invoke模式 -response, err := model.Generate(ctx, messages) - -// 流式模式 -stream, err := model.Stream(ctx, messages) -``` - -消息格式示例: - -```go -import ( - "encoding/base64" - "log" - "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.ChatMessagePartTypeImageURL, - 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", - }, - }, - }, - } - - // 生成回复 - resp, err := cm.Generate(ctx, messages) - if err != nil { - panic(err) - } - - // 处理回复 - /* - 生成的多模态内容存储在 AssistantGenMultiContent 字段中 - 本例中最终生成的 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.Content) -} -``` - -#### **流式对话** - -```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 := cm.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.Content) - } -} -``` - -### [更多示例](https://github.com/cloudwego/eino-ext/tree/main/components/model/gemini/examples) - -## **相关文档** - -- [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 deleted file mode 100644 index d553a1fde54..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md +++ /dev/null @@ -1,639 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - Ollama -weight: 0 ---- - -# Ollama 模型 - -一个针对 [Eino](https://github.com/cloudwego/eino) 的 Ollama 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## 特性 - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/ollama@latest -``` - -## 快速开始 - -以下是如何使用 Ollama 模型的快速示例: - -```go -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} -``` - -## 配置 - -可以使用 `ollama.ChatModelConfig` 结构体配置模型: - -```go -type ChatModelConfig struct { - BaseURL string `json:"base_url"` - Timeout time.Duration `json:"timeout"` // request timeout for http client - - // 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"` - - Model string `json:"model"` - Format json.RawMessage `json:"format"` - KeepAlive *time.Duration `json:"keep_alive"` - - Options *Options `json:"options"` - - Thinking *ThinkValue `json:"thinking"` -} - - -type Options struct { - Runner - - // NumKeep specifies the number of tokens from the prompt to retain when the context size is exceeded and tokens need to be trimmed. - NumKeep int `json:"num_keep,omitempty"` - // Seed sets the random number seed for the model. Using the same seed with the same parameters will produce the same output. - Seed int `json:"seed,omitempty"` - // NumPredict sets the maximum number of tokens to generate. - NumPredict int `json:"num_predict,omitempty"` - // TopK controls the diversity of the generated text by limiting the selection of tokens to the top k most likely tokens. - TopK int `json:"top_k,omitempty"` - // TopP, also known as nucleus sampling, is another way to control the diversity of the generated text. It filters out the least likely tokens whose cumulative probability is below a certain threshold. - TopP float32 `json:"top_p,omitempty"` - // MinP is a parameter that works with TopP to ensure that the generated text is not too constrained. It sets a minimum probability for a token to be considered. - MinP float32 `json:"min_p,omitempty"` - // TypicalP is a parameter that helps to generate more "typical" or expected text by sampling from a reduced set of tokens that are considered typical. - TypicalP float32 `json:"typical_p,omitempty"` - // RepeatLastN specifies how many of the last N tokens to consider for penalizing repetition. - RepeatLastN int `json:"repeat_last_n,omitempty"` - // Temperature controls the randomness of the generated text. A higher temperature results in more random and creative output, while a lower temperature produces more predictable and conservative text. - Temperature float32 `json:"temperature,omitempty"` - // RepeatPenalty is used to penalize the model for repeating tokens that have already appeared in the generated text. - RepeatPenalty float32 `json:"repeat_penalty,omitempty"` - // PresencePenalty is used to penalize the model for introducing new tokens that were not present in the prompt. - PresencePenalty float32 `json:"presence_penalty,omitempty"` - // FrequencyPenalty is used to penalize the model for using tokens that appear frequently in the training data. - FrequencyPenalty float32 `json:"frequency_penalty,omitempty"` - // Stop is a list of strings that will cause the generation to stop if they are encountered. - Stop []string `json:"stop,omitempty"` -} - -type ThinkValue struct { - // Value can be a bool or string - Value interface{} -} -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} -``` - -### 多模态支持(图片理解) - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} - -func of[T any](a T) *T { - return &a -} -``` - -### 流式生成 - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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") -} -``` - -### 工具调用 - -```go -package main - -import ( - "context" - "log" - "os" - - "github.com/cloudwego/eino/schema" - - "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) -} -``` - -### 开启 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, // 重复惩罚 - }, -}) -``` - -### **生成对话** - -对话生成支持普通模式和流式模式: - -```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" - "time" - - "github.com/cloudwego/eino-ext/components/model/ollama" - "github.com/cloudwego/eino/schema" - "github.com/ollama/ollama/api" -) - -func main() { - 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: 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 deleted file mode 100644 index 52b93cdb815..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md +++ /dev/null @@ -1,940 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - OpenAI -weight: 0 ---- - -## **OpenAI 模型** - -一个针对 [Eino](https://github.com/cloudwego/eino) 的 OpenAI 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力 - -## **特性** - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 支持自定义响应解析 -- 灵活的模型配置 - -## 安装 - -```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" - - "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) - -} -``` - -## 配置 - -可以使用 `openai.ChatModelConfig` 结构体配置模型: - -```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"` - -// The following three fields are only required when using Azure OpenAI Service, otherwise they can be ignored. -// For more details, see: https://learn.microsoft.com/en-us/azure/ai-services/openai/ - -// ByAzure indicates whether to use Azure OpenAI Service -// Required for Azure -ByAzure bool `json:"by_azure"` - -// AzureModelMapperFunc is used to map the model name to the deployment name for Azure OpenAI Service. -// This is useful when the model name is different from the deployment name. -// Optional for Azure, remove [,:] from the model name by default. -AzureModelMapperFunc func(model string) string - -// BaseURL is the Azure OpenAI endpoint URL -// Format: https://{YOUR_RESOURCE_NAME}.openai.azure.com. YOUR_RESOURCE_NAME is the name of your resource that you have created on Azure. -// Required for Azure -BaseURL string `json:"base_url"` - -// APIVersion specifies the Azure OpenAI API version -// Required for Azure -APIVersion string `json:"api_version"` - -// 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 -// Deprecated: use MaxCompletionTokens. Not compatible with o1-series models. -// refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens -MaxTokens *int `json:"max_tokens,omitempty"` - -// MaxCompletionTokens specifies an upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. -MaxCompletionTokens *int `json:"max_completion_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 指定模型响应的格式 -// 可选。用于结构化输出 -ResponseFormat *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"` - -// ExtraFields will override any existing fields with the same key. -// Optional. Useful for experimental features not yet officially supported. -ExtraFields map[string]any `json:"extra_fields,omitempty"` - -// ReasoningEffort will override the default reasoning level of "medium" -// Optional. Useful for fine tuning response latency vs. accuracy -ReasoningEffort ReasoningEffortLevel - -// Modalities are output types that you would like the model to generate. Most models are capable of generating text, which is the default: ["text"] -// The gpt-4o-audio-preview model can also be used to generate audio. To request that this model generate both text and audio responses, you can use: ["text", "audio"] -Modalities []Modality `json:"modalities,omitempty"` - -// Audio parameters for audio output. Required when audio output is requested with modalities: ["audio"] -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" - - "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{ - // 如果您想使用 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" - - "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 -} -``` - -### 流式生成 - -```go -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "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 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" - - "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 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" - - "log" - "os" - - "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)) - -} -``` - -### 结构化输出 - -```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" -) - -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) -} -``` - -## **使用方式** - -### **组件初始化** - -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.AssistantGenMultiContent 字段中 - 本例中最终生成的 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: 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 deleted file mode 100644 index b0d16b2c7a8..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qianfan.md +++ /dev/null @@ -1,697 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - Qianfan -weight: 0 ---- - -## **Qianfan 模型** - -一个针对 [Eino](https://github.com/cloudwego/eino) 的千帆模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## 特性 - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 自定义响应解析支持 -- 灵活的模型配置 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/qianfan@latest -``` - -## 快速开始 - -以下是如何使用千帆模型的快速示例: - -```go -package main - -import ( - "context" - "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") - 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 -} -``` - -## 配置 - -可以使用 `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 - - // LLMRetryCount is the number of times to retry a failed request. - LLMRetryCount *int - - // LLMRetryTimeout is the timeout for each retry attempt. - LLMRetryTimeout *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 - - // 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 - - // 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 - - // 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 - - // 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 - - // ParallelToolCalls specifies whether to call tools in parallel. Defaults to true. - ParallelToolCalls *bool - - // ResponseFormat specifies the format of the response. - ResponseFormat *ResponseFormat -} -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "context" - "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") - 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 -} -``` - -### 多模态理解(图片理解) - -```go -package main - -import ( - "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) -} - -func of[T any](t T) *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" -) - -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) -} - -func of[T any](t T) *T { - return &t -} -``` - -### 工具调用 - -```go -package main - -import ( - "context" - "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") - 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 -} -``` - -## **使用方式** - -### **组件初始化** - -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.AssistantGenMultiContent 字段中 - 本例中最终生成的 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: 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 deleted file mode 100644 index 6c6699c0890..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_qwen.md +++ /dev/null @@ -1,801 +0,0 @@ ---- -Description: "" -date: "2025-12-03" -lastmod: "" -tags: [] -title: ChatModel - Qwen -weight: 0 ---- - -## **Qwen 模型** - -一个针对 [Eino](https://github.com/cloudwego/eino) 的 Qwen 模型实现,实现了 `ToolCallingChatModel` 接口。这使得能够与 Eino 的 LLM 功能无缝集成,以增强自然语言处理和生成能力。 - -## 特性 - -- 实现了 `github.com/cloudwego/eino/components/model.Model` -- 轻松与 Eino 的模型系统集成 -- 可配置的模型参数 -- 支持聊天补全 -- 支持流式响应 -- 自定义响应解析支持 -- 灵活的模型配置 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/model/qwen@latest -``` - -## 快速开始 - -以下是如何使用 Qwen 模型的快速示例: - -```go -package main - -import ( - "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) - -} - -func of[T any](t T) *T { - return &t -} -``` - -## 配置 - -可以使用 `qwen.ChatModelConfig` 结构体配置模型: - -```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"` -} -``` - -## 示例 - -### 文本生成 - -```go -package main - -import ( - "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) - -} - -func of[T any](t T) *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" -) - -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 -} -``` - -### 流式生成 - -```go -package main - -import ( - "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} -} - -func of[T any](t T) *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" -) - -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 -} -``` - -## **使用方式** - -### **组件初始化** - -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.AssistantGenMultiContent 字段中 - 本例中最终生成的 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: 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/_index.md b/content/zh/docs/eino/ecosystem_integration/chat_template/_index.md index 0395a1add1a..bcd238879ba 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_template/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_template/_index.md @@ -4,7 +4,22 @@ date: "2025-01-20" lastmod: "" tags: [] title: ChatTemplate -weight: 0 +weight: 8 --- +# ChatTemplate 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + +
    组件名称中文文档English Docs
    MCPREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 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 deleted file mode 100644 index b07e5e2a0aa..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -Description: "" -date: "2025-11-20" -lastmod: "" -tags: [] -title: ChatTemplate - MCP -weight: 0 ---- - -## 基本介绍 - -Model Context Protocol(MCP)是 Anthropic 推出的供模型访问的资源的标准化开放协议,Eino 提供了相关封装,使用这些封装可以直接访问已有 MCP Server 上的资源。 - -本节介绍 MCPPrompt 的封装,封装实现了 Eino ChatTemplate 接口([Eino: ChatTemplate 使用说明](/zh/docs/eino/core_modules/components/chat_template_guide))。 - - - -其他封装参见: - -[[WIP]Tool - MCP](/zh/docs/eino/ecosystem_integration/tool/tool_mcp) - -## 使用方式 - -首先创建 mcp client,Eino 利用了开源 sdk [mark3labs/mcp-go](https://github.com/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 -// while stdio does not require it. -err = cli.Start(ctx) -``` - -考虑到 client 的复用,封装假设 client 已经完成和 Server 的 [Initialize](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/),用户需要自行完成 client 初始化: - -```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) -``` - -之后使用 Client 创建 Eino ChatTemplate: - -```go -import "github.com/cloudwego/eino-ext/components/prompt/mcp" - -tpl, err := mcp.NewPromptTemplate(ctx, &mcp.Config{ - Cli: cli, - Name: "your prompt name", -}) -``` - -template 可以直接调用: - -``` -result, err := tpl.Format(ctx, map[string]interface{}{/* input k-v */}) -``` - -也可以在任意 Eino Agent 中使用,以最简单的一个 llm chain 为例: - -``` -import ( - "github.com/cloudwego/eino/compose" - "github.com/cloudwego/eino-ext/components/prompt/mcp" -) - -llm, err := /*create a chat model*/ -tpl, err := mcp.NewPromptTemplate(ctx, &mcp.Config{ - Cli: cli, - Name: "your prompt name", -}) - -runner, err := compose.NewChain[map[string]any, *schema.Message](). - AppendChatTemplate(tpl). - AppendChatModel(llm). - Compile(ctx) -``` - -## 更多信息 - -实践例子可以参考:[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) diff --git a/content/zh/docs/eino/ecosystem_integration/document/_index.md b/content/zh/docs/eino/ecosystem_integration/document/_index.md index 752eb529fd7..2e03b44b528 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/document/_index.md @@ -1,10 +1,32 @@ --- Description: "" -date: "2025-01-06" +date: "2026-03-02" lastmod: "" tags: [] title: Document -weight: 0 +weight: 2 --- +# Document 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + + + +
    组件名称中文文档English Docs
    amazon s3README_zh.mdREADME.md
    local fileREADME_zh.mdREADME.md
    web urlREADME_zh.mdREADME.md
    htmlREADME_zh.mdREADME.md
    pdfREADME_zh.mdREADME.md
    markdownREADME_zh.mdREADME.md
    recursiveREADME_zh.mdREADME.md
    semanticREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3.md b/content/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3.md deleted file mode 100644 index f3c0e790adb..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Loader - amazon s3 -weight: 0 ---- - -## **基本介绍** - -S3 文档加载器是 Document Loader 接口的一个实现,用于从 AWS S3 存储桶中加载文档内容。该组件实现了 [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) - -### **AWS S3 服务介绍** - -Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,提供行业领先的可扩展性、数据可用性、安全性和性能。本组件通过 AWS SDK for Go v2 与 S3 服务进行交互,支持通过访问密钥或默认凭证的方式进行认证。 - -## **使用方式** - -### **组件初始化** - -S3 文档加载器通过 `NewS3Loader` 函数进行初始化,主要配置参数如下: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/loader/s3" -) - -func main() { - loader, err := s3.NewS3Loader(ctx, &s3.LoaderConfig{ - Region: aws.String("us-east-1"), // AWS 区域 - AWSAccessKey: aws.String("your-access-key"), // AWS 访问密钥ID - AWSSecretKey: aws.String("your-secret-key"), // AWS 访问密钥 - UseObjectKeyAsID: true, // 是否使用对象键作为文档ID - Parser: &parser.TextParser{}, // 文档解析器,默认为 TextParser - }) -} -``` - -配置参数说明: - -- `Region`:AWS S3 存储桶所在的区域 -- `AWSAccessKey` 和 `AWSSecretKey`:AWS 访问凭证,如果不提供则使用默认凭证链 -- `UseObjectKeyAsID`:是否将 S3 对象的键值用作文档 ID -- `Parser`:用于解析文档内容的解析器,默认使用 TextParser 直接将内容转换为字符串 - -### **加载文档** - -文档加载通过 `Load` 方法实现: - -```go -docs, err := loader.Load(ctx, document.Source{ - URI: "s3://bucket-name/path/to/document.txt", -}) -``` - -URI 格式说明: - -- 必须以 `s3://` 开头 -- 后接存储桶名称和对象键 -- 示例:`s3://my-bucket/folder/document.pdf` - -注意事项: - -- 目前不支持通过前缀批量加载文档 -- URI 必须指向具体的对象,不能以 `/` 结尾 -- 确保有足够的权限访问指定的存储桶和对象 - -### **完整使用示例** - -#### **单独使用** - -```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" -) - -func main() { - ctx := context.Background() - - loader, err := s3.NewS3Loader(ctx, &s3.LoaderConfig{ - Region: aws.String("us-east-1"), - AWSAccessKey: aws.String("your-access-key"), - AWSSecretKey: aws.String("your-secret-key"), - UseObjectKeyAsID: true, - }) - 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) - } -} -``` - -## **相关文档** - -- [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) -- [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) 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 deleted file mode 100644 index fcdd008412d..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Loader - local file -weight: 0 ---- - -## **基本介绍** - -local file 文件加载器是 Document Loader 接口的一个实现,用于从本地文件系统中加载文档内容。该组件实现了 [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide)。 - -### **特性介绍** - -本地文件加载器具有以下特点: - -- 支持通过文件路径直接加载文档 -- 自动识别文件类型并选择合适的解析器 (需设置 ExtParser) -- 保留文件的元数据信息 -- 支持将文件名作为文档 ID - -## **使用方式** - -### **组件初始化** - -本地文件加载器通过 `NewFileLoader` 函数进行初始化,主要配置参数如下: - -```go -import ( - "github.com/cloudwego/eino/components/document/loader/file" -) - -func main() { - loader, err := file.NewFileLoader(ctx, &FileLoaderConfig{ - UseNameAsID: true, // 是否使用文件名作为文档ID - Parser: &parser.TextParser{}, // 可选:指定自定义解析器 - }) -} -``` - -配置参数说明: - -- `UseNameAsID`:是否将文件名用作文档 ID -- `Parser`:文档解析器,如果不指定则使用默认的扩展名解析器(ExtParser,当前仅实现了 TextParser) - -### **加载文档** - -文档加载通过 `Load` 方法实现: - -```go -docs, err := loader.Load(ctx, document.Source{ - URI: "./path/to/document.txt", -}) -``` - -文档加载后会自动添加以下元数据: - -- `_file_name`:文件名 -- `_extension`:文件扩展名 -- `_source`:文件的完整路径 - -注意事项: - -- 路径必须指向一个文件,不能是目录 -- 文件必须可读 -- 如果 `UseNameAsID` 为 true,单文件时使用文件名作为 ID,多文档时使用 `文件名_序号` 作为 ID - -### **完整使用示例** - -#### **单独使用** - -```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() - - // 初始化加载器 - 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) - // 访问元数据 - fileName := doc.MetaData[file.MetaKeyFileName] - extension := doc.MetaData[file.MetaKeyExtension] - source := doc.MetaData[file.MetaKeySource] - } -} -``` - -## **相关文档** - -- [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) -- [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/document/loader_web_url.md b/content/zh/docs/eino/ecosystem_integration/document/loader_web_url.md deleted file mode 100644 index 00cd396511f..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_web_url.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Loader - web url -weight: 0 ---- - -## **基本介绍** - -URL 文档加载器是 Document Loader 接口的一个实现,用于从网络 URL 中加载文档内容。该组件实现了 [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide)。 - -### **特性介绍** - -URL 文档加载器具有以下特点: - -- 默认支持 HTML 网页内容的解析 -- 可自定义 HTTP 客户端配置 (eg: 自定义代理等) -- 支持自定义内容解析器 (eg: body、或其他特定的容器) - -## **使用方式** - -### **组件初始化** - -URL 文档加载器通过 `NewLoader` 函数进行初始化,主要配置参数如下: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/loader/url" -) - -func main() { - loader, err := url.NewLoader(ctx, &url.LoaderConfig{ - Parser: parser, - Client: httpClient, - RequestBuilder: requestBuilder, - }) -} -``` - -配置参数说明: - -- `Parser`:文档解析器,默认使用 HTML 解析器,会提取网页正文内容 -- `Client`:HTTP 客户端,可以自定义超时、代理等配置 -- `RequestBuilder`:请求构建器,用于自定义请求方法、请求头等 - -### **加载文档** - -文档加载通过 `Load` 方法实现: - -```go -docs, err := loader.Load(ctx, document.Source{ - URI: "https://example.com/document", -}) -``` - -注意事项: - -- URI 必须是有效的 HTTP/HTTPS URL -- 默认使用 GET 方法请求 -- 如果需要其他 HTTP 方法或自定义请求头,需要配置 RequestBuilder,例如 鉴权 场景 - -### **完整使用示例** - -#### **基本使用** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/document/loader/url" - "github.com/cloudwego/eino/components/document" -) - -func main() { - ctx := context.Background() - - // 使用默认配置初始化加载器 - loader, err := url.NewLoader(ctx, nil) - 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) - } -} -``` - -#### **自定义配置示例** - -```go -package main - -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() - - // 自定义 HTTP 客户端 - 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 - } - // 添加自定义请求头 - req.Header.Add("User-Agent", "MyBot/1.0") - return req, nil - } - - // 初始化加载器 - 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) - } -} -``` - -## **相关文档** - -- [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) -- [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md b/content/zh/docs/eino/ecosystem_integration/document/parser_html.md deleted file mode 100644 index 5197b8e95de..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Parser - html -weight: 0 ---- - -## **基本介绍** - -HTML 文档解析器是 Document Parser 接口的一个实现,用于将 HTML 网页内容解析为纯文本。该组件实现了 [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide),主要用于以下场景: - -- 需要从网页中提取纯文本内容 -- 需要获取网页的元数据(标题、描述等) - -### **特性介绍** - -HTML 解析器具有以下特点: - -- 支持选择性提取页面内容,灵活的内容选择器配置 (html selector) -- 自动提取网页元数据 (metadata) -- 安全的 HTML 解析 - -## **使用方式** - -### **组件初始化** - -HTML 解析器通过 `NewParser` 函数进行初始化,主要配置参数如下: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/parser/html" -) - -parser, err := html.NewParser(ctx, &html.Config{ - Selector: &selector, // 可选:内容选择器,默认为 body -}) -``` - -配置参数说明: - -- `Selector`:可选参数,指定要提取的内容区域,使用 goquery 选择器语法 - - 例如:`body` 表示提取 `` 标签内容 - - `#content` 表示提取 id 为 "content" 的元素内容 - -### **元数据说明** - -解析器会自动提取以下 metadata: - -- `html.MetaKeyTitle` ("_title"):网页标题 -- `html.MetaKeyDesc` ("_description"):网页描述 -- `html.MetaKeyLang` ("_language"):网页语言 -- `html.MetaKeyCharset` ("_charset"):字符编码 -- `html.MetaKeySource` ("_source"):文档来源 URI - -### **完整使用示例** - -#### **基本使用** - -```go -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() - - // 初始化解析器 - p, err := html.NewParser(ctx, nil) // 使用默认配置 - if err != nil { - panic(err) - } - - // HTML 内容 - html := ` - - - 示例页面 - - - - -
    -

    欢迎

    -

    这是正文内容。

    -
    - - - ` - - // 解析文档 - docs, err := p.Parse(ctx, strings.NewReader(html), - parser.WithURI("https://example.com"), - parser.WithExtraMeta(map[string]any{ - "custom": "value", - }), - ) - if err != nil { - panic(err) - } - - // 使用解析结果 - doc := docs[0] - println("内容:", doc.Content) - println("标题:", doc.MetaData[html.MetaKeyTitle]) - println("描述:", doc.MetaData[html.MetaKeyDesc]) - println("语言:", doc.MetaData[html.MetaKeyLang]) -} -``` - -#### **使用选择器** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/document/parser/html" -) - -func main() { - ctx := context.Background() - - // 指定只提取 id 为 content 的元素内容 - selector := "#content" - p, err := html.NewParser(ctx, &html.Config{ - Selector: &selector, - }) - if err != nil { - panic(err) - } - - // ... 解析文档的代码 ... -} -``` - -#### 在 loader 中使用 - -可参考 [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) 中的示例 - -## **相关文档** - -- [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) -- [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) -- [Parser - pdf](/zh/docs/eino/ecosystem_integration/document/parser_pdf) diff --git a/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md b/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md deleted file mode 100644 index 0ba183b0f09..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Parser - pdf -weight: 0 ---- - -## **基本介绍** - -PDF 文档解析器是 Document Parser 接口的一个实现,用于将 PDF 文件内容解析为纯文本。该组件实现了 [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide),主要用于以下场景: - -- 需要将 PDF 文档转换为可处理的纯文本格式 -- 需要按页面分割 PDF 文档内容 - -### **特性说明** - -PDF 解析器具有以下特点: - -- 支持基本的 PDF 文本提取 -- 可以选择按页面分割文档 -- 自动处理 PDF 字体和编码 -- 支持多页面 PDF 文档 - -注意事项: - -- 目前可能不能完全支持所有 PDF 格式 -- 不会保留空格和换行等格式信息 -- 复杂的 PDF 布局可能会影响提取效果 - -## **使用方式** - -### **组件初始化** - -PDF 解析器通过 `NewPDFParser` 函数进行初始化,主要配置参数如下: - -```go -import ( - "github.com/cloudwego/eino-ext/components/document/parser/pdf" -) - -func main() { - parser, err := pdf.NewPDFParser(ctx, &pdf.Config{ - ToPages: true, // 是否按页面分割文档 - }) -} -``` - -配置参数说明: - -- `ToPages`:是否将 PDF 按页面分割成多个文档,默认为 false - -### **解析文档** - -文档解析通过 `Parse` 方法实现: - -```go -docs, err := parser.Parse(ctx, reader, opts...) -``` - -解析选项: - -- 支持通过 `parser.WithURI` 设置文档 URI -- 支持通过 `parser.WithExtraMeta` 添加额外元数据 - -### **完整使用示例** - -#### **基本使用** - -```go -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() - - // 初始化解析器 - p, err := pdf.NewPDFParser(ctx, &pdf.Config{ - ToPages: false, // 不按页面分割 - }) - if err != nil { - panic(err) - } - - // 打开 PDF 文件 - file, err := os.Open("document.pdf") - if err != nil { - panic(err) - } - defer file.Close() - - // 解析文档 - docs, err := p.Parse(ctx, file, - parser.WithURI("document.pdf"), - parser.WithExtraMeta(map[string]any{ - "source": "./document.pdf", - }), - ) - if err != nil { - panic(err) - } - - // 使用解析结果 - for _, doc := range docs { - println(doc.Content) - } -} -``` - -#### 在 loader 中使用 - -可参考 [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) 中的示例 - -## **相关文档** - -- [Eino: Document Parser 接口使用说明](/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide) -- [Eino: Document Loader 使用说明](/zh/docs/eino/core_modules/components/document_loader_guide) -- [Parser - pdf](/zh/docs/eino/ecosystem_integration/document/parser_pdf) diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md deleted file mode 100644 index c1594208b20..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/splitter_markdown.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -Description: "" -date: "2025-01-07" -lastmod: "" -tags: [] -title: Splitter - markdown -weight: 0 ---- - -## **基本介绍** - -Markdown 分割器是 Document Transformer 接口的一个实现,用于根据 Markdown 文档的标题层级结构进行分割。该组件实现了 [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide)。 - -### **工作原理** - -Markdown 标题分割器通过以下步骤工作: - -1. 识别文档中的 Markdown 标题(`#`、`##`、`###` 等) -2. 根据标题层级构建文档结构树 -3. 将文档按标题分割成独立片段 - -## **使用方式** - -### **组件初始化** - -Markdown 标题分割器通过 `NewHeaderSplitter` 函数进行初始化,主要配置参数如下: - -```go -splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ - Headers: map[string]string{ - "#": "h1", // 一级标题 - "##": "h2", // 二级标题 - "###": "h3", // 三级标题 - }, - TrimHeaders: false, // 是否在输出中保留标题行 -}) -``` - -配置参数说明: - -- `Headers`:必需参数,定义标题标记和对应的元数据键名映射 -- `TrimHeaders`:是否在输出的内容中移除标题行 - -### **完整使用示例** - -```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() - - // 初始化分割器 - splitter, err := markdown.NewHeaderSplitter(ctx, &markdown.HeaderConfig{ - Headers: map[string]string{ - "#": "h1", - "##": "h2", - "###": "h3", - }, - TrimHeaders: false, - }) - if err != nil { - panic(err) - } - - // 准备要分割的文档 - docs := []*schema.Document{ - { - ID: "doc1", - Content: `# 文档标题 - -这是介绍部分的内容。 - -## 第一章 - -这是第一章的内容。 - -### 1.1 节 - -这是 1.1 节的内容。 - -## 第二章 - -这是第二章的内容。 - -\`\`\` -# 这是代码块中的注释,不会被识别为标题 -\`\`\` -`, - }, - } - - // 执行分割 - results, err := splitter.Transform(ctx, docs) - if err != nil { - panic(err) - } - - // 处理分割结果 - for i, doc := range results { - println("片段", i+1, ":", doc.Content) - println("标题层级:") - for k, v := range doc.MetaData { - if k == "h1" || k == "h2" || k == "h3" { - println(" ", k, ":", v) - } - } - } -} -``` - -## **特性说明** - -- 支持 ````` 和 `~~~` 风格的代码块 -- 自动维护标题的层级关系 - - 新的同级标题会重置下级标题 - - 标题层级信息通过元数据传递 - -## **相关文档** - -- [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide) -- [Splitter - recursive](/zh/docs/eino/ecosystem_integration/document/splitter_recursive) -- [Splitter - semantic](/zh/docs/eino/ecosystem_integration/document/splitter_semantic) diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md deleted file mode 100644 index 776b19f0bde..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Splitter - recursive -weight: 0 ---- - -## **基本介绍** - -递归分割器是 Document Transformer 接口的一个实现,用于将长文档按照指定大小递归地切分成更小的片段。该组件实现了 [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide)。 - -### **工作原理** - -递归分割器通过以下步骤工作: - -1. 按照分隔符列表顺序尝试分割文档 -2. 如果当前分隔符无法将文档分割成小于目标大小的片段,则使用下一个分隔符 -3. 对分割后的片段进行合并,确保片段大小接近目标大小 -4. 在合并过程中保持指定大小的重叠区域 - -## **使用方式** - -### **组件初始化** - -递归分割器通过 `NewSplitter` 函数进行初始化,主要配置参数如下: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, // 必需:目标片段大小 - OverlapSize: 200, // 可选:片段重叠大小 - Separators: []string{"\n", ".", "?", "!"}, // 可选:分隔符列表 - LenFunc: nil, // 可选:自定义长度计算函数 - KeepType: recursive.KeepTypeNone, // 可选:分隔符保留策略 -}) -``` - -配置参数说明: - -- `ChunkSize`:必需参数,指定目标片段的大小 -- `OverlapSize`:片段之间的重叠大小,用于保持上下文连贯性 -- `Separators`:分隔符列表,按优先级顺序使用 -- `LenFunc`:自定义文本长度计算函数,默认使用 `len()` -- `KeepType`:分隔符保留策略,可选值: - - `KeepTypeNone`:不保留分隔符 - - `KeepTypeStart`:在片段开始处保留分隔符 - - `KeepTypeEnd`:在片段结尾处保留分隔符 - -### **完整使用示例** - -```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() - - // 初始化分割器 - splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - OverlapSize: 200, - Separators: []string{"\n\n", "\n", "。", "!", "?"}, - KeepType: recursive.KeepTypeEnd, - }) - if err != nil { - panic(err) - } - - // 准备要分割的文档 - docs := []*schema.Document{ - { - ID: "doc1", - Content: `这是第一个段落,包含了一些内容。 - - 这是第二个段落。这个段落有多个句子!这些句子通过标点符号分隔。 - - 这是第三个段落。这里有更多的内容。`, - }, - } - - // 执行分割 - results, err := splitter.Transform(ctx, docs) - if err != nil { - panic(err) - } - - // 处理分割结果 - for i, doc := range results { - println("片段", i+1, ":", doc.Content) - } -} -``` - -### **高级用法** - -自定义长度计算: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - LenFunc: func(s string) int { - // eg: 使用 unicode 字符数而不是字节数 - return len([]rune(s)) - }, -}) -``` - -调整重叠策略: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - // 增大重叠区域以保持更多上下文 - OverlapSize: 300, - // 在片段结尾保留分隔符 - KeepType: recursive.KeepTypeEnd, -}) -``` - -自定义分隔符: - -```go -splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 1000, - // 按优先级排序的分隔符列表 - Separators: []string{ - "\n\n", // 空行(段落分隔) - "\n", // 换行 - "。", // 句号 - }, -}) -``` - -## **相关文档** - -- [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide) -- [Splitter - markdown](/zh/docs/eino/ecosystem_integration/document/splitter_markdown) -- [Splitter - semantic](/zh/docs/eino/ecosystem_integration/document/splitter_semantic) diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md deleted file mode 100644 index e0c059f10b9..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Splitter - semantic -weight: 0 ---- - -## **基本介绍** - -语义分割器是 Document Transformer 接口的一个实现,用于基于语义相似度将长文档切分成更小的片段。该组件实现了 [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide)。 - -### **工作原理** - -语义分割器通过以下步骤工作: - -1. 首先使用基本分隔符(如换行符、句号等)将文档分割成始片段 -2. 使用向量嵌入模型为每个片段生成语义向量 -3. 计算相邻片段之间的余弦相似度 -4. 根据相似度阈值决定是否在两个片段之间进行分割 -5. 对小于最小大小的片段进行合并 - -## **使用方式** - -### **组件初始化** - -语义分割器通过 `NewSplitter` 函数进行初始化,主要配置参数如下: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, // 必需:用于生成文本向量的嵌入器 - BufferSize: 2, // 可选:上下文缓冲区大小 - MinChunkSize: 100, // 可选:最小片段大小 - Separators: []string{"\n", ".", "?", "!"}, // 可选:分隔符列表 - Percentile: 0.9, // 可选:分割阈值百分位数 - LenFunc: nil, // 可选:自定义长度计算函数 -}) -``` - -配置参数说明: - -- `Embedding`:必需参数,用于生成文本向量的嵌入器实例 -- `BufferSize`:上下文缓冲区大小,用于在计算语义相似度时包含更多上下文信息 -- `MinChunkSize`:最小片段大小,小于此大小的片段会被合并 -- `Separators`:用于初始分割的分隔符列表,按顺序使用 -- `Percentile`:分割阈值的百分位数,范围 0-1,越大分割越少 -- `LenFunc`:自定义文本长度计算函数,默认使用 `len()` - -### **完整使用示例** - -```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" -) - -func main() { - ctx := context.Background() - - // 初始化嵌入器(示例使用) - embedder := &embedding.SomeEmbeddingImpl{} // eg: openai embedding - - // 初始化分割器 - splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - BufferSize: 2, - MinChunkSize: 100, - Separators: []string{"\n", ".", "?", "!"}, - Percentile: 0.9, - }) - if err != nil { - panic(err) - } - - // 准备要分割的文档 - docs := []*schema.Document{ - { - ID: "doc1", - Content: `这是第一段内容,包含了一些重要信息。 - 这是第二段内容,与第一段语义相关。 - 这是第三段内容,主题已经改变。 - 这是第四段内容,继续讨论新主题。`, - }, - } - - // 执行分割 - results, err := splitter.Transform(ctx, docs) - if err != nil { - panic(err) - } - - // 处理分割结果 - for i, doc := range results { - println("片段", i+1, ":", doc.Content) - } -} -``` - -### **高级用法** - -自定义长度计算: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - LenFunc: func(s string) int { - // 使用 unicode 字符数而不是字节数 - return len([]rune(s)) - }, -}) -``` - -调整分割粒度: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - // 增大百分位数,减少分割点 - Percentile: 0.95, - // 增大最小片段大小,避免过小的片段 - MinChunkSize: 200, -}) -``` - -优化语义判断: - -```go -splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: embedder, - // 增大缓冲区大小,可包含更多上下文 - BufferSize: 10, - // 自定义分隔符优先级 - Separators: []string{"\n\n", "\n", "。", "!", "?", ","}, -}) -``` - -## **相关文档** - -- [Eino: Document Transformer 使用说明](/zh/docs/eino/core_modules/components/document_transformer_guide) -- [Splitter - recursive](/zh/docs/eino/ecosystem_integration/document/splitter_recursive) -- [Splitter - markdown](/zh/docs/eino/ecosystem_integration/document/splitter_markdown) diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/_index.md b/content/zh/docs/eino/ecosystem_integration/embedding/_index.md index cf069862dba..c47933e6ca3 100644 --- a/content/zh/docs/eino/ecosystem_integration/embedding/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/embedding/_index.md @@ -1,10 +1,30 @@ --- Description: "" -date: "2025-01-06" +date: "2026-03-02" lastmod: "" tags: [] title: Embedding -weight: 0 +weight: 3 --- +# Embedding 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + +
    组件名称中文文档English Docs
    ARKREADME_zh.mdREADME.md
    OllamaREADME_zh.mdREADME.md
    OpenAIREADME_zh.mdREADME.md
    QianfanREADME_zh.mdREADME.md
    TencentCloudREADME_zh.mdREADME.md
    dashscopeREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md deleted file mode 100644 index 22396aaa8d4..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Embedding - ARK -weight: 0 ---- - -## **基本介绍** - -Ark Embedding 是 Eino Embedding 接口的一个实现,用于将文本转换为向量表示,火山引擎 Ark 是一个提供机器学习模型推理服务的平台,其中包含了文本向量化服务。该组件实现了 [[🚧]Eino: Embedding 使用说明](/zh/docs/eino/core_modules/components/embedding_guide)。 - -## **使用方式** - -### **组件初始化** - -Ark 向量嵌入器通过 `NewEmbedder` 函数进行初始化,主要配置参数如下: - -```go -import "github.com/cloudwego/eino-ext/components/embedding/ark" - -embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - // 认证配置(二选一) - APIKey: "your-api-key", // 使用 API Key 认证 - // 或使用 AK/SK 认证 - AccessKey: "your-access-key", - SecretKey: "your-secret-key", - - // 服务配置 - Model: "ep-xxxxxxx-xxxxx", // Ark 平台的端点 ID - BaseURL: "https://ark.cn-beijing.volces.com/api/v3", // 可选,默认为北京区域 - Region: "cn-beijing", // 可选,默认为北京区域 - - // 高级配置 - Timeout: &timeout, // 请求超时时间 - RetryTimes: &retryTimes, // 重试次数 - Dimensions: &dimensions, // 输出向量维度 - User: &user, // 用户标识 -}) -``` - -### **生成向量嵌入** - -文本向量化通过 `EmbedStrings` 方法实现: - -```go -embeddings, err := embedder.EmbedStrings(ctx, []string{ - "第一段文本", - "第二段文本", -}) -``` - -### **完整使用示例** - -#### **基本使用** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/embedding/ark" -) - -func main() { - ctx := context.Background() - - // 初始化嵌入器 - timeout := 30 * time.Second - embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: "your-api-key", - Model: "ep-20xxxxxxx-xxxxx", - Timeout: &timeout, - }) - if err != nil { - panic(err) - } - - // 生成文本向量 - texts := []string{ - "这是第一段示例文本", - "这是第二段示例文本", - } - - embeddings, err := embedder.EmbedStrings(ctx, texts) - if err != nil { - panic(err) - } - - // 使用生成的向量 - for i, embedding := range embeddings { - println("文本", i+1, "的向量维度:", len(embedding)) - } -} -``` - -## **相关文档** - -- [Eino: Embedding 使用说明](/zh/docs/eino/core_modules/components/embedding_guide) -- [Embedding - OpenAI](/zh/docs/eino/ecosystem_integration/embedding/embedding_openai) -- [火山引擎 Ark 服务](https://www.volcengine.com/product/ark) diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md deleted file mode 100644 index 76ac8d76419..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_dashscope.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Embedding - dashscope -weight: 0 ---- - -你可以使用阿里云的百炼平台提供的 embedding 服务。 diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md deleted file mode 100644 index b472675f030..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ollama.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -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 的工作流 -- 支持自定义 Ollama 服务端点和模型 -- Eino 内置回调支持 - -## **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/embedding/ollama -``` - -## **快速开始** - -```go -package main - -import ( - "context" - "log" - "os" - "time" - - "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) -} -``` - -## **配置说明** - -embedder 可以通过 `EmbeddingConfig` 结构体进行配置: - -```go -type EmbeddingConfig struct { - // 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 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/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md deleted file mode 100644 index e8883842f13..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Embedding - OpenAI -weight: 0 ---- - -## **基本介绍** - -OpenAI 向量嵌入器是 Eino Embedding 接口的一个实现,用于将文本转换为向量表示。该组件实现了 [[🚧]Eino: Embedding 使用说明](/zh/docs/eino/core_modules/components/embedding_guide),主要用于以下场景: - -- 需要将文本转换为高维向量表示 -- 使用 OpenAI 的 embedding 模型 -- 使用 Azure OpenAI Service 的 embedding 模型 - -## **使用方式** - -### **组件初始化** - -OpenAI 向量嵌入器通过 `NewEmbedder` 函数进行初始化,主要配置参数如下: - -```go -import "github.com/cloudwego/eino-ext/components/embedding/openai" - -embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{ - // OpenAI API 配置 - APIKey: "your-api-key", - Model: "text-embedding-ada-002", - Timeout: 30 * time.Second, - - // 可选:Azure OpenAI Service 配置 - ByAzure: true, - BaseURL: "https://your-resource.openai.azure.com", - APIVersion: "2023-05-15", - - EncodingFormat: &format, // 编码格式 - Dimensions: &dimension, // 向量维度 - User: &user, // 用户标识 -}) -``` - -### **生成向量嵌入** - -文本向量化通过 `EmbedStrings` 方法实现: - -```go -embeddings, err := embedder.EmbedStrings(ctx, []string{ - "第一段文本", - "第二段文本", -}) -``` - -### **完整使用示例** - -#### **基本使用** - -```go -package main - -import ( - "context" - "time" - - "github.com/cloudwego/eino-ext/components/embedding/openai" -) - -func main() { - ctx := context.Background() - - // 初始化嵌入器 - embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{ - APIKey: "your-api-key", - Model: "text-embedding-ada-002", - Timeout: 30 * time.Second, - }) - if err != nil { - panic(err) - } - - // 生成文本向量 - texts := []string{ - "这是第一段示例文本", - "这是第二段示例文本", - } - - embeddings, err := embedder.EmbedStrings(ctx, texts) - if err != nil { - panic(err) - } - - // 使用生成的向量 - for i, embedding := range embeddings { - println("文本", i+1, "的向量维度:", len(embedding)) - } -} -``` - -## **相关文档** - -- [Eino: Embedding 使用说明](/zh/docs/eino/core_modules/components/embedding_guide) -- [Embedding - ARK](/zh/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/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md deleted file mode 100644 index dde774b3d11..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_qianfan.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Embedding - Qianfan -weight: 0 ---- - -你可以使用千帆平台提供的 embedding 服务 (百度)。 diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md deleted file mode 100644 index 171c9f9ba9c..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_tencentcloud.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Embedding - TencentCloud -weight: 0 ---- - -## **腾讯云混元 Embedding** - -这是一个为 [Eino](https://github.com/cloudwego/eino) 实现的腾讯云混元 Embedding 组件,实现了 `Embedder` 接口。它可以无缝集成到 Eino 的 embedding 系统中,提供文本向量化能力。 - -## **特性** - -- 实现了 `github.com/cloudwego/eino/components/embedding.Embedder` 接口 -- 易于集成到 Eino 的 rag 工作流中 -- 内置 token 使用量追踪 -- 自动处理大规模文本数组的批处理 -- 内置回调支持 - -## **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/embedding/tencentcloud -``` - -## **快速开始** - -```go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/cloudwego/eino-ext/components/embedding/tencentcloud" -) - -func main() { - ctx := context.Background() - - // 创建 embedder 配置 - cfg := &tencentcloud.EmbeddingConfig{ - SecretID: os.Getenv("TENCENTCLOUD_SECRET_ID"), - SecretKey: os.Getenv("TENCENTCLOUD_SECRET_KEY"), - Region: "ap-guangzhou", - } - - // 创建 embedder - embedder, err := tencentcloud.NewEmbedder(ctx, cfg) - if err != nil { - panic(err) - } - - // 获取文本的向量表示 - embeddings, err := embedder.EmbedStrings(ctx, []string{"hello world", "bye world"}) - if err != nil { - panic(err) - } - - fmt.Printf("Embeddings: %v\n", embeddings) -} -``` - -## **配置说明** - -embedder 可以通过 `EmbeddingConfig` 结构体进行配置: - -```go -type EmbeddingConfig struct { - SecretID string // 腾讯云 Secret ID - SecretKey string // 腾讯云 Secret Key - Region string // 腾讯云地域(如 "ap-guangzhou") -} -``` - -## **功能详情** - -### **自动批处理** - -embedder 会自动处理大规模文本数组的批处理。根据腾讯云 API 的限制,每个请求最多可以处理 200 个文本。embedder 会自动将较大的数组分割成适当的批次进行处理。 - -### **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) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/_index.md b/content/zh/docs/eino/ecosystem_integration/indexer/_index.md index b37e9ddda24..c89624182ec 100644 --- a/content/zh/docs/eino/ecosystem_integration/indexer/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/indexer/_index.md @@ -1,10 +1,33 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Indexer -weight: 0 +weight: 6 --- -Indexer 为把文本进行索引存储,一般使用 [Embedding](/zh/docs/eino/ecosystem_integration/embedding) 做语义化索引,也可做分词索引等,以便于 [Retriever](/zh/docs/eino/ecosystem_integration/retriever) 中召回使用 +# Indexer 组件列表 + +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + + + + +
    组件名称中文文档English Docs
    ElasticSearch 8README_zh.mdREADME.md
    Elasticsearch 7README_zh.mdREADME.md
    Elasticsearch 9README_zh.mdREADME.md
    Milvus v1 (旧版)README_zh.mdREADME.md
    Milvus v2 (推荐)README_zh.mdREADME.md
    OpenSearch 2README_zh.mdREADME.md
    OpenSearch 3README_zh.mdREADME.md
    RedisREADME_zh.mdREADME.md
    volc VikingDBREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md deleted file mode 100644 index 9311ff525b9..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch7.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: 'Indexer - Elasticsearch 7 ' -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 Elasticsearch 7.x 索引器实现,实现了 `Indexer` 接口。该组件可以与 Eino 的文档索引系统无缝集成,提供强大的向量存储和检索能力。 - -## 功能特性 - -- 实现了 `github.com/cloudwego/eino/components/indexer.Indexer` -- 易于集成 Eino 索引系统 -- 可配置 Elasticsearch 参数 -- 支持向量相似度搜索 -- 支持批量索引操作 -- 支持自定义字段映射 -- 灵活的文档向量化支持 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/es7@latest -``` - -## 快速开始 - -以下是一个使用索引器的简单示例: - -```go -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v7" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es7" -) - -const ( - 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") - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - - // 使用 Volcengine ARK 创建 embedding 组件 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 加载文档 - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // 创建 ES 索引器组件 - indexer, _ := es7.NewIndexer(ctx, &es7.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es7.FieldValue, err error) { - return map[string]es7.FieldValue{ - fieldContent: { - Value: doc.Content, - EmbedKey: fieldContentVector, // 对文档内容进行向量化并保存到 "content_vector" 字段 - }, - fieldExtraLocation: { - Value: doc.MetaData[docExtraLocation], - }, - }, nil - }, - Embedding: emb, - }) - - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## 配置 - -可以使用 `IndexerConfig` 结构体配置索引器: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // 必填:Elasticsearch 客户端实例 - Index string // 必填:存储文档的索引名称 - BatchSize int // 选填:最大文本嵌入批次大小(默认:5) - - // 必填:将 Document 字段映射到 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) // 选填:自定义字符串转换 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md deleted file mode 100644 index 0890058b186..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_elasticsearch9.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Elasticsearch 9 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -为 [Eino](https://github.com/cloudwego/eino) 实现的 Elasticsearch 9.x 索引器,实现了 `Indexer` 接口。这使得可以与 Eino 的向量存储和检索系统无缝集成,从而增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/indexer.Indexer` -- 易于集成 Eino 的索引系统 -- 可配置 Elasticsearch 参数 -- 支持向量相似度搜索 -- 批量索引操作 -- 自定义字段映射支持 -- 灵活的文档向量化 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/es9@latest -``` - -## 快速开始 - -这里是使用索引器的快速示例,更多细节请阅读 components/indexer/es9/examples/indexer/add_documents.go: - -```go -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - "github.com/elastic/go-elasticsearch/v9" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es9" -) - -const ( - 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") - - var cert []byte - var err error - if httpCACertPath != "" { - cert, err = os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. 创建 embedding 组件 (使用 Ark) - // 请将 "ARK_API_KEY", "ARK_REGION", "ARK_MODEL" 替换为实际配置 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 3. 准备文档 - // 文档通常包含 ID 和 Content - // 也可以包含额外的 Metadata 用于过滤或其他用途 - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // 4. 创建 ES 索引器组件 - indexer, _ := es9.NewIndexer(ctx, &es9.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - // DocumentToFields 指定如何将文档字段映射到 ES 字段 - DocumentToFields: func(ctx context.Context, doc *schema.Document) (field2Value map[string]es9.FieldValue, err error) { - return map[string]es9.FieldValue{ - fieldContent: { - Value: doc.Content, - EmbedKey: fieldContentVector, // 对文档内容进行向量化并保存到 "content_vector" 字段 - }, - fieldExtraLocation: { - // 额外的 metadata 字段 - Value: doc.MetaData[docExtraLocation], - }, - }, nil - }, - // 提供 embedding 组件用于向量化 - Embedding: emb, - }) - - // 5. 索引文档 - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## 配置 - -可以使用 `IndexerConfig` 结构体配置索引器: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // 必填: Elasticsearch 客户端实例 - Index string // 必填: 存储文档的索引名称 - BatchSize int // 选填: 用于 embedding 的最大文本数量 (默认: 5) - - // 必填: 将 Document 字段映射到 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) // 选填: 自定义字符串转换 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [Elasticsearch Go Client 文档](https://github.com/elastic/go-elasticsearch) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md deleted file mode 100644 index 414160c14ba..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_es8.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - ElasticSearch 8 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -为 [Eino](https://github.com/cloudwego/eino) 实现的 Elasticsearch 8.x 索引器,实现了 `Indexer` 接口。这使得可以与 Eino 的向量存储和检索系统无缝集成,从而增强语义搜索能力。 - -## 功能特性 - -- 实现 `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 ( - "context" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v8" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/indexer/es8" -) - -const ( - 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") - - // 1. 创建 ES 客户端 - httpCACertPath := os.Getenv("ES_HTTP_CA_CERT_PATH") - if httpCACertPath != "" { - cert, err := os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. 创建 embedding 组件 - // 使用火山引擎 Ark,替换环境变量为真实配置 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 3. 准备文档 - // 文档通常包含 ID 和 Content。也可以添加额外的元数据用于过滤等用途。 - docs := []*schema.Document{ - { - ID: "1", - Content: "Eiffel Tower: Located in Paris, France.", - MetaData: map[string]any{ - docExtraLocation: "France", - }, - }, - { - ID: "2", - Content: "The Great Wall: Located in China.", - MetaData: map[string]any{ - docExtraLocation: "China", - }, - }, - } - - // 4. 创建 ES 索引器组件 - indexer, _ := es8.NewIndexer(ctx, &es8.IndexerConfig{ - Client: client, - Index: indexName, - BatchSize: 10, - // DocumentToFields 指定如何将文档字段映射到 ES 字段 - 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 组件用于向量化 - Embedding: emb, - }) - - // 5. 索引文档 - ids, err := indexer.Store(ctx, docs) - if err != nil { - fmt.Printf("index error: %v\n", err) - return - } - fmt.Println("indexed ids:", ids) -} -``` - -## 配置 - -可以使用 `IndexerConfig` 结构体配置索引器: - -```go -type IndexerConfig struct { - Client *elasticsearch.Client // 必填: Elasticsearch 客户端实例 - Index string // 必填: 存储文档的索引名称 - BatchSize int // 选填: 用于 embedding 的最大文本数量 (默认: 5) - - // 必填: 将 Document 字段映射到 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) // 选填: 自定义字符串转换 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [Elasticsearch Go Client 文档](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 deleted file mode 100644 index 499f9f1c1fd..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvus.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Milvus v1 (旧版) -weight: 0 ---- - -> **模块说明:** 本模块 (`EINO-ext/milvus`) 基于 `milvus-sdk-go` 实现。鉴于底层 SDK 已停止维护,且最高仅适配至 Milvus 2.4 版本,本模块仅作为向后兼容组件保留。 -> -> **建议:** 新接入的用户请直接使用 [Indexer - Milvus 2 (v2.5+)](/zh/docs/eino/ecosystem_integration/indexer/indexer_milvusv2) 模块以获得持续支持。 - -## **Milvus 存储** - -基于 Milvus 2.x 的向量存储实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Indexer` 接口的存储方案。该组件可无缝集成 Eino 的向量存储和检索系统,增强语义搜索能力。 - -## **快速开始** - -### **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/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" -) - -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) -} -``` - -## **Configuration** - -```go -type IndexerConfig struct { - // 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 -} -``` - -## **默认数据模型** - -## 获取帮助 - -- [[集团内部版] Milvus 快速入门](https://bytedance.larkoffice.com/wiki/P3JBw4PtKiLGPhkUCQZcXbHFnkf) - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -### 外部参考 - -- [Milvus 文档](https://milvus.io/docs) -- [Milvus 索引类型](https://milvus.io/docs/index.md) -- [Milvus 度量类型](https://milvus.io/docs/metric.md) -- [milvus-sdk-go 参考](https://milvus.io/api-reference/go/v2.4.x/About.md) - -### 相关文档 - -- [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvusv2.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvusv2.md deleted file mode 100644 index b6c27f178d3..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_milvusv2.md +++ /dev/null @@ -1,395 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Milvus v2 (推荐) -weight: 0 ---- - -> **向量数据库 Milvus 版介绍** -> -> 向量检索服务 Milvus 版为基于开源 Milvus 构建的全托管数据库服务,提供高效的非结构化数据检索能力,适用于多样化 AI 场景,客户无需再关心底层硬件资源,降低使用成本,提高整体效率。 -> -> 鉴于公司**内场**的 Milvus 服务采用标准 SDK,因此适用 **EINO-ext 社区版本**。 - -本包为 EINO 框架提供 Milvus 2.x (V2 SDK) 索引器实现,支持文档存储和向量索引。 - -> **注意**: 本包需要 **Milvus 2.5+** 以支持服务器端函数(如 BM25),基础功能兼容低版本。 - -## 功能特性 - -- **Milvus V2 SDK**: 使用最新的 `milvus-io/milvus/client/v2` SDK -- **灵活的索引类型**: 支持多种索引构建器,包括 Auto, HNSW, IVF 系列, SCANN, DiskANN, GPU 索引以及 RaBitQ (Milvus 2.6+) -- **混合搜索就绪**: 原生支持稀疏向量 (BM25/SPLADE) 与稠密向量的混合存储 -- **服务端向量生成**: 使用 Milvus Functions (BM25) 自动生成稀疏向量 -- **自动化管理**: 自动处理集合 Schema 创建、索引构建和加载 -- **字段分析**: 可配置的文本分析器(支持中文 Jieba、英文、Standard 等) -- **自定义文档转换**: Eino 文档到 Milvus 列的灵活映射 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/milvus2 -``` - -## 快速开始 - -```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/client/v2/milvusclient" - - milvus2 "github.com/cloudwego/eino-ext/components/indexer/milvus2" -) - -func main() { - // 获取环境变量 - 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() - - // 创建 embedding 模型 - emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: arkApiKey, - Model: arkModel, - }) - if err != nil { - log.Fatalf("Failed to create embedding: %v", err) - return - } - - // 创建索引器 - indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: addr, - Username: username, - Password: password, - }, - Collection: "my_collection", - - Vector: &milvus2.VectorConfig{ - Dimension: 1024, // 与 embedding 模型维度匹配 - MetricType: milvus2.COSINE, - IndexBuilder: milvus2.NewHNSWIndexBuilder().WithM(16).WithEfConstruction(200), - }, - Embedding: emb, - }) - if err != nil { - log.Fatalf("Failed to create indexer: %v", err) - return - } - log.Printf("Indexer created successfully") - - // 存储文档 - docs := []*schema.Document{ - { - ID: "doc1", - Content: "Milvus is an open-source vector database", - MetaData: map[string]any{ - "category": "database", - "year": 2021, - }, - }, - { - ID: "doc2", - Content: "EINO is a framework for building AI applications", - }, - } - ids, err := indexer.Store(ctx, docs) - if err != nil { - log.Fatalf("Failed to store: %v", err) - return - } - log.Printf("Store success, ids: %v", ids) -} -``` - -## 配置选项 - - - - - - - - - - - - - - - -
    字段类型默认值描述
    Client
    *milvusclient.Client
    -预配置的 Milvus 客户端(可选)
    ClientConfig
    *milvusclient.ClientConfig
    -客户端配置(Client 为空时必需)
    Collection
    string
    "eino_collection"
    集合名称
    Vector
    *VectorConfig
    -稠密向量配置 (维度, MetricType, 字段名)
    Sparse
    *SparseVectorConfig
    -稀疏向量配置 (MetricType, 字段名)
    IndexBuilder
    IndexBuilder
    AutoIndexBuilder
    索引类型构建器
    Embedding
    embedding.Embedder
    -用于向量化的 Embedder(可选)。如果为空,文档必须包含向量 (BYOV)。
    ConsistencyLevel
    ConsistencyLevel
    ConsistencyLevelDefault
    一致性级别 (
    ConsistencyLevelDefault
    使用 Milvus 默认: Bounded; 如果未显式设置,则保持集合级别设置)
    PartitionName
    string
    -插入数据的默认分区
    EnableDynamicSchema
    bool
    false
    启用动态字段支持
    Functions
    []*entity.Function
    -Schema 函数定义(如 BM25),用于服务器端处理
    FieldParams
    map[string]map[string]string
    -字段参数配置(如 enable_analyzer)
    - -### 稠密向量配置 (`VectorConfig`) - - - - - - -
    字段类型默认值描述
    Dimension
    int64
    -向量维度 (必需)
    MetricType
    MetricType
    L2
    相似度度量类型 (L2, IP, COSINE 等)
    VectorField
    string
    "vector"
    稠密向量字段名
    - -### 稀疏向量配置 (`SparseVectorConfig`) - - - - - - -
    字段类型默认值描述
    VectorField
    string
    "sparse_vector"
    稀疏向量字段名
    MetricType
    MetricType
    BM25
    相似度度量类型
    Method
    SparseMethod
    SparseMethodAuto
    生成方法 (
    SparseMethodAuto
    SparseMethodPrecomputed
    )
    - -> **注意**: 仅当 `MetricType` 为 `BM25` 时,`Method` 默认为 `Auto`。`Auto` 意味着使用 Milvus 服务器端函数(远程函数)。对于其他度量类型(如 `IP`),默认为 `Precomputed`。 - -## 索引构建器 - -### 稠密索引构建器 (Dense) - - - - - - - - - - - - - - - - - - -
    构建器描述关键参数
    NewAutoIndexBuilder()
    Milvus 自动选择最优索引-
    NewHNSWIndexBuilder()
    基于图的高性能索引
    M
    ,
    EfConstruction
    NewIVFFlatIndexBuilder()
    基于聚类的搜索
    NList
    NewIVFPQIndexBuilder()
    乘积量化,内存高效
    NList
    ,
    M
    ,
    NBits
    NewIVFSQ8IndexBuilder()
    标量量化
    NList
    NewIVFRabitQIndexBuilder()
    IVF + RaBitQ 二进制量化 (Milvus 2.6+)
    NList
    NewFlatIndexBuilder()
    暴力精确搜索-
    NewDiskANNIndexBuilder()
    面向大数据集的磁盘索引-
    NewSCANNIndexBuilder()
    高召回率的快速搜索
    NList
    ,
    WithRawDataEnabled
    NewBinFlatIndexBuilder()
    二进制向量的暴力搜索-
    NewBinIVFFlatIndexBuilder()
    二进制向量的聚类搜索
    NList
    NewGPUBruteForceIndexBuilder()
    GPU 加速暴力搜索-
    NewGPUIVFFlatIndexBuilder()
    GPU 加速 IVF_FLAT-
    NewGPUIVFPQIndexBuilder()
    GPU 加速 IVF_PQ-
    NewGPUCagraIndexBuilder()
    GPU 加速图索引 (CAGRA)
    IntermediateGraphDegree
    ,
    GraphDegree
    - -### 稀疏索引构建器 (Sparse) - - - - - -
    构建器描述关键参数
    NewSparseInvertedIndexBuilder()
    稀疏向量倒排索引
    DropRatioBuild
    NewSparseWANDIndexBuilder()
    稀疏向量 WAND 算法
    DropRatioBuild
    - -### 示例:HNSW 索引 - -```go -indexBuilder := milvus2.NewHNSWIndexBuilder(). - WithM(16). // 每个节点的最大连接数 (4-64) - WithEfConstruction(200) // 索引构建时的搜索宽度 (8-512) -``` - -### 示例:IVF_FLAT 索引 - -```go -indexBuilder := milvus2.NewIVFFlatIndexBuilder(). - WithNList(256) // 聚类单元数量 (1-65536) -``` - -### 示例:IVF_PQ 索引(内存高效) - -```go -indexBuilder := milvus2.NewIVFPQIndexBuilder(). - WithNList(256). // 聚类单元数量 - WithM(16). // 子量化器数量 - WithNBits(8) // 每个子量化器的位数 (1-16) -``` - -### 示例:SCANN 索引(高召回率快速搜索) - -```go -indexBuilder := milvus2.NewSCANNIndexBuilder(). - WithNList(256). // 聚类单元数量 - WithRawDataEnabled(true) // 启用原始数据进行重排序 -``` - -### 示例:DiskANN 索引(大数据集) - -```go -indexBuilder := milvus2.NewDiskANNIndexBuilder() // 基于磁盘,无额外参数 -``` - -### 示例:Sparse Inverted Index (稀疏倒排索引) - -```go -indexBuilder := milvus2.NewSparseInvertedIndexBuilder(). - WithDropRatioBuild(0.2) // 构建时忽略小值的比例 (0.0-1.0) -``` - -### 稠密向量度量 (Dense) - - - - - - -
    度量类型描述
    L2
    欧几里得距离
    IP
    内积
    COSINE
    余弦相似度
    - -### 稀疏向量度量 (Sparse) - - - - - -
    度量类型描述
    BM25
    Okapi BM25 (
    SparseMethodAuto
    必需)
    IP
    内积 (适用于预计算的稀疏向量)
    - -### 二进制向量度量 (Binary) - - - - - - - - -
    度量类型描述
    HAMMING
    汉明距离
    JACCARD
    杰卡德距离
    TANIMOTO
    Tanimoto 距离
    SUBSTRUCTURE
    子结构搜索
    SUPERSTRUCTURE
    超结构搜索
    - -## 稀疏向量支持 - -索引器支持两种稀疏向量模式:**自动生成 (Auto-Generation)** 和 **预计算 (Precomputed)**。 - -### 自动生成 (BM25) - -使用 Milvus 服务器端函数从内容字段自动生成稀疏向量。 - -- **要求**: Milvus 2.5+ -- **配置**: 设置 `MetricType: milvus2.BM25`。 - -```go -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - // ... 基础配置 ... - Collection: "hybrid_collection", - - Sparse: &milvus2.SparseVectorConfig{ - VectorField: "sparse_vector", - MetricType: milvus2.BM25, - // BM25 时 Method 默认为 SparseMethodAuto - }, - - // BM25 的分析器配置 - FieldParams: map[string]map[string]string{ - "content": { - "enable_analyzer": "true", - "analyzer_params": `{"type": "standard"}`, // 中文使用 {"type": "chinese"} - }, - }, -}) -``` - -### 预计算 (SPLADE, BGE-M3 等) - -允许存储由外部模型(如 SPLADE, BGE-M3)或自定义逻辑生成的稀疏向量。 - -- **配置**: 设置 `MetricType`(通常为 `IP`)和 `Method: milvus2.SparseMethodPrecomputed`。 -- **用法**: 通过 `doc.WithSparseVector()` 传入稀疏向量。 - -```go -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - Collection: "sparse_collection", - - Sparse: &milvus2.SparseVectorConfig{ - VectorField: "sparse_vector", - MetricType: milvus2.IP, - Method: milvus2.SparseMethodPrecomputed, - }, -}) - -// 存储包含稀疏向量的文档 -doc := &schema.Document{ID: "1", Content: "..."} -doc.WithSparseVector(map[int]float64{ - 1024: 0.5, - 2048: 0.3, -}) -indexer.Store(ctx, []*schema.Document{doc}) -``` - -## 自带向量 (Bring Your Own Vectors) - -如果您的文档已经包含向量,可以不配置 Embedder 使用 Indexer。 - -```go -// 创建不带 embedding 的 indexer -indexer, err := milvus2.NewIndexer(ctx, &milvus2.IndexerConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: "localhost:19530", - }, - Collection: "my_collection", - Vector: &milvus2.VectorConfig{ - Dimension: 128, - MetricType: milvus2.L2, - }, - // Embedding: nil, // 留空 -}) - -// 存储带有预计算向量的文档 -docs := []*schema.Document{ - { - ID: "doc1", - Content: "Document with existing vector", - }, -} - -// 附加稠密向量到文档 -// 向量维度必须与集合维度匹配 -vector := []float64{0.1, 0.2, ...} -docs[0].WithDenseVector(vector) - -// 附加稀疏向量(可选,如果配置了 Sparse) -// 稀疏向量是 index -> weight 的映射 -sparseVector := map[int]float64{ - 10: 0.5, - 25: 0.8, -} -docs[0].WithSparseVector(sparseVector) - -ids, err := indexer.Store(ctx, docs) -``` - -对于 BYOV 模式下的稀疏向量,请参考上文 **预计算 (Precomputed)** 部分进行配置。 - -## 示例 - -查看 [https://github.com/cloudwego/eino-ext/tree/main/components/indexer/milvus2/examples](https://github.com/cloudwego/eino-ext/tree/main/components/indexer/milvus2/examples) 目录获取完整的示例代码: - -- [demo](./examples/demo) - 使用 HNSW 索引的基础集合设置 -- [hnsw](./examples/hnsw) - HNSW 索引示例 -- [ivf_flat](./examples/ivf_flat) - IVF_FLAT 索引示例 -- [rabitq](./examples/rabitq) - IVF_RABITQ 索引示例 (Milvus 2.6+) -- [auto](./examples/auto) - AutoIndex 示例 -- [diskann](./examples/diskann) - DISKANN 索引示例 -- [hybrid](./examples/hybrid) - 混合搜索设置 (稠密 + BM25 稀疏) (Milvus 2.5+) -- [hybrid_chinese](./examples/hybrid_chinese) - 中文混合搜索示例 (Milvus 2.5+) -- [sparse](./examples/sparse) - 纯稀疏索引示例 (BM25) -- [byov](./examples/byov) - 自带向量示例 - -## 获取帮助 - -- [[集团内部版] Milvus 快速入门](https://bytedance.larkoffice.com/wiki/P3JBw4PtKiLGPhkUCQZcXbHFnkf) - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -### 外部参考 - -- [Milvus 文档](https://milvus.io/docs) -- [Milvus 索引类型](https://milvus.io/docs/index.md) -- [Milvus 度量类型](https://milvus.io/docs/metric.md) -- [Milvus Go SDK 参考](https://milvus.io/api-reference/go/v2.6.x/About.md) - -### 相关文档 - -- [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md deleted file mode 100644 index 4a0ccd17d30..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch2.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - OpenSearch 2 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 OpenSearch 2 索引器实现,实现了 `Indexer` 接口。这使得 OpenSearch 可以无缝集成到 Eino 的向量存储和检索系统中,增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/indexer.Indexer` -- 易于集成到 Eino 的索引系统 -- 可配置的 OpenSearch 参数 -- 支持向量相似度搜索 -- 支持批量索引操作 -- 支持自定义字段映射 -- 灵活的文档向量化支持 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/opensearch2@latest -``` - -## 快速开始 - -以下是一个如何使用该索引器的简单示例,更多细节可参考 components/indexer/opensearch2/examples/indexer/main.go: - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v2" - - "github.com/cloudwego/eino-ext/components/indexer/opensearch2" -) - -func main() { - ctx := context.Background() - - client, err := opensearch.NewClient(opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - if err != nil { - log.Fatal(err) - } - - // 创建 embedding 组件 - emb := createYourEmbedding() - - // 创建 opensearch indexer 组件 - indexer, _ := opensearch2.NewIndexer(ctx, &opensearch2.IndexerConfig{ - Client: client, - Index: "your_index_name", - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (map[string]opensearch2.FieldValue, error) { - return map[string]opensearch2.FieldValue{ - "content": { - Value: doc.Content, - EmbedKey: "content_vector", - }, - }, nil - }, - Embedding: emb, - }) - - docs := []*schema.Document{ - {ID: "1", Content: "example content"}, - } - - ids, _ := indexer.Store(ctx, docs) - fmt.Println(ids) -} -``` - -## 配置说明 - -可以通过 `IndexerConfig` 结构体配置索引器: - -```go -type IndexerConfig struct { - Client *opensearch.Client // 必填:OpenSearch 客户端实例 - Index string // 必填:用于存储文档的索引名称 - BatchSize int // 选填:最大文本嵌入批次大小(默认:5) - - // 必填:将 Document 字段映射到 OpenSearch 字段的函数 - 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) // 选填:自定义字符串转换函数 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [OpenSearch Go 客户端文档](https://github.com/opensearch-project/opensearch-go) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md deleted file mode 100644 index 85407c778ff..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_opensearch3.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - OpenSearch 3 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 OpenSearch 3 索引器实现,实现了 `Indexer` 接口。这使得 OpenSearch 可以无缝集成到 Eino 的向量存储和检索系统中,增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/indexer.Indexer` -- 易于集成到 Eino 的索引系统 -- 可配置的 OpenSearch 参数 -- 支持向量相似度搜索 -- 支持批量索引操作 -- 支持自定义字段映射 -- 灵活的文档向量化支持 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/indexer/opensearch3@latest -``` - -## 快速开始 - -以下是一个如何使用该索引器的简单示例,更多细节可参考 components/indexer/opensearch3/examples/indexer/main.go: - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v4" - "github.com/opensearch-project/opensearch-go/v4/opensearchapi" - - "github.com/cloudwego/eino-ext/components/indexer/opensearch3" -) - -func main() { - ctx := context.Background() - - client, err := opensearchapi.NewClient(opensearchapi.Config{ - Client: opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }, - }) - if err != nil { - log.Fatal(err) - } - - // 创建 embedding 组件 - emb := createYourEmbedding() - - // 创建 opensearch indexer 组件 - indexer, _ := opensearch3.NewIndexer(ctx, &opensearch3.IndexerConfig{ - Client: client, - Index: "your_index_name", - BatchSize: 10, - DocumentToFields: func(ctx context.Context, doc *schema.Document) (map[string]opensearch3.FieldValue, error) { - return map[string]opensearch3.FieldValue{ - "content": { - Value: doc.Content, - EmbedKey: "content_vector", - }, - }, nil - }, - Embedding: emb, - }) - - docs := []*schema.Document{ - {ID: "1", Content: "example content"}, - } - - ids, _ := indexer.Store(ctx, docs) - fmt.Println(ids) -} -``` - -## 配置说明 - -可以通过 `IndexerConfig` 结构体配置索引器: - -```go -type IndexerConfig struct { - Client *opensearchapi.Client // 必填:OpenSearch 客户端实例 - Index string // 必填:用于存储文档的索引名称 - BatchSize int // 选填:最大文本嵌入批次大小(默认:5) - - // 必填:将 Document 字段映射到 OpenSearch 字段的函数 - 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) // 选填:自定义字符串转换函数 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [OpenSearch Go 客户端文档](https://github.com/opensearch-project/opensearch-go) diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md deleted file mode 100644 index a52f5466f58..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_redis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Indexer - Redis -weight: 0 ---- - -You can use redis-stack as an indexer. diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md deleted file mode 100644 index e8567464b7e..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Indexer - volc VikingDB -weight: 0 ---- - -## **基本介绍** - -火山引擎 VikingDB 向量索引器是 Indexer 接口的一个实现,用于将文档内容存储到火山引擎的 VikingDB 向量数据库中。该组件实现了 [[🚧]Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) - -### **火山引擎 VikingDB 服务介绍** - -火山引擎 VikingDB 是一个高性能的向量数据库服务,提供向量存储、检索和向量化等功能。本组件通过火山引擎 SDK 与服务进行交互,支持两种向量化方式: - -- 使用 VikingDB 内置的向量化方法(Embedding V2) -- 使用自定义的向量嵌入模型 - -## **使用方式** - -### **组件初始化** - -火山引擎 VikingDB 索引器通过 `NewIndexer` 函数进行初始化,主要配置参数如下: - -```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", // 服务地址 - Region: "cn-beijing", // 区域 - AK: "your-ak", // Access Key - SK: "your-sk", // Secret Key - Scheme: "https", // 协议 - ConnectionTimeout: 30, // 连接超时时间(秒) - - Collection: "your-collection", // 集合名称 - - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: true, // 是否使用内置向量化 - ModelName: "text2vec-base", // 模型名称 - UseSparse: true, // 是否使用稀疏向量 - Embedding: embedder, // 自定义向量嵌入器 - }, - - AddBatchSize: 5, // 批量添加大小 -}) -``` - -### **完整使用示例** - -#### **使用内置向量化** - -```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() - - // 初始化索引器 - 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", - UseSparse: true, - }, - }) - if err != nil { - panic(err) - } - - // 准备文档 - docs := []*schema.Document{ - { - Content: "这是第一个文档的内容", - }, - { - Content: "这是第二个文档的内容", - }, - } - - // 存储文档 - ids, err := idx.Store(ctx, docs) - if err != nil { - panic(err) - } - - // 处理返回的ID - for i, id := range ids { - println("文档", i+1, "的存储ID:", id) - } -} -``` - -#### **使用自定义向量嵌入** - -```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" -) - -func main() { - ctx := context.Background() - - // 初始化向量嵌入器(openai 示例) - embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{}) - if err != nil { - panic(err) - } - - // 初始化索引器 - 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, - }, - }) - if err != nil { - panic(err) - } - - // 准备文档 - docs := []*schema.Document{ - { - Content: "Document content one", - }, - { - Content: "Document content two", - }, - } - - // 存储文档 - ids, err := idx.Store(ctx, docs) - if err != nil { - panic(err) - } - - // 处理返回的ID - for i, id := range ids { - println("文档", i+1, "的存储ID:", id) - } -} -``` - -## **相关文档** - -- [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) -- [火山引擎 VikingDB 使用指南](https://www.volcengine.com/docs/84313/1254617) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/_index.md b/content/zh/docs/eino/ecosystem_integration/retriever/_index.md index cd054e97567..0fbf3373eb8 100644 --- a/content/zh/docs/eino/ecosystem_integration/retriever/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/retriever/_index.md @@ -1,10 +1,34 @@ --- Description: "" -date: "2025-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Retriever -weight: 0 +weight: 7 --- -Retriever 用于把 [Indexer](/zh/docs/eino/ecosystem_integration/indexer) 构建索引之后的内容进行召回,在 AI 应用中,一般使用 [Embedding](/zh/docs/eino/ecosystem_integration/embedding) 进行语义相似性召回。 +# Retriever 组件列表 + +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + + + + + +
    组件名称中文文档English Docs
    DifyREADME_zh.mdREADME.md
    Elasticsearch 7README_zh.mdREADME.md
    Elasticsearch 8README_zh.mdREADME.md
    Elasticsearch 9README_zh.mdREADME.md
    Milvus v1 (旧版)README_zh.mdREADME.md
    Milvus v2 (推荐)README_zh.mdREADME.md
    OpenSearch 2README_zh.mdREADME.md
    OpenSearch 3README_zh.mdREADME.md
    RedisREADME_zh.mdREADME.md
    volc VikingDBREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md deleted file mode 100644 index 4048cba1f75..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_dify.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Dify -weight: 0 ---- - -## **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" - - "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()) - } -} -``` - -## **配置** - -检索器可以使用 `RetrieverConfig` 结构体进行配置: - -```go -type RetrieverConfig struct { - APIKey string // APIKey 是 Dify API 的认证密钥 - Endpoint string // Endpoint 是 Dify API 的服务地址, 默认为: https://api.dify.ai/v1 - DatasetID string // DatasetID 是知识库的唯一标识 - RetrievalModel *RetrievalModel // RetrievalModel 检索参数 选填,如不填,按照默认方式召回 - Timeout time.Duration // Timeout 定义了 HTTP 连接超时时间 -} - -type RetrievalModel struct { - SearchMethod SearchMethod // 搜索方法 - RerankingEnable *bool // 启用重排序 - RerankingMode *string // 重排序模式 - RerankingModel *RerankingModel // 重排序模型设置 - Weights *float64 // 搜索权重 - TopK *int // 要检索的文档数量 - ScoreThresholdEnabled *bool // 启用分数阈值 - ScoreThreshold *float64 // 最小分数阈值 -} -``` - -## **文档元数据** - -检索器会为检索到的文档添加以下元数据: - -- `orig_doc_id`:Dify 中的原始文档 ID -- `orig_doc_name`:Dify 中的原始文档名称 -- `keywords`:从文档中提取的关键词 - -你可以使用以下辅助函数访问这些元数据: - -```go -docID := dify.GetOrgDocID(doc) -docName := dify.GetOrgDocName(doc) -keywords := dify.GetKeywords(doc) -``` - -## **更多详情** - -- [Dify 文档](https://github.com/langgenius/dify) -- [Eino 文档](https://github.com/cloudwego/eino) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md deleted file mode 100644 index 1d58af5840a..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch7.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 7 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 Elasticsearch 7.x 检索器实现,实现了 `Retriever` 接口。该组件可以与 Eino 的检索系统无缝集成,提供增强的语义搜索能力。 - -## 功能特性 - -- 实现了 `github.com/cloudwego/eino/components/retriever.Retriever` -- 易于集成 Eino 检索系统 -- 可配置 Elasticsearch 参数 -- 多种搜索模式: - - 精确匹配(文本搜索) - - 稠密向量相似度(语义搜索) - - 原始字符串(自定义查询) -- 支持默认结果解析器及自定义 -- 支持过滤器以进行精细查询 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/es7@latest -``` - -## 快速开始 - -以下是一个使用检索器的简单示例: - -```go -import ( - "context" - "fmt" - "os" - - "github.com/cloudwego/eino/schema" - elasticsearch "github.com/elastic/go-elasticsearch/v7" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/es7" - "github.com/cloudwego/eino-ext/components/retriever/es7/search_mode" -) - -func main() { - ctx := context.Background() - - // 连接到 Elasticsearch - username := os.Getenv("ES_USERNAME") - password := os.Getenv("ES_PASSWORD") - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - - // 使用 Volcengine ARK 创建用于向量搜索的 embedding 组件 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 创建带有稠密向量相似度搜索的检索器 - retriever, _ := es7.NewRetriever(ctx, &es7.RetrieverConfig{ - Client: client, - Index: "my_index", - TopK: 10, - SearchMode: search_mode.DenseVectorSimilarity(search_mode.DenseVectorSimilarityTypeCosineSimilarity, "content_vector"), - Embedding: emb, - }) - - // 检索文档 - docs, _ := retriever.Retrieve(ctx, "search query") - - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s, Score: %v\n", doc.ID, doc.Content, doc.Score()) - } -} -``` - -## 搜索模式 - -### 精确匹配 (Exact Match) - -使用 Elasticsearch match 查询进行简单的文本搜索: - -```go -searchMode := search_mode.ExactMatch("content") -``` - -### 稠密向量相似度 (Dense Vector Similarity) - -使用带有稠密向量的 script_score 进行语义搜索: - -```go -// 余弦相似度 -searchMode := search_mode.DenseVectorSimilarity( - search_mode.DenseVectorSimilarityTypeCosineSimilarity, - "content_vector", -) - -// 其他相似度类型: -// - DenseVectorSimilarityTypeDotProduct -// - DenseVectorSimilarityTypeL1Norm -// - DenseVectorSimilarityTypeL2Norm -``` - -### 原始字符串请求 (Raw String Request) - -直接传递自定义 JSON 查询: - -```go -searchMode := search_mode.RawStringRequest() - -// 然后使用 JSON 查询字符串作为搜索查询 -query := `{"query": {"bool": {"must": [{"match": {"content": "search term"}}]}}}` -docs, _ := retriever.Retrieve(ctx, query) -``` - -## 配置 - -```go -type RetrieverConfig struct { - Client *elasticsearch.Client // 必填:Elasticsearch 客户端 - Index string // 必填:索引名称 - TopK int // 选填:结果数量(默认:10) - ScoreThreshold *float64 // 选填:最低分数阈值 - SearchMode SearchMode // 必填:搜索策略 - ResultParser func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) // 选填:自定义解析器 - Embedding embedding.Embedder // 向量搜索模式必填 -} -``` - -## 使用过滤器 - -使用 `WithFilters` 选项添加查询过滤器: - -```go -filters := []interface{}{ - map[string]interface{}{ - "term": map[string]interface{}{ - "category": "news", - }, - }, -} - -docs, _ := retriever.Retrieve(ctx, "query", es7.WithFilters(filters)) -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [Elasticsearch Go 客户端文档](https://github.com/elastic/go-elasticsearch) -- [Elasticsearch 7.10 Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/7.10/query-dsl.html) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md deleted file mode 100644 index bbd13e45965..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_elasticsearch9.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 9 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -为 [Eino](https://github.com/cloudwego/eino) 实现的 Elasticsearch 9.x 检索器,实现了 `Retriever` 接口。这使得可以与 Eino 的向量检索系统无缝集成,从而增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/retriever.Retriever` -- 易于集成 Eino 的检索系统 -- 可配置 Elasticsearch 参数 -- 支持向量相似度搜索 -- 多种搜索模式(包括近似搜索) -- 自定义结果解析支持 -- 灵活的文档过滤 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/es9@latest -``` - -## 快速开始 - -这里是使用近似搜索模式的快速示例,更多细节请阅读 components/retriever/es9/examples/approximate/approximate.go: - -```go -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/components/embedding" - "github.com/cloudwego/eino/schema" - "github.com/elastic/go-elasticsearch/v9" - "github.com/elastic/go-elasticsearch/v9/typedapi/types" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/es9" - "github.com/cloudwego/eino-ext/components/retriever/es9/search_mode" -) - -const ( - 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") - - var cert []byte - var err error - if httpCACertPath != "" { - cert, err = os.ReadFile(httpCACertPath) - if err != nil { - log.Fatalf("read file failed, err=%v", err) - } - } - - client, _ := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{"https://localhost:9200"}, - Username: username, - Password: password, - CACert: cert, - }) - - // 2. 创建 embedding 组件 (使用 Ark) - // 请将 "ARK_API_KEY", "ARK_REGION", "ARK_MODEL" 替换为实际配置 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 创建 retriever 组件 - retriever, _ := es9.NewRetriever(ctx, &es9.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, - }) - - // 不带过滤器的搜索 - docs, _ := retriever.Retrieve(ctx, "tourist attraction") - - // 带过滤器的搜索 - docs, _ = retriever.Retrieve(ctx, "tourist attraction", - es9.WithFilters([]types.Query{{ - Term: map[string]types.TermQuery{ - fieldExtraLocation: { - CaseInsensitive: of(true), - Value: "China", - }, - }, - }}), - ) - - fmt.Printf("retrieved docs: %+v\n", docs) -} - -func of[T any](v T) *T { - return &v -} -``` - -## 配置 - -可以使用 `RetrieverConfig` 结构体配置检索器: - -```go -type RetrieverConfig struct { - Client *elasticsearch.Client // 必填: Elasticsearch 客户端实例 - Index string // 必填: 检索文档的索引名称 - TopK int // 必填: 返回的结果数量 - - // 必填: 搜索模式配置 - SearchMode search_mode.SearchMode - - // 选填: 将 Elasticsearch hits 解析为 Document 的函数 - // 如果未提供,将使用默认解析器: - // 1. 从 source 中提取 "content" 字段作为 Document.Content - // 2. 将其他 source 字段作为 Document.MetaData - ResultParser func(ctx context.Context, hit types.Hit) (*schema.Document, error) - - // 选填: 仅在需要查询向量化时必填 - Embedding embedding.Embedder -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [Elasticsearch Go Client 文档](https://github.com/elastic/go-elasticsearch) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md deleted file mode 100644 index b1360681924..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_es8.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Elasticsearch 8 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -## **ES8 检索器** - -这是一个 [Eino](https://github.com/cloudwego/eino) 的 Elasticsearch 8.x 检索器实现,它实现了 `Retriever` 接口。这使得与 Eino 的向量检索系统无缝集成,从而增强了语义搜索能力。 - -## **特性** - -- 实现了 `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" - elasticsearch "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" -) - -const ( - 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) - } -} -``` - -## **配置** - -检索器可以通过 `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 -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [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 deleted file mode 100644 index 2a0126ae6f2..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvus.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Milvus v1 (旧版) -weight: 0 ---- - -> **模块说明:** 本模块 (`EINO-ext/milvus`) 基于 `milvus-sdk-go` 实现。鉴于底层 SDK 已停止维护,且最高仅适配至 Milvus 2.4 版本,本模块仅作为向后兼容组件保留。 -> -> **建议:** 新接入的用户请直接使用 [Retriever - Milvus 2 (v2.5+) ](/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2) 模块以获得持续支持。 - -## **Milvus 搜索** - -基于 Milvus 2.x 的向量搜索实现,为 [Eino](https://github.com/cloudwego/eino) 提供了符合 `Retriever` 接口的存储方案。该组件可无缝集成 Eino 的向量存储和检索系统,增强语义搜索能力。 - -## **快速开始** - -### **安装** - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/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" -) - -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) - } -} -``` - -## **配置** - -```go -type RetrieverConfig struct { - // Client 是要调用的 milvus 客户端 - // 必需 - Client client.Client - - // Retriever 通用配置 - // Collection 是 milvus 数据库中的集合名称 - // 可选,默认值为 "eino_collection" - Collection string - // Partition 是集合的分区名称 - // 可选,默认值为空 - Partition []string - // VectorField 是集合中的向量字段���称 - // 可选,默认值为 "vector" - VectorField string - // OutputFields 是要返回的字段 - // 可选,默认值为空 - OutputFields []string - // DocumentConverter 是将搜索结果转换为 s.Document 的函数 - // 可选,默认值为 defaultDocumentConverter - DocumentConverter func(ctx context.Context, doc client.SearchResult) ([]*s.Document, error) - // MetricType 是向量的度量类型 - // 可选,默认值为 "HAMMING" - MetricType entity.MetricType - // TopK 是要返回的前 k 个结果 - // 可选,默认值为 5 - TopK int - // ScoreThreshold 是搜索结果的阈值 - // 可选,默认值为 0 - ScoreThreshold float64 - // SearchParams - // 可选,默认值为 entity.IndexAUTOINDEXSearchParam,级别为 1 - Sp entity.SearchParam - - // Embedding 是从 s.Document 的内容中嵌入需要嵌入的值的方法 - // 必需的 - Embedding embedding.Embedder -} -``` - -## 获取帮助 - -- [[集团内部版] Milvus 快速入门](https://bytedance.larkoffice.com/wiki/P3JBw4PtKiLGPhkUCQZcXbHFnkf) - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -### 外部参考 - -- [Milvus 文档](https://milvus.io/docs) -- [Milvus 索引类型](https://milvus.io/docs/index.md) -- [Milvus 度量类型](https://milvus.io/docs/metric.md) -- [milvus-sdk-go 参考](https://milvus.io/api-reference/go/v2.4.x/About.md) - -### 相关文档 - -- [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2.md deleted file mode 100644 index 294ec2a892b..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_milvusv2.md +++ /dev/null @@ -1,288 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: 'Retriever - Milvus v2 (推荐) ' -weight: 0 ---- - -> **向量数据库 Milvus 版介绍** -> -> 向量检索服务 Milvus 版为基于开源 Milvus 构建的全托管数据库服务,提供高效的非结构化数据检索能力,适用于多样化 AI 场景,客户无需再关心底层硬件资源,降低使用成本,提高整体效率。 -> -> 鉴于公司**内场**的 Milvus 服务采用标准 SDK,因此适用 **EINO-ext 社区版本**。 - -本包为 EINO 框架提供 Milvus 2.x (V2 SDK) 检索器实现,支持多种搜索模式的向量相似度搜索。 - -> **注意**: 本包需要 **Milvus 2.5+** 以支持服务器端函数(如 BM25),基础功能兼容低版本。 - -## 功能特性 - -- **Milvus V2 SDK**: 使用最新的 `milvus-io/milvus/client/v2` SDK -- **多种搜索模式**: 支持近似搜索、范围搜索、混合搜索、迭代器搜索和标量搜索 -- **稠密 + 稀疏混合搜索**: 结合稠密向量和稀疏向量,使用 RRF 重排序 -- **自定义结果转换**: 可配置的结果到文档转换 - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/milvus2 -``` - -## 快速开始 - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/milvus-io/milvus/client/v2/milvusclient" - - milvus2 "github.com/cloudwego/eino-ext/components/retriever/milvus2" - "github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode" -) - -func main() { - // 获取环境变量 - 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() - - // 创建 embedding 模型 - emb, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: arkApiKey, - Model: arkModel, - }) - if err != nil { - log.Fatalf("Failed to create embedding: %v", err) - return - } - - // 创建 retriever - retriever, err := milvus2.NewRetriever(ctx, &milvus2.RetrieverConfig{ - ClientConfig: &milvusclient.ClientConfig{ - Address: addr, - Username: username, - Password: password, - }, - Collection: "my_collection", - TopK: 10, - SearchMode: search_mode.NewApproximate(milvus2.COSINE), - Embedding: emb, - }) - if err != nil { - log.Fatalf("Failed to create retriever: %v", err) - return - } - log.Printf("Retriever created successfully") - - // 检索文档 - documents, err := retriever.Retrieve(ctx, "search query") - if err != nil { - log.Fatalf("Failed to retrieve: %v", err) - return - } - - // 打印文档 - for i, doc := range documents { - fmt.Printf("Document %d:\n", i) - fmt.Printf(" ID: %s\n", doc.ID) - fmt.Printf(" Content: %s\n", doc.Content) - fmt.Printf(" Score: %v\n", doc.Score()) - } -} -``` - -## 配置选项 - - - - - - - - - - - - - - - -
    字段类型默认值描述
    Client
    *milvusclient.Client
    -预配置的 Milvus 客户端(可选)
    ClientConfig
    *milvusclient.ClientConfig
    -客户端配置(Client 为空时必需)
    Collection
    string
    "eino_collection"
    集合名称
    TopK
    int
    5
    返回结果数量
    VectorField
    string
    "vector"
    稠密向量字段名
    SparseVectorField
    string
    "sparse_vector"
    稀疏向量字段名
    OutputFields
    []string
    所有字段结果中返回的字段
    SearchMode
    SearchMode
    -搜索策略(必需)
    Embedding
    embedding.Embedder
    -用于查询向量化的 Embedder(必需)
    DocumentConverter
    func
    默认转换器自定义结果到文档转换
    ConsistencyLevel
    ConsistencyLevel
    ConsistencyLevelDefault
    一致性级别 (
    ConsistencyLevelDefault
    使用 collection 的级别;不应用按请求覆盖)
    Partitions
    []string
    -要搜索的分区
    - -## 搜索模式 - -从 `github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode` 导入搜索模式。 - -### 近似搜索 (Approximate) - -标准的近似最近邻 (ANN) 搜索。 - -```go -mode := search_mode.NewApproximate(milvus2.COSINE) -``` - -### 范围搜索 (Range) - -在指定距离范围内搜索 (向量在 `Radius` 内)。 - -```go -// L2: 距离 <= Radius -// IP/Cosine: 分数 >= Radius -mode := search_mode.NewRange(milvus2.L2, 0.5). - WithRangeFilter(0.1) // 可选: 环形搜索的内边界 -``` - -### 稀疏搜索 (BM25) - -使用 BM25 进行纯稀疏向量搜索。需要 Milvus 2.5+ 支持稀疏向量字段并启用 Functions。 - -```go -// 纯稀疏搜索 (BM25) 需要指定 OutputFields 以获取内容 -// MetricType: BM25 (默认) 或 IP -mode := search_mode.NewSparse(milvus2.BM25) - -// 在配置中,使用 "*" 或特定字段以确保返回内容: -// OutputFields: []string{"*"} -``` - -### 混合搜索 (Hybrid - 稠密 + 稀疏) - -结合稠密向量和稀疏向量的多向量搜索,支持结果重排序。需要一个同时包含稠密和稀疏向量字段的集合(参见 indexer sparse 示例)。 - -```go -import ( - "github.com/milvus-io/milvus/client/v2/milvusclient" - milvus2 "github.com/cloudwego/eino-ext/components/retriever/milvus2" - "github.com/cloudwego/eino-ext/components/retriever/milvus2/search_mode" -) - -// 定义稠密 + 稀疏子请求的混合搜索 -hybridMode := search_mode.NewHybrid( - milvusclient.NewRRFReranker().WithK(60), // RRF 重排序器 - &search_mode.SubRequest{ - VectorField: "vector", // 稠密向量字段 - VectorType: milvus2.DenseVector, // 默认值,可省略 - TopK: 10, - MetricType: milvus2.L2, - }, - // 稀疏子请求 (Sparse SubRequest) - &search_mode.SubRequest{ - VectorField: "sparse_vector", // 稀疏向量字段 - VectorType: milvus2.SparseVector, // 指定稀疏类型 - TopK: 10, - MetricType: milvus2.BM25, // 使用 BM25 或 IP - }, -) - -// 创建 retriever (稀疏向量生成由 Milvus Function 服务器端处理) -retriever, err := milvus2.NewRetriever(ctx, &milvus2.RetrieverConfig{ - ClientConfig: &milvusclient.ClientConfig{Address: "localhost:19530"}, - Collection: "hybrid_collection", - VectorField: "vector", // 默认稠密字段 - SparseVectorField: "sparse_vector", // 默认稀疏字段 - TopK: 5, - SearchMode: hybridMode, - Embedding: denseEmbedder, // 稠密向量的标准 Embedder -}) -``` - -### 迭代器搜索 (Iterator) - -基于批次的遍历,适用于大结果集。 - -> [!WARNING] -> -> `Iterator` 模式的 `Retrieve` 方法会获取 **所有** 结果,直到达到总限制 (`TopK`) 或集合末尾。对于极大数据集,这可能会消耗大量内存。 - -```go -// 100 是批次大小 (每次网络调用的条目数) -mode := search_mode.NewIterator(milvus2.COSINE, 100). - WithSearchParams(map[string]string{"nprobe": "10"}) - -// 使用 RetrieverConfig.TopK 设置总限制 (IteratorLimit)。 -``` - -### 标量搜索 (Scalar) - -仅基于元数据过滤,不使用向量相似度(将过滤表达式作为查询)。 - -```go -mode := search_mode.NewScalar() - -// 使用过滤表达式查询 -docs, err := retriever.Retrieve(ctx, `category == "electronics" AND year >= 2023`) -``` - -### 稠密向量度量 (Dense) - - - - - - -
    度量类型描述
    L2
    欧几里得距离
    IP
    内积
    COSINE
    余弦相似度
    - -### 稀疏向量度量 (Sparse) - - - - - -
    度量类型描述
    BM25
    Okapi BM25 (BM25 搜索必需)
    IP
    内积 (适用于预计算的稀疏向量)
    - -### 二进制向量度量 (Binary) - - - - - - - - -
    度量类型描述
    HAMMING
    汉明距离
    JACCARD
    杰卡德距离
    TANIMOTO
    Tanimoto 距离
    SUBSTRUCTURE
    子结构搜索
    SUPERSTRUCTURE
    超结构搜索
    - -> **重要提示**: SearchMode 中的度量类型必须与创建集合时使用的索引度量类型一致。 - -## 示例 - -查看 [https://github.com/cloudwego/eino-ext/tree/main/components/retriever/milvus2/examples](https://github.com/cloudwego/eino-ext/tree/main/components/retriever/milvus2/examples) 录获取完整的示例代码: - -- [approximate](./examples/approximate) - 基础 ANN 搜索 -- [range](./examples/range) - 范围搜索示例 -- [hybrid](./examples/hybrid) - 混合多向量搜索 (稠密 + BM25) -- [hybrid_chinese](./examples/hybrid_chinese) - 中文混合搜索示例 -- [iterator](./examples/iterator) - 批次迭代器搜索 -- [scalar](./examples/scalar) - 标量/元数据过滤 -- [grouping](./examples/grouping) - 分组搜索结果 -- [filtered](./examples/filtered) - 带过滤的向量搜索 -- [sparse](./examples/sparse) - 纯稀疏搜索示例 (BM25) - -## 获取帮助 - -- [[集团内部版] Milvus 快速入门](https://bytedance.larkoffice.com/wiki/P3JBw4PtKiLGPhkUCQZcXbHFnkf) - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -### 外部参考 - -- [Milvus 文档](https://milvus.io/docs) -- [Milvus 索引类型](https://milvus.io/docs/index.md) -- [Milvus 度量类型](https://milvus.io/docs/metric.md) -- [Milvus Go SDK 参考](https://milvus.io/api-reference/go/v2.6.x/About.md) - -### 相关文档 - -- [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch2.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch2.md deleted file mode 100644 index be636a14777..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch2.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - OpenSearch 2 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 OpenSearch 2 检索器实现,实现了 `Retriever` 接口。这使得 OpenSearch 可以无缝集成到 Eino 的向量检索系统中,增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/retriever.Retriever` -- 易于集成到 Eino 的检索系统 -- 可配置的 OpenSearch 参数 -- 支持向量相似度搜索和关键词搜索 -- 多种搜索模式: - - KNN (近似最近邻) - - Exact Match (精确匹配/关键词) - - Raw String (原生 JSON 请求体) - - Dense Vector Similarity (脚本评分,稠密向量) - - Neural Sparse (稀疏向量) -- 支持自定义结果解析 - -## 搜索模式兼容性 - - - - - - - - - - - -
    搜索模式最低 OpenSearch 版本说明
    ExactMatch
    1.0+标准查询 DSL
    RawString
    1.0+标准查询 DSL
    DenseVectorSimilarity
    1.0+使用
    script_score
    和 painless 向量函数
    Approximate
    (KNN)
    1.0+自 1.0 起支持基础 KNN。高效过滤 (Post-filtering) 需要 2.4+ (Lucene HNSW) 或 2.9+ (Faiss)。
    Approximate
    (Hybrid)
    2.10+生成
    bool
    查询。需要 2.10+
    normalization-processor
    支持高级分数归一化 (Convex Combination)。基础
    bool
    查询在早期版本 (1.0+) 也可工作。
    Approximate
    (RRF)
    2.19+需要
    score-ranker-processor
    (2.19+) 和
    neural-search
    插件。
    NeuralSparse
    (Query Text)
    2.11+需要
    neural-search
    插件和已部署的模型。
    NeuralSparse
    (TokenWeights)
    2.11+需要
    neural-search
    插件。
    - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/opensearch2@latest -``` - -## 快速开始 - -以下是一个如何使用该检索器的简单示例: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v2" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/opensearch2" - "github.com/cloudwego/eino-ext/components/retriever/opensearch2/search_mode" -) - -func main() { - ctx := context.Background() - - client, err := opensearch.NewClient(opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }) - if err != nil { - log.Fatal(err) - } - - // 使用 Volcengine ARK 创建 embedding 组件 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 创建检索器组件 - retriever, _ := opensearch2.NewRetriever(ctx, &opensearch2.RetrieverConfig{ - Client: client, - Index: "your_index_name", - TopK: 5, - // 选择搜索模式 - SearchMode: search_mode.Approximate(&search_mode.ApproximateConfig{ - VectorField: "content_vector", - K: 5, - }), - ResultParser: func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) { - // 解析 hit map 为 Document - id, _ := hit["_id"].(string) - source := hit["_source"].(map[string]interface{}) - content, _ := source["content"].(string) - return &schema.Document{ID: id, Content: content}, nil - }, - Embedding: emb, - }) - - docs, err := retriever.Retrieve(ctx, "search query") - if err != nil { - fmt.Printf("retrieve error: %v\n", err) - return - } - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s\n", doc.ID, doc.Content) - } -} -``` - -## 配置说明 - -可以通过 `RetrieverConfig` 结构体配置检索器: - -```go -type RetrieverConfig struct { - Client *opensearch.Client // 必填:OpenSearch 客户端实例 - Index string // 必填:从中检索文档的索引名称 - TopK int // 必填:返回的结果数量 - - // 必填:搜索模式配置 - // search_mode 包中提供了预置实现: - // - search_mode.Approximate(&ApproximateConfig{...}) - // - search_mode.ExactMatch(field) - // - search_mode.RawStringRequest() - // - search_mode.DenseVectorSimilarity(type, vectorField) - // - search_mode.NeuralSparse(vectorField, &NeuralSparseConfig{...}) - SearchMode SearchMode - - // 选填:将 OpenSearch hits (map[string]interface{}) 解析为 Document 的函数 - // 如果未提供,将使用默认解析器。 - ResultParser func(ctx context.Context, hit map[string]interface{}) (doc *schema.Document, err error) - - // 选填:仅当需要查询向量化时必填 - Embedding embedding.Embedder -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [OpenSearch Go 客户端文档](https://github.com/opensearch-project/opensearch-go) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md deleted file mode 100644 index 47b7498c0c5..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_opensearch3.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - OpenSearch 3 -weight: 0 ---- - -> **云搜索服务介绍** -> -> 云搜索服务(Cloud Search)是一个全托管、一站式信息检索与分析平台,提供了 ElasticSearch 和 OpenSearch 引擎,支持全文检索、向量检索、混合搜索及时空检索等多种核心能力。 - -[Eino](https://github.com/cloudwego/eino) 的 OpenSearch 3 检索器实现,实现了 `Retriever` 接口。这使得 OpenSearch 可以无缝集成到 Eino 的向量检索系统中,增强语义搜索能力。 - -## 功能特性 - -- 实现 `github.com/cloudwego/eino/components/retriever.Retriever` -- 易于集成到 Eino 的检索系统 -- 可配置的 OpenSearch 参数 -- 支持向量相似度搜索和关键词搜索 -- 多种搜索模式: - - KNN (近似最近邻) - - Exact Match (精确匹配/关键词) - - Raw String (原生 JSON 请求体) - - Dense Vector Similarity (脚本评分,稠密向量) - - Neural Sparse (稀疏向量) -- 支持自定义结果解析 - -## 搜索模式兼容性 - - - - - - - - - - - -
    搜索模式最低 OpenSearch 版本说明
    ExactMatch
    1.0+标准查询 DSL
    RawString
    1.0+标准查询 DSL
    DenseVectorSimilarity
    1.0+使用
    script_score
    和 painless 向量函数
    Approximate
    (KNN)
    1.0+自 1.0 起支持基础 KNN。高效过滤 (Post-filtering) 需要 2.4+ (Lucene HNSW) 或 2.9+ (Faiss)。
    Approximate
    (Hybrid)
    2.10+生成
    bool
    查询。需要 2.10+
    normalization-processor
    支持高级分数归一化 (Convex Combination)。基础
    bool
    查询在早期版本 (1.0+) 也可工作。
    Approximate
    (RRF)
    2.19+需要
    score-ranker-processor
    (2.19+) 和
    neural-search
    插件。
    NeuralSparse
    (Query Text)
    2.11+需要
    neural-search
    插件和已部署的模型。
    NeuralSparse
    (TokenWeights)
    2.11+需要
    neural-search
    插件。
    - -## 安装 - -```bash -go get github.com/cloudwego/eino-ext/components/retriever/opensearch3@latest -``` - -## 快速开始 - -以下是一个如何使用该检索器的简单示例: - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino/schema" - opensearch "github.com/opensearch-project/opensearch-go/v4" - "github.com/opensearch-project/opensearch-go/v4/opensearchapi" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/retriever/opensearch3" - "github.com/cloudwego/eino-ext/components/retriever/opensearch3/search_mode" -) - -func main() { - ctx := context.Background() - - client, err := opensearchapi.NewClient(opensearchapi.Config{ - Client: opensearch.Config{ - Addresses: []string{"http://localhost:9200"}, - Username: username, - Password: password, - }, - }) - if err != nil { - log.Fatal(err) - } - - // 使用 Volcengine ARK 创建 embedding 组件 - emb, _ := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ - APIKey: os.Getenv("ARK_API_KEY"), - Region: os.Getenv("ARK_REGION"), - Model: os.Getenv("ARK_MODEL"), - }) - - // 创建检索器组件 - retriever, _ := opensearch3.NewRetriever(ctx, &opensearch3.RetrieverConfig{ - Client: client, - Index: "your_index_name", - TopK: 5, - // 选择搜索模式 - SearchMode: search_mode.Approximate(&search_mode.ApproximateConfig{ - VectorField: "content_vector", - K: 5, - }), - ResultParser: func(ctx context.Context, hit map[string]interface{}) (*schema.Document, error) { - // 解析 hit map 为 Document - id, _ := hit["_id"].(string) - source := hit["_source"].(map[string]interface{}) - content, _ := source["content"].(string) - return &schema.Document{ID: id, Content: content}, nil - }, - Embedding: emb, - }) - - docs, err := retriever.Retrieve(ctx, "search query") - if err != nil { - fmt.Printf("retrieve error: %v\n", err) - return - } - for _, doc := range docs { - fmt.Printf("ID: %s, Content: %s\n", doc.ID, doc.Content) - } -} -``` - -## 配置说明 - -可以通过 `RetrieverConfig` 结构体配置检索器: - -```go -type RetrieverConfig struct { - Client *opensearchapi.Client // 必填:OpenSearch 客户端实例 - Index string // 必填:从中检索文档的索引名称 - TopK int // 必填:返回的结果数量 - - // 必填:搜索模式配置 - // search_mode 包中提供了预置实现: - // - search_mode.Approximate(&ApproximateConfig{...}) - // - search_mode.ExactMatch(field) - // - search_mode.RawStringRequest() - // - search_mode.DenseVectorSimilarity(type, vectorField) - // - search_mode.NeuralSparse(vectorField, &NeuralSparseConfig{...}) - SearchMode SearchMode - - // 选填:将 OpenSearch hits (map[string]interface{}) 解析为 Document 的函数 - // 如果未提供,将使用默认解析器。 - ResultParser func(ctx context.Context, hit map[string]interface{}) (doc *schema.Document, err error) - - // 选填:仅当需要查询向量化时必填 - Embedding embedding.Embedder -} -``` - -## 获取帮助 - -如果有任何问题 或者任何功能建议,欢迎进这个群 oncall。 - -- [Eino 文档](https://www.cloudwego.io/zh/docs/eino/) -- [OpenSearch Go 客户端文档](https://github.com/opensearch-project/opensearch-go) diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md deleted file mode 100644 index 1d777929ce6..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_redis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - Redis -weight: 0 ---- - -你可以使用 redis-stack 作为 retriever。 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 deleted file mode 100644 index faddb2f4e7d..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_knowledge.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Retriever - volc Knowledge -weight: 0 ---- - -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 deleted file mode 100644 index ac0fdd5a916..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -Description: "" -date: "2025-01-20" -lastmod: "" -tags: [] -title: Retriever - volc VikingDB -weight: 0 ---- - -## **基本介绍** - -火山引擎 VikingDB 检索器是 Retriever 接口的一个实现,火山引擎 VikingDB 是火山引擎提供的向量数据库服务,提供了高性能的向量检索能力,本组件通过火山引擎 VikingDB Go SDK 与服务进行交互。该组件实现了 [[🚧]Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) - -## **使用方式** - -### **组件初始化** - -火山引擎 VikingDB 检索器通过 `NewRetriever` 函数进行初始化,主要配置参数如下: - -```go -import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb" - -retriever, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - // 服务配置 - Host: "api-vikingdb.volces.com", // 服务地址 - Region: "cn-beijing", // 区域 - AK: "your-ak", // 访问密钥 ID - SK: "your-sk", // 访问密钥密码 - Scheme: "https", // 协议 - ConnectionTimeout: 30, // 连接超时时间(秒) - - // 数据配置 - Collection: "collection-name", // 集合名称 - Index: "index-name", // 索引名称 - - // 向量化配置 - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: true, // 是否使用内置向量化 - ModelName: "model-name",// 模型名称 - UseSparse: true, // 是否使用稀疏向量 - DenseWeight: 0.5, // 稠密向量权重 - Embedding: embedder, // 自定义向量化器 - }, - - // 检索配置 - Partition: "partition", // 分区名称 - TopK: ptrOf(100), // 返回结果数量 - ScoreThreshold: ptrOf(0.7), // 相似度阈值 - - // 过滤配置 - FilterDSL: map[string]any{ // DSL 过滤条件 - "term": map[string]any{ - "field": "value", - }, - }, -}) -``` - -### **检索文档** - -文档检索通过 `Retrieve` 方法实现: - -```go -docs, err := retriever.Retrieve(ctx, "查询文本", retriever.WithTopK(5)) -``` - -### **完整使用示例** - -#### **基本检索** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb" -) - -func main() { - ctx := context.Background() - - // 初始化检索器 - r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api-vikingdb.volces.com", - Region: "cn-beijing", - AK: "your-ak", - SK: "your-sk", - Collection: "your-collection", - Index: "your-index", - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: true, - ModelName: "model-name", - UseSparse: true, - DenseWeight: 0.5, - }, - TopK: ptrOf(5), - }) - if err != nil { - panic(err) - } - - // 执行检索 - docs, err := r.Retrieve(ctx, "如何使用 VikingDB?") - if err != nil { - panic(err) - } - - // 处理结果 - for _, doc := range docs { - println("文档ID:", doc.ID) - println("内容:", doc.Content) - println("相似度:", doc.MetaData["_score"]) - } -} -``` - -#### **自定义向量化** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb" - "github.com/cloudwego/eino/components/embedding" -) - -func main() { - ctx := context.Background() - - // 初始化向量化器 (以 openai 为例) - embedder, err := &openai.NewEmbedder(ctx, &openai.EmbeddingConfig{}) - if err != nil { - panic(err) - } - - // 初始化检索器 - r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api-vikingdb.volces.com", - Region: "cn-beijing", - AK: "your-ak", - SK: "your-sk", - Collection: "your-collection", - Index: "your-index", - EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ - UseBuiltin: false, - Embedding: embedder, - }, - }) - if err != nil { - panic(err) - } - - // 执行检索 - docs, err := r.Retrieve(ctx, "查询文本") - if err != nil { - panic(err) - } - - // 处理结果 - for _, doc := range docs { - println(doc.Content) - } -} -``` - -## **相关文档** - -- [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) -- [火山引擎 VikingDB 文档](https://www.volcengine.com/docs/84313) diff --git a/content/zh/docs/eino/ecosystem_integration/tool/_index.md b/content/zh/docs/eino/ecosystem_integration/tool/_index.md index 4e31e926967..0a277e760fb 100644 --- a/content/zh/docs/eino/ecosystem_integration/tool/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/tool/_index.md @@ -1,10 +1,33 @@ --- Description: "" -date: "2025-07-21" +date: "2026-03-02" lastmod: "" tags: [] title: Tool -weight: 0 +weight: 4 --- +# Tool 组件列表 +本分类的各组件详细文档请参考 GitHub README: + + + + + + + + + + + + +
    组件名称中文文档English Docs
    MCPREADME_zh.mdREADME.md
    BingsearchREADME_zh.mdREADME.md
    BrowserUseREADME_zh.mdREADME.md
    CommandLineREADME_zh.mdREADME.md
    DuckDuckGoSearchREADME_zh.mdREADME.md
    GooglesearchREADME_zh.mdREADME.md
    HTTPRequestREADME_zh.mdREADME.md
    SequentialThinkingREADME_zh.mdREADME.md
    WikipediaREADME_zh.mdREADME.md
    + +--- + +**说明**: + +- 上述链接直接指向 GitHub 仓库的最新文档 +- 中文文档和英文文档内容同步更新 +- 如需查看历史版本或提交文档修改建议,请访问 GitHub 仓库 diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md deleted file mode 100644 index 6b0e45134a2..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_bingsearch.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - Bingsearch -weight: 0 ---- - -## **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" -) - -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) - } - // ... 配置并使用 ToolsNode -``` - -## **配置** - -工具可以通过 `Config` 结构体进行配置: - -```go -// Config represents the Bing search tool configuration. -type Config struct { -// 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 -} -``` - -## **Search** - -### **请求 Schema** - -```go -type SearchRequest struct { - Query string `json:"query" jsonschema_description:"The query to search the web for"` - Offset int `json:"page" jsonschema_description:"The index of the first result to return, default is 0"` -} -``` - -### **响应 Schema** - -```go -type SearchResponse struct { - Results []*searchResult `json:"results" jsonschema_description:"The results of the search"` -} - -type searchResult struct { - Title string `json:"title" jsonschema_description:"The title of the search result"` - URL string `json:"url" jsonschema_description:"The link of the search result"` - Description string `json:"description" jsonschema_description:"The description of the search result"` -} -``` - -## **更多详情** - -- 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 deleted file mode 100644 index 0e845495694..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_browseruse.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - BrowserUse -weight: 0 ---- - -## **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** - -- Implements `github.com/cloudwego/eino/components/tool.BaseTool` -- Easy integration with Eino's tool system -- Support for executing browser actions - -## **Install****ation** - -```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: - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "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() -} -``` - -## **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 deleted file mode 100644 index 2963fab5974..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_commandline.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - CommandLine -weight: 0 ---- - -## **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** - -- Implements `github.com/cloudwego/eino/components/tool.InvokableTool` -- Easy integration with Eino's tool system -- Support executing command-line instructions in Docker containers - -## **Install****ation** - -```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: - -Editor: - -```go -package main - -import ( - "context" - "log" - - "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) -} -``` - -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. - */ - -package main - -import ( - "context" - "log" - - "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) -} -``` - -## **For More Details** - -- [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 deleted file mode 100644 index 6be045c0c70..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -Description: "" -date: "2025-12-01" -lastmod: "" -tags: [] -title: Tool - DuckDuckGoSearch -weight: 0 ---- - -## **基本介绍** - -DuckDuckGo 搜索工具是 Tool InvokableTool 接口的一个实现,用于通过 DuckDuckGo 搜索引擎进行网络搜索,DuckDuckGo 是一个注重隐私的搜索引擎,不会追踪用户的搜索行为,重点是无需 api key 鉴权即可直接使用。该组件实现了 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide) - -## **使用方式** - -### **组件初始化** - -DuckDuckGo 搜索工具通过 `NewTextSearchTool` 函数进行初始化,主要配置参数如下: - -```go -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: ddgsearch.RegionWT, // The geographical region for results. - MaxResults: 3, // Limit the number of results returned. -}) -``` - -### **搜索参数** - -搜索请求支持以下参数: - -```go -type TextSearchRequest struct { - // Query is the user's search query - Query string `json:"query"` - // TimeRange is the search time range - // Default: TimeRangeAny - TimeRange TimeRange `json:"time_range"` -} -``` - -### **完整使用示例** - -```go -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "time" - - "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2" -) - -func main() { - ctx := context.Background() - - // Create configuration - config := &duckduckgo.Config{ - MaxResults: 3, // Limit to return 20 results - Region: duckduckgo.RegionWT, - Timeout: 10 * time.Second, - } - - // Create search client - tool, err := duckduckgo.NewTextSearchTool(ctx, config) - if err != nil { - log.Fatalf("NewTextSearchTool of duckduckgo failed, err=%v", err) - } - - results := make([]*duckduckgo.TextSearchResult, 0, config.MaxResults) - - searchReq := &duckduckgo.TextSearchRequest{ - Query: "eino", - } - jsonReq, err := json.Marshal(searchReq) - if err != nil { - log.Fatalf("Marshal of search request failed, err=%v", err) - } - - resp, err := tool.InvokableRun(ctx, string(jsonReq)) - if err != nil { - log.Fatalf("Search of duckduckgo failed, err=%v", err) - } - - var searchResp duckduckgo.TextSearchResponse - if err = json.Unmarshal([]byte(resp), &searchResp); err != nil { - log.Fatalf("Unmarshal of search response failed, err=%v", err) - } - - results = append(results, searchResp.Results...) - - // Print results - fmt.Println("Search Results:") - fmt.Println("==============") - fmt.Printf("%s\n", searchResp.Message) - for i, result := range results { - fmt.Printf("\n%d. Title: %s\n", i+1, result.Title) - fmt.Printf(" URL: %s\n", result.URL) - fmt.Printf(" Summary: %s\n", result.Summary) - } - fmt.Println("") - fmt.Println("==============") -} -``` - -### **搜索结果示例** - -```json -{ - "message": "Found 3 results successfully.", - "results": [ - { - "title": "GitHub - cloudwego/eino: The ultimate LLM/AI application development ...", - "url": "https://github.com/cloudwego/eino", - "summary": "Eino ['aino] (pronounced similarly to \"I know\") aims to be the ultimate LLM application development framework in Golang. Drawing inspirations 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 ..." - }, - { - "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." - } - ] -} -``` - -## **相关文档** - -- [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide) -- [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch) -- [DuckDuckGo 帮助文档](https://duckduckgo.com/duckduckgo-help-pages/settings/params/) diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md deleted file mode 100644 index 07619d08ec5..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -Description: "" -date: "2025-11-20" -lastmod: "" -tags: [] -title: Tool - Googlesearch -weight: 0 ---- - -## **基本介绍** - -Google 搜索工具是 Eino InvokableTool 接口的一个实现,用于通过 Google Custom Search API 进行网络搜索。该组件实现了 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide)。 - -## **使用方式** - -### **组件初始化** - -Google 搜索工具通过 `NewGoogleSearchTool` 函数进行初始化,主要配置参数如下: - -```go -import "github.com/cloudwego/eino-ext/components/tool/googlesearch" - -tool, err := googlesearch.NewTool(ctx, &googlesearch.Config{ - APIKey: "your-api-key", // Google API 密钥 - SearchEngineID: "your-engine-id", // 搜索引擎 ID - BaseURL: "custom-base-url", // 可选:自定义 API 基础 URL, default: https://customsearch.googleapis.com - Num: 5, // 可选:每页结果数量 - Lang: "zh-CN", // 可选:搜索界面语言 - ToolName: "google_search", // 可选:工具名称 - ToolDesc: "google search tool", // 可选:工具描述 -}) -``` - -注:Google Search 的 APIKey、SearchEngineID,请到 Google 搜索引擎官网申请:[https://programmablesearchengine.google.com/controlpanel/all](https://programmablesearchengine.google.com/controlpanel/all) - -### **搜索参数** - -搜索请求支持以下参数: - -```go -type SearchRequest struct { - Query string `json:"query"` // 搜索关键词 - Num int `json:"num"` // 返回结果数量 - Offset int `json:"offset"` // 结果起始位置 - Lang string `json:"lang"` // 搜索语言 -} -``` - -### **完整使用示例** - -```go -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - - "github.com/cloudwego/eino-ext/components/tool/googlesearch" -) - -func main() { - ctx := context.Background() - - googleAPIKey := os.Getenv("GOOGLE_API_KEY") - googleSearchEngineID := os.Getenv("GOOGLE_SEARCH_ENGINE_ID") - - if googleAPIKey == "" || googleSearchEngineID == "" { - log.Fatal("[GOOGLE_API_KEY] and [GOOGLE_SEARCH_ENGINE_ID] must set") - } - - // create tool - searchTool, err := googlesearch.NewTool(ctx, &googlesearch.Config{ - APIKey: googleAPIKey, - SearchEngineID: googleSearchEngineID, - Lang: "zh-CN", - Num: 5, - }) - if err != nil { - log.Fatal(err) - } - - // prepare params - req := googlesearch.SearchRequest{ - Query: "Golang concurrent programming", - Num: 3, - Lang: "en", - } - - args, err := json.Marshal(req) - if err != nil { - log.Fatal(err) - } - - // do search - resp, err := searchTool.InvokableRun(ctx, string(args)) - if err != nil { - log.Fatal(err) - } - - var searchResp googlesearch.SearchResult - if err := json.Unmarshal([]byte(resp), &searchResp); err != nil { - log.Fatal(err) - } - - // Print results - fmt.Println("Search Results:") - fmt.Println("==============") - for i, result := range searchResp.Items { - fmt.Printf("\n%d. Title: %s\n", i+1, result.Title) - fmt.Printf(" Link: %s\n", result.Link) - fmt.Printf(" Desc: %s\n", result.Desc) - } - fmt.Println("") - fmt.Println("==============") - - // seems like: - // Search Results: - // ============== - // 1. Title: My Concurrent Programming book is finally PUBLISHED!!! : r/golang - // Link: https://www.reddit.com/r/golang/comments/18b86aa/my_concurrent_programming_book_is_finally/ - // Desc: Posted by u/channelselectcase - 398 votes and 46 comments - // 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 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… - - // ============== -} -``` - -### **搜索结果示例** - -```json -{ - "query": "Golang concurrent programming", - "items": [ - { - "link": "https://example.com/article1", - "title": "Go 并发编程实践", - "snippet": "这是一篇关于 Go 语言并发编程的文章...", - "desc": "详细介绍了 Go 语言中的 goroutine 和 channel..." - }, - { - "link": "https://example.com/article2", - "title": "Understanding Concurrency in Go", - "snippet": "A comprehensive guide to concurrent programming...", - "desc": "Learn about Go's concurrency model and best practices..." - } - ] -} -``` - -## **相关文档** - -- [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide) -- [Google Custom Search API 文档](https://developers.google.com/custom-search/v1/overview) -- [Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search) diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md deleted file mode 100644 index 390c8b22a4b..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_httprequest.md +++ /dev/null @@ -1,371 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - HTTPRequest -weight: 0 ---- - -## **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` 安装该包(请根据你的项目结构调整模块路径): - -```bash -go get github.com/cloudwego/eino-ext/components/tool/httprequest -``` - -## **快速开始** - -下面分别展示了如何单独使用 GET 和 POST 工具的示例。 - -### **GET 请求示例** - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "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) -} -``` - -### **POST 请求示例** - -```go -package main - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/bytedance/sonic" - post "github.com/cloudwego/eino-ext/components/tool/httprequest/post" -) - -func main() { - config := &post.Config{} - - ctx := context.Background() - - 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}`, - } - - jsonReq, 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) - } - - 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 -} -``` - -对于 GET 工具,请求结构如下: - -```go -type GetRequest struct { - URL string `json:"url" jsonschema_description:"要执行 GET 请求的 URL"` -} -``` - -对于 POST 工具,请求结构如下: - -```go -type PostRequest struct { - URL string `json:"url" jsonschema_description:"要执行 POST 请求的 URL"` - Body string `json:"body" jsonschema_description:"POST 请求要发送的请求体"` -} -``` - -## **与 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" -) - -// float32Ptr is a helper to return a pointer for a float32 value. -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 := ` -openapi: "3.0.0" -info: - title: JSONPlaceholder API - version: "1.0.0" -servers: - - url: https://jsonplaceholder.typicode.com -paths: - /posts: - get: - summary: Get posts - parameters: - - name: _limit - in: query - required: false - schema: - type: integer - example: 2 - description: Limit the number of results - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - userId: - type: integer - id: - type: integer - title: - type: string - body: - type: string - /comments: - get: - summary: Get comments - parameters: - - name: _limit - in: query - required: false - schema: - type: integer - example: 2 - description: Limit the number of results - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - postId: - type: integer - id: - type: integer - name: - type: string - email: - type: string - body: - 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) - } -} -``` - -## **更多详情** - -- [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 deleted file mode 100644 index 0c1b43be39e..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_mcp.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -Description: "" -date: "2025-11-20" -lastmod: "" -tags: [] -title: Eino Tool - MCP -weight: 0 ---- - -## 基本介绍 - -[Model Context Protocol(MCP)](https://modelcontextprotocol.io/introduction)是 Anthropic 推出的供模型访问的资源的标准化开放协议,Eino 提供了相关封装,使用这些封装可以直接访问已有 MCP Server 上的资源。 - -本节介绍 [MCPTool](https://modelcontextprotocol.io/docs/concepts/tools) 的封装,封装实现了 Eino InvokableTool 接口([Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide))。 - - - -其他封装参见: - -[ChatTemplate - MCP](/zh/docs/eino/ecosystem_integration/chat_template/chat_template_mcp) - -## 使用方式 - -### QuickStart - -首先创建 mcp client,Eino 利用了开源 sdk [mark3labs/mcp-go](https://github.com/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 -// while stdio does not require it. -err = cli.Start(ctx) -``` - -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 初始化: - -```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) -``` - -之后使用 Client 创建 Eino Tool: - -```go -import "github.com/cloudwego/eino-ext/components/tool/mcp" - -tools, err := mcp.GetTools(ctx, &mcp.Config{Cli: cli}) -``` - -tool 可以直接调用: - -``` -for i, mcpTool := range mcpTools { - fmt.Println(i, ":") - info, err := mcpTool.Info(ctx) - if err != nil { - log.Fatal(err) - } - fmt.Println("Name:", info.Name) - fmt.Println("Desc:", info.Desc) - fmt.Println() -} -``` - -也可以在任意 Eino Agent 中使用,以 [ReAct Agent](/zh/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" -) - -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}, -}) -``` - -### 按 Name 指定 Tool - -GetTools 支持使用 Name 筛选 Server 提供的 Tools,避免调用预期外的 Tools: - -```go -import "github.com/cloudwego/eino-ext/components/tool/mcp" - -tools, err := mcp.GetTools(ctx, &mcp.Config{ - Cli: cli, - ToolNameList: []string{"name"}, -}) -``` - -## 更多信息 - -实践例子可以参考:[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) diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md deleted file mode 100644 index cf88984ecfb..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_sequentialthinking.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -title: Tool - SequentialThinking -weight: 0 ---- - -## **Sequential Thinking Tool** - -**Sequential Thinking Tool** 是一个用于支持动态和反思性问题解决的工具,通过结构化的思考流程帮助用户逐步分析并解决问题。灵感来自于 [@modelcontextprotocol /sequentialthinking](https://github.com/modelcontextprotocol/servers/tree/HEAD/src/sequentialthinking) ,它通过一系列问题引导大语言模型逐步思考问题。 - -## **功能特性** - -- 指导性的逐步思考流程。 -- 动态提问与自我反思。 -- 提升问题解决能力。 - -## **使用场景** - -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" -) - -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) -} -``` diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md deleted file mode 100644 index f4e5c4c93b4..00000000000 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_wikipedia.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -Description: "" -date: "2026-01-20" -lastmod: "" -tags: [] -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" -) - -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 -} -``` - -## **配置** - -工具可以通过 `Config` 结构体进行配置: - -```go -type Config struct { - // BaseURL 是 Wikipedia API 的基础 URL。 - // 格式: https://.wikipedia.org/w/api.php - // URL 语言依赖于您为 Language 字段设置的语言。 - // 可选。默认值: "https://en.wikipedia.org/w/api.php"。 - BaseURL string - - // UserAgent 是 HTTP 客户端使用的用户代理。 - // 可选,但强烈建议覆盖默认值并使用您项目的相关信息。 - // 建议遵循 Wikipedia 的机器人规范: - // https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy - // 可选。默认值: "eino (https://github.com/cloudwego/eino)" - UserAgent string `json:"user_agent"` - - // DocMaxChars 是返回页面内容的最大字符数。 - // 如果内容超过此值,将会被截断。 - // 可选。默认值: 15s。 - DocMaxChars int `json:"doc_max_chars"` - - // Timeout 是 HTTP 客户端返回响应的最大等待时间。 - // 可选。默认值: 15s。 - Timeout time.Duration `json:"timeout"` - - // TopK 是返回的搜索结果数量。 - // 可选。默认值: 3。 - TopK int `json:"top_k"` - - // MaxRedirect 是最大允许的重定向次数。 - // 可选。默认值: 3。 - MaxRedirect int `json:"max_redirect"` - - // 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** - -### **请求 Schema** - -```go -type SearchRequest struct { - // Query 是搜索的查询字符串。 - Query string `json:"query" jsonschema_description:"The query to search the web for"` -} -``` - -### **响应 Schema** - -```go -type SearchResponse struct { - // Results 是搜索结果的列表。 - Results []*Result `json:"results" jsonschema_description:"The results of the search"` -} -type SearchResult struct { - // Title 是搜索结果的标题。 - Title string `json:"title" jsonschema_description:"The title of the search result"` - // URL 是搜索结果的 URL。 - URL string `json:"url" jsonschema_description:"The url of the search result"` - // Extract 是搜索结果的摘要。 - Extract string `json:"extract" jsonschema_description:"The extract of the search result"` - // Snippet 是搜索结果的片段。 - Snippet string `json:"snippet" jsonschema_description:"The snippet of the search result"` -} -``` - -## **更多详情** - -- [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 b47b565b1eb..5ba6598ae26 100644 --- a/content/zh/docs/eino/overview/_index.md +++ b/content/zh/docs/eino/overview/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2026-01-30" +date: "2026-03-02" lastmod: "" tags: [] title: 'Eino: 概述' @@ -347,7 +347,7 @@ Eino 框架由几个部分组成: > 💡 > 针对字节内部使用的组件,有对应的内部代码仓库: > -> EinoBytedExt: [https://code.byted.org/search/flow/eino-byted-ext](https://code.byted.org/search/flow/eino-byted-ext) +> EinoBytedExt: [https://code.byted.org/flow/eino-byted-ext](https://code.byted.org/flow/eino-byted-ext) > > 包含当前定位为内部使用的组件实现,如 llmgateway, bytedgpt, fornax tracing, bytees 等。 diff --git a/content/zh/docs/eino/overview/eino_adk0_1.md b/content/zh/docs/eino/overview/eino_adk0_1.md index b9b31f98c13..09712a66537 100644 --- a/content/zh/docs/eino/overview/eino_adk0_1.md +++ b/content/zh/docs/eino/overview/eino_adk0_1.md @@ -4,7 +4,7 @@ date: "2026-01-20" lastmod: "" tags: [] title: Eino ADK:一文搞定 AI Agent 核心设计模式,从 0 到 1 搭建智能体系统 -weight: 3 +weight: 6 --- # 前言 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 4bdf3805c2f..7da9a7ea643 100644 --- a/content/zh/docs/eino/overview/eino_adk_excel_agent.md +++ b/content/zh/docs/eino/overview/eino_adk_excel_agent.md @@ -4,7 +4,7 @@ date: "2025-12-02" lastmod: "" tags: [] title: 用 Eino ADK 构建你的第一个 AI 智能体:从 Excel Agent 实战开始 -weight: 4 +weight: 7 --- ## 从 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 c9e17146988..62d6c258bd6 100644 --- a/content/zh/docs/eino/overview/eino_open_source.md +++ b/content/zh/docs/eino/overview/eino_open_source.md @@ -4,7 +4,7 @@ date: "2025-12-01" lastmod: "" tags: [] title: 大语言模型应用开发框架 —— Eino 正式开源! -weight: 1 +weight: 3 --- 今天,经过字节跳动内部半年多的使用和迭代,基于 Golang 的大模型应用综合开发框架 —— Eino,已在 CloudWeGo 正式开源啦! diff --git a/content/zh/docs/eino/overview/graph_or_agent.md b/content/zh/docs/eino/overview/graph_or_agent.md index 96912d6f8bc..7eebda5d8e8 100644 --- a/content/zh/docs/eino/overview/graph_or_agent.md +++ b/content/zh/docs/eino/overview/graph_or_agent.md @@ -1,10 +1,10 @@ --- Description: "" -date: "2026-01-20" +date: "2026-03-02" lastmod: "" tags: [] title: Agent 还是 Graph?AI 应用路线辨析 -weight: 5 +weight: 8 --- ## 引言:两种并存的 AI 交互范式 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 64da61872df..df7bafd381b 100644 --- a/content/zh/docs/eino/quick_start/simple_llm_application.md +++ b/content/zh/docs/eino/quick_start/simple_llm_application.md @@ -120,7 +120,7 @@ chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ }) ``` -> OpenAI ChatModel 的详细信息可以参考:[ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) +> OpenAI ChatModel 的详细信息可以参考:[ChatModel - OpenAI](https://bytedance.larkoffice.com/wiki/NguEw85n6iJjShkVtdQcHpydnld) #### **Ollama(和上方 openai 2 选 1)** @@ -140,7 +140,7 @@ chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{ }) ``` -> OpenAI 相关信息,可以参考:[ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) +> OpenAI 相关信息,可以参考:[ChatModel - Ollama](https://bytedance.larkoffice.com/wiki/WWngw1XMViwgyYkNuZgcjZnxnke) Eino 为大模型提供了统一的 ChatModel 抽象,并提供了开箱即用的各类 LLM 实现,因此业务代码可以无需关注模型实现细节从而专注业务逻辑编写,模型实现迭代时不会影响核心业务逻辑,这意味着开发者可以轻松地在不同的模型之间切换,而无需修改大量代码。 diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md new file mode 100644 index 00000000000..d3e9fcaf103 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.4._-compose_optimization.md @@ -0,0 +1,47 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.4.*-compose optimization' +weight: 4 +--- + +## 版本概述 + +v0.4.0 版本主要对 Graph 编排能力进行了优化,移除了 `GetState` 方法,并为 AllPredecessor 触发模式启用了默认的 eager execution 模式。 + +--- + +## v0.4.0 + +**发布日期**: 2025-07-25 + +### Breaking Changes + +- **移除 GetState 方法**:Graph 不再支持 `GetState` 方法,状态管理需要通过其他机制实现 + +### 新特性 + +- **AllPredecessor 模式默认启用 Eager Execution**:使用 AllPredecessor 触发模式的 Graph 默认采用 eager execution 策略,提升执行效率 + +--- + +## v0.4.1 - v0.4.8 主要更新 + +### 功能增强 + +- 支持使用 JSONSchema 描述工具参数 (#402) +- `ToJSONSchema()` 兼容 OpenAPIV3 到 JSONSchema 的转换 (#418) +- React Agent 新增 `WithTools` 便捷函数 (#435) +- 支持打印推理内容 (reasoning content) (#436) +- 新增 `PromptTokenDetails` 定义 (#377) + +### 问题修复 + +- 修复子图从父图保存状态不正确的问题 (#389) +- 修复分支输入类型为 interface 且值为 nil 的处理 (#403) +- 修复 flow_react 中 `toolCallChecker` 使用错误上下文的问题 (#373) +- 修复 edge handlers 在 successor ready 时才解析的问题 (#438) +- 修复 end node 被跳过时的错误上报 (#411) +- 优化流式包装器错误处理 (#409) diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md new file mode 100644 index 00000000000..b3b96c5fa41 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.5._-ADK_implementation.md @@ -0,0 +1,72 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.5.*-ADK implementation' +weight: 5 +--- + +## 版本概述 + +v0.5.0 是一个重要的里程碑版本,引入了 **ADK (Agent Development Kit)** 框架。ADK 提供了一套完整的智能体开发工具集,支持 Agent 编排、预置 Agent、会话管理、中断恢复等核心能力。 + +--- + +## v0.5.0 + +**发布日期**: 2025-09-10 + +### 重大新特性 + +#### ADK 框架 (#262) + +ADK 是一套面向智能体开发的完整解决方案,主要包括: + +- **ChatModelAgent**:基于大语言模型的基础智能体实现 + - 支持 MaxIterations 配置 + - 支持工具调用 + - 流式输出支持 +- **Agent as Tool**:支持将 Agent 封装为工具供其他 Agent 调用 +- **预置多智能体模式**: + - **Supervisor**:监督者模式,由一个主 Agent 协调多个子 Agent + - **Sequential**:顺序执行模式,Agent 按序执行并继承上下文 + - **Plan-Execute-Replan**:计划-执行-重规划模式 +- **会话管理**: + - Session 事件存储与管理 + - Session Values 支持 + - History Rewriter 历史重写能力 +- **中断与恢复**: + - 支持 Agent 执行中断 + - 支持从检查点恢复执行 + - Deterministic Transfer 中断恢复支持 +- **Agent 运行选项**: + - `WithSessionValues` 支持传入会话级别变量 + - Agent CallOption 扩展 + +--- + +## v0.5.1 - v0.5.15 主要更新 + +### 功能增强 + +- **DeepAgent 预置实现** (#540):支持深度 Agent 模式 +- **Agent 中间件支持** (#533):允许通过中间件扩展 Agent 行为 +- **全局回调支持** (#512):内置 Agent 支持全局 Callbacks +- **MessageRewriter 配置** (#496):React Agent 支持消息重写 +- **BreakLoopAction 定义** (#492):支持循环 Agent 中断 +- **取消中断支持** (#425):支持取消正在进行的中断操作 +- **多模态支持**: + - Message 新增多模态输出内容 (#459) + - 默认提示模板支持多模态 (#470) + - Format 函数支持 UserInputMultiContent (#516) + +### 问题修复 + +- 修复 ChatModelAgent 的 max step 计算 (#549) +- 修复 Session 仅存储有输出的事件 (#503) +- 修复 Sequential Agent 报错时退出问题 (#484) +- 修复 Workflow 仅在最后一个事件执行 action (#463) +- 修复 ChatModelAgent return directly tool panic (#464) +- 修复 Go 1.25 编译错误 (#457) +- 修复空 slice 和空字段序列化问题 (#473) diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md new file mode 100644 index 00000000000..1ac5d1eedf4 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.6._-jsonschema_optimization.md @@ -0,0 +1,41 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.6.*-jsonschema optimization' +weight: 6 +--- + +## 版本概述 + +v0.6.0 版本专注于依赖优化,移除了 kin-openapi 依赖和 OpenAPI3.0 相关定义,简化了 JSONSchema 模块的实现。 + +--- + +## v0.6.0 + +**发布日期**: 2025-11-14 + +### Breaking Changes + +- **移除 kin-openapi 依赖** (#544): + - 删除了对 `kin-openapi` 库的依赖 + - 移除了 OpenAPI 3.0 相关的类型定义 + - 简化了 JSONSchema 模块的实现 + +### 迁移指南 + +如果你的代码中使用了 OpenAPI 3.0 相关的类型定义,需要: + +1. 检查是否有直接使用 `kin-openapi` 相关类型的代码 +2. 将 OpenAPI 3.0 类型替换为标准 JSONSchema 类型 +3. 使用 `schema.ToJSONSchema()` 方法获取工具参数的 JSONSchema 定义 + +--- + +## v0.6.1 主要更新 + +### 问题修复 + +- 常规 bug 修复和稳定性改进 diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md new file mode 100644 index 00000000000..2047d9c8062 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.7._-interrupt_resume_refactor.md @@ -0,0 +1,139 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.7.*-interrupt resume refactor' +weight: 7 +--- + +## 版本概述 + +v0.7.0 是 Eino 框架的一个**重要里程碑版本**,核心亮点是对 **Human in the Loop (HITL) / Interrupt-Resume 能力进行了架构级重构**,提供了更强大、更灵活的中断恢复机制。同时引入了 Skill 中间件、ChatModel 重试机制、多模态工具支持等核心能力。 + +--- + +## v0.7.0 + +**发布日期**: 2025-11-20 + +### 🔥 核心变更:Interrupt-Resume 架构重构 (#563) + +v0.7.0 对中断恢复机制进行了架构级重构,代码变更量巨大(+7527/-1692 行),涉及 28 个文件的重大改动。 + +#### 新增核心模块 + +- **compose/resume.go**:提供类型安全的恢复状态获取 API + - `GetInterruptState[T]`:获取上次中断的持久化状态 + - `GetResumeContext`:检查当前组件是否为恢复目标 +- **internal/core/interrupt.go**:中断信号核心定义 + - `InterruptSignal`:支持嵌套的中断信号结构 + - `InterruptState`:支持状态和层级特定负载 + - `InterruptConfig`:中断配置参数 +- **internal/core/address.go**:组件地址系统 +- **internal/core/resume.go**:恢复逻辑核心实现 + +#### 重构的核心模块 + +- **adk/interrupt.go**:ADK 层中断处理重构 (+238 行) +- **compose/interrupt.go**:编排层中断处理重构 (+256 行) +- **adk/workflow.go**:工作流 Agent 支持中断恢复 (+510 行重构) +- **compose/graph_run.go**:Graph 执行时中断恢复支持 +- **compose/tool_node.go**:工具节点中断支持 + +#### 新增恢复策略 + +支持两种恢复策略: + +1. **隐式 "Resume All"**:单一"继续"按钮恢复所有中断点 +2. **显式 "Targeted Resume"**:独立恢复特定中断点(推荐) + +#### Agent Tool 中断改进 + +- 使用 `GetInterruptState` 替代手动状态管理 +- 支持 `CompositeInterrupt` 组合中断 +- Agent Tool 正确传递内部中断信号 + +### 其他改进 + +- **ADK 序列化增强** (#557):修复 checkpoint 中 gob 序列化缺失类型注册 +- **DeepAgent 优化** (#558):无子 Agent 时自动移除 task tool +- **ChatModelAgent 改进** (#552):无工具配置时正确应用 compose option +- **Plan-Execute 增强** (#555):无工具调用时正确报错 +- **MultiAgent 修复** (#548):修复默认 summary prompt 无法使用的问题 + +--- + +## v0.7.1 - v0.7.36 主要更新 + +### Interrupt-Resume 持续增强 + +基于 v0.7.0 的架构重构,后续版本持续完善中断恢复能力: + +#### 工具中断 API (#691) + +- **新增工具中断 API**:支持在工具执行时触发中断 +- **扩展 isResumeTarget**:支持后代目标识别 + +#### 嵌套 Agent 中断恢复 (#647, #672) + +- **嵌套预置/工作流 Agent**:支持任意层级的 Agent 包装器中断恢复 +- **Wrapped FlowAgents**:正确处理 deterministic transfer 跳过 + +#### Checkpoint 增强 + +- **节点输入持久化** (#634):checkpoint 中持久化 rerun 节点输入 +- **Graph 恢复改进** (#695):恢复时 OnStart 中正确启用 ProcessState +- **序列化修复** (#608, #606):修复数组/切片反序列化 panic + +#### 工具错误处理 (#583) + +- 工具错误处理器不再包装 interrupt error + +### 重大新特性 + +#### Skill 中间件 (#661) + +- 将可复用能力封装为 Skill +- 中间件方式扩展 Agent 能力 +- 优化 Skill 提示词 (#724) + +#### ChatModel 重试机制 (#635) + +- ChatModelAgent 支持调用失败自动重试 +- 可配置 ModelRetryConfig (#648) +- 新增 WillRetryError 支持错误链检查 (#707) + +#### 多模态工具支持 (#760) + +- compose 模块增强工具接口 +- 支持多模态输入输出 + +#### 嵌套 Graph 状态访问 (#584) + +- 嵌套 Graph 可访问父 Graph 状态 + +### 功能增强 + +- **Execute Backend & Tool** (#682) +- **OutputKey 配置** (#711):存储最终答案到 SessionValues +- **嵌套 Runner 共享会话** (#645) +- **Agent 事件发送** (#620, #791) +- **ToJSONSchema 确定性输出** (#630) +- **Token 用量详情** (#629) + +### 问题修复 + +- AfterChatModel 返回修改后消息 (#717, #792) +- 循环 Agent BreakLoopAction 中断 (#814) +- 子 Agent 报错中止循环 Agent (#813) +- 流式执行命令无输出错误 (#790) +- DeepAgent 自动指令渲染 (#726) +- Graph 重复跳过上报 (#694) +- Graph unique successors (#693) + +### 文档与工程 + +- 重写 README 聚焦 ADK (#748, #686, #719) +- 启用 golangci-lint (#602) +- 新增代码风格指南 (#673) diff --git "a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_\344\270\215\345\205\274\345\256\271\346\233\264\346\226\260.md" "b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_\344\270\215\345\205\274\345\256\271\346\233\264\346\226\260.md" new file mode 100644 index 00000000000..466a327666d --- /dev/null +++ "b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_\344\270\215\345\205\274\345\256\271\346\233\264\346\226\260.md" @@ -0,0 +1,238 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: Eino v0.8 不兼容更新 +weight: 1 +--- + +> 本文档记录了 `v0.8.0.Beta` 分支相比 `main` 分支的所有不兼容变更。 + +## 1. API 不兼容变更 + +### 1.1 filesystem Shell 接口重命名 + +**位置**: `adk/filesystem/backend.go` **变更描述**: Shell 相关接口被重命名,且不再嵌入 `Backend` 接口。**Before (main)**: + +```go +type ShellBackend interface { + Backend + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShellBackend interface { + Backend + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +**After (v0.8.0.Beta)**: + +```go +type Shell interface { + Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error) +} + +type StreamingShell interface { + ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error) +} +``` + +**影响**: + +- `ShellBackend` 重命名为 `Shell` +- `StreamingShellBackend` 重命名为 `StreamingShell` +- 接口不再嵌入 `Backend`,如果你的实现依赖组合接口,需要分别实现**迁移指南**: + +```go +// Before +type MyBackend struct {} +func (b *MyBackend) Execute(...) {...} +// MyBackend 实现 ShellBackend 需要同时实现 Backend 的所有方法 + +// After +type MyShell struct {} +func (s *MyShell) Execute(...) {...} +// MyShell 只需要实现 Shell 接口的方法 +// 如果同时需要 Backend 功能,需要分别实现两个接口 +``` + +--- + +## 2. 行为不兼容变更 + +### 2.1 AgentEvent 发送机制变更 + +**位置**: `adk/chatmodel.go` **变更描述**: `ChatModelAgent` 的 `AgentEvent` 发送机制从 eino callback 机制改为 Middleware 机制。**Before (main)**: + +- `AgentEvent` 通过 eino 的 callback 机制发送 +- 如果用户自定义了 ChatModel 或 Tool 的 Decorator/Wrapper,且原始 ChatModel/Tool 内部埋入了 Callback 点位,则 `AgentEvent` 会在 Decorator/Wrapper 的**内部**发送 +- 这对 eino-ext 实现的所有 ChatModel 适用,但对大部分用户自行实现的 Tool 以及 eino 一方提供的 Tool 可能不适用 **After (v0.8.0.Beta)**: +- `AgentEvent` 通过 Middleware 机制发送 +- `AgentEvent` 会在用户自定义的 Decorator/Wrapper 的**外部**发送**影响**: +- 正常情况下用户不感知此变更 +- 如果用户之前自行实现了 ChatModel 或 Tool 的 Decorator/Wrapper,事件发送的相对位置会发生变化 +- 位置变化可能导致 `AgentEvent` 的内容也发生变化:之前的事件不包含 Decorator/Wrapper 做出的变更,现在的事件会包含**变更原因**: +- 正常业务场景下,希望发出的事件包含 Decorator/Wrapper 做出的变更**迁移指南**:如果你之前通过 Decorator/Wrapper 包装了 ChatModel 或 Tool,需要改为实现 `ChatModelAgentMiddleware` 接口: + +```go +// Before: 通过 Decorator/Wrapper 包装 ChatModel +type MyModelWrapper struct { + inner model.BaseChatModel +} + +func (w *MyModelWrapper) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) { + // 自定义逻辑 + return w.inner.Generate(ctx, input, opts...) +} + +// After: 实现 ChatModelAgentMiddleware 的 WrapModel 方法 +type MyMiddleware struct{} + +func (m *MyMiddleware) WrapModel(ctx context.Context, chatModel model.BaseChatModel, mc *ModelContext) (model.BaseChatModel, error) { + return &myWrappedModel{inner: chatModel}, nil +} + +// 对于 Tool 的 Wrapper,改为实现 WrapInvokableToolCall / WrapStreamableToolCall 等方法 +``` + +### 2.2 filesystem.ReadRequest.Offset 语义变更 + +**位置**: `adk/filesystem/backend.go` **变更描述**: `Offset` 字段从 0-based 改为 1-based。**Before (main)**: + +```go +type ReadRequest struct { + FilePath string + // Offset is the 0-based line number to start reading from. + Offset int + Limit int +} +``` + +**After (v0.8.0.Beta)**: + +```go +type ReadRequest struct { + FilePath string + // Offset specifies the starting line number (1-based) for reading. + // Line 1 is the first line of the file. + // Values < 1 will be treated as 1. + Offset int + Limit int +} +``` + +**迁移指南**: + +```go +// Before: 读取从第 0 行开始(即第一行) +req := &ReadRequest{Offset: 0, Limit: 100} + +// After: 读取从第 1 行开始(即第一行) +req := &ReadRequest{Offset: 1, Limit: 100} + +// 如果原来使用 Offset: 10 表示从第 11 行开始 +// 现在需要使用 Offset: 11 +``` + +--- + +### 2.3 filesystem.FileInfo.Path 语义变更 + +**位置**: `adk/filesystem/backend.go` **变更描述**: `FileInfo.Path` 字段不再保证是绝对路径。**Before (main)**: + +```go +type FileInfo struct { + // Path is the absolute path of the file or directory. + Path string +} +``` + +**After (v0.8.0.Beta)**: + +```go +type FileInfo struct { + // Path is the path of the file or directory, which can be a filename, + // relative path, or absolute path. + Path string + // ... +} +``` + +**影响**: + +- 依赖 `Path` 为绝对路径的代码可能会出现问题 +- 需要检查并处理相对路径的情况 + +--- + +### 2.4 filesystem.WriteRequest 行为变更 + +**位置**: `adk/filesystem/backend.go` **变更描述**: `WriteRequest` 的写入行为从"文件存在则报错"变更为"文件存在则覆盖"。**Before (main)**: + +```go +// WriteRequest 注释说明: +// The file will be created if it does not exist, or error if file exists. +``` + +**After (v0.8.0.Beta)**: + +```go +// WriteRequest 注释说明: +// Creates the file if it does not exist, overwrites if it exists. +``` + +**影响**: + +- 原来依赖"文件存在报错"行为的代码将不再报错,而是直接覆盖 +- 可能导致意外的数据丢失**迁移指南**: +- 如果需要保留原有行为,在写入前先检查文件是否存在 + +--- + +### 2.5 GrepRequest.Pattern 语义变更 + +**位置**: `adk/filesystem/backend.go` **变更描述**: `GrepRequest.Pattern` 从字面量匹配变更为正则表达式匹配。**Before (main)**: + +```go +// Pattern is the literal string to search for. This is not a regular expression. +// The search performs an exact substring match within the file's content. +``` + +**After (v0.8.0.Beta)**: + +```go +// Pattern is the search pattern, supports full regular expression syntax. +// Uses ripgrep syntax (not grep). +``` + +**影响**: + +- 包含正则表达式特殊字符的搜索模式行为将发生变化 +- 例如,搜索 `interface{}` 现在需要转义为 `interface\{\}` **迁移指南**: + +```go +// Before: 字面量搜索 +req := &GrepRequest{Pattern: "interface{}"} + +// After: 正则表达式搜索,需要转义特殊字符 +req := &GrepRequest{Pattern: "interface\\{\\}"} + +// 或者如果要搜索包含 . * + ? 等的字面量,也需要转义 +// Before +req := &GrepRequest{Pattern: "config.json"} +// After +req := &GrepRequest{Pattern: "config\\.json"} +``` + +--- + +## 迁移建议 + +1. **优先处理编译错误**: 类型变更(如 Shell 接口重命名)会导致编译失败,需要首先修复 +2. **关注语义变更**: `ReadRequest.Offset` 从 0-based 改为 1-based,`Pattern` 从字面量改为正则表达式,这些不会导致编译错误但会改变运行时行为 +3. **检查文件操作**: `WriteRequest` 的覆盖行为变更可能导致数据丢失,需要额外检查 +4. **迁移 Decorator/Wrapper**: 如有自定义的 ChatModel/Tool Decorator/Wrapper,改为实现 `ChatModelAgentMiddleware` +5. 按需升级 backend 实现:如果使用 eino-ext 提供的 local/ark agentkit backend,升级到对应的 alpha 版本:[local backend v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Flocal%2Fv0.2.0-alpha.1), [ark agentkit backend v0.2.0-alpha](https://github.com/cloudwego/eino-ext/releases/tag/adk%2Fbackend%2Fagentkit%2Fv0.2.0-alpha.1) +6. **测试验证**: 迁移后进行全面的测试,特别是涉及文件操作和搜索功能的代码 diff --git a/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md new file mode 100644 index 00000000000..e91a9f5c9f8 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/_index.md @@ -0,0 +1,279 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.8.*-adk middlewares' +weight: 8 +--- + +本文档介绍 Eino ADK v0.8.* 版本的主要新功能和改进。 + +> 💡 +> 目前尚处于 v0.8.0.Beta 版本阶段:[https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1](https://github.com/cloudwego/eino/releases/tag/v0.8.0-beta.1) + +## 版本亮点 + +v0.8 是一个重要的功能增强版本,引入了全新的中间件接口架构,新增多个实用中间件,并提供了增强的可观测性支持。 + + +
    +🔧 灵活的中间件架构 +全新 ChatModelAgentMiddleware 接口 +📊 增强的可观测性 +Agent 级别 Callback 支持
    + +--- + +## 1. ChatModelAgentMiddleware 接口 + +> 💡 +> **核心更新**: 全新的中间件接口,提供更灵活的 Agent 扩展机制 + +`ChatModelAgentMiddleware` 是 v0.8 最重要的架构更新,为 `ChatModelAgent` 及基于它构建的 Agent(如 `DeepAgent`)提供统一的扩展点。 + +**相比 AgentMiddleware 的优势**: + + + + + + +
    特性AgentMiddlewareChatModelAgentMiddleware
    扩展性封闭开放,可实现自定义 handler
    Context 传播回调只返回 error所有方法返回 (ctx, ..., error)
    配置管理分散在闭包中集中在结构体字段中
    + +**接口方法**: + +- `BeforeAgent` - Agent 运行前修改配置 +- `BeforeModelRewriteState` - 模型调用前处理状态 +- `AfterModelRewriteState` - 模型调用后处理状态 +- `WrapInvokableToolCall` - 包装同步工具调用 +- `WrapStreamableToolCall` - 包装流式工具调用 +- `WrapModel` - 包装模型调用 + +**使用方式**: + +```go +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: model, + Handlers: []adk.ChatModelAgentMiddleware{mw1, mw2, mw3}, +}) +``` + +详见 [Eino ADK: ChatModelAgentMiddleware](/zh/docs/eino/core_modules/eino_adk/Eino_ADK_ChatModelAgentMiddleware) + +--- + +### 1.1 Summarization 中间件 + +> 💡 +> **功能**: 自动对话历史摘要,防止超出模型上下文窗口限制 + +📚 **详细文档**: [Middleware: Summarization](https://www.feishu.cn/wiki/KFJ9wnrMmicvI4kWuz4cymnTnfe) + +当对话历史的 Token 数量超过阈值时,自动调用 LLM 生成摘要,压缩上下文。 + +**核心能力**: + +- 可配置的触发条件(Token 阈值) +- 支持保留最近的用户消息 +- 支持记录完整对话历史到文件 +- 提供前后处理钩子 + +**快速开始**: + +```go +mw, err := summarization.New(ctx, &summarization.Config{ + Model: chatModel, + Trigger: &summarization.TriggerCondition{ + ContextTokens: 100000, + }, +}) +``` + +### 1.2 ToolReduction 中间件 + +> 💡 +> **功能**: 工具结果压缩,优化上下文使用效率 + +📚 **详细文档**: [Middleware: ToolReduction](https://www.feishu.cn/wiki/C5QAwAg3QisDo5kwaWicJBo9ndd) + +提供两阶段的工具输出管理: + + + + + +
    阶段触发时机作用
    截断 (Truncation)工具返回后截断超长输出,保存到文件
    清理 (Clear)模型调用前清理历史工具结果,释放 Token
    + +**快速开始**: + +```go +mw, err := reduction.New(ctx, &reduction.Config{ + Backend: fsBackend, + MaxLengthForTrunc: 30000, + MaxTokensForClear: 50000, +}) +``` + +### 1.3 Filesystem 中间件 + +> 💡 +> **功能**: 文件系统操作工具集 + +📚 **详细文档**: [Middleware: FileSystem](https://www.feishu.cn/wiki/NODlwh1Q9imdetkhmgKcyF0Vncd) + +**新增能力**: + +- **Grep 功能增强**: 支持完整正则表达式语法 +- **新增选项**: `CaseInsensitive`、`EnableMultiline`、`FileType` 过滤 +- **自定义工具名**: 所有 filesystem 工具支持自定义名称 + +### 1.4 Skill 中间件 + +> 💡 +> **功能**: 动态加载和执行 Skill + +📚 **详细文档**: [Middleware: Skill](https://www.feishu.cn/wiki/NG1xwde86ig3qhkdMhQc5bh2nIm) + +**新增能力**: + +- **Context 模式**: 支持 `fork` 和 `isolate` 两种上下文模式 +- **自定义配置**: 支持自定义系统提示和工具描述 +- **FrontMatter 扩展**: 支持通过 FrontMatter 指定 agent 和 model + +### 1.5 PlanTask 中间件 + +> 💡 +> **功能**: 任务规划和执行工具 + +📚 **详细文档**: [Middleware: PlanTask](https://www.feishu.cn/wiki/H7dlwwO0ZiotwBk8iTpcxesKn4d) + +支持 Agent 创建和管理任务计划,适用于需要分步执行的复杂任务场景。 + +### 1.6 ToolSearch 中间件 + +> 💡 +> **功能**: 工具搜索,支持从大量工具中动态检索 + +📚 **详细文档**: [Middleware: ToolSearch](https://www.feishu.cn/wiki/GVgXww3HJiZUx0kS7O6cfOHpnZf) + +当工具数量较多时,通过语义搜索动态选择最相关的工具,避免上下文过载。 + +### 1.7 PatchToolCalls 中间件 + +> 💡 +> **功能**: 修补悬空的工具调用,确保消息历史完整性 + +📚 **详细文档**: [Middleware: PatchToolCalls](https://www.feishu.cn/wiki/WpkUwXnwMidkUVkGTx8ckIWWnjg) + +扫描消息历史,为缺少响应的工具调用插入占位符消息。适用于工具调用被中断或取消的场景。 + +**快速开始**: + +```go +mw, err := patchtoolcalls.New(ctx, nil) +``` + +## 2. Agent Callback 支持 + +> 💡 +> **功能**: Agent 级别的回调机制,用于观测和追踪 + +支持在 Agent 执行的全生命周期中注册回调,实现日志记录、追踪、监控等功能。 + +**核心类型**: + +- `AgentCallbackInput` - 回调输入,包含 Agent 输入或恢复信息 +- `AgentCallbackOutput` - 回调输出,包含 Agent 事件流 + +**使用方式**: + +```go +agent.Run(ctx, input, adk.WithCallbacks( + callbacks.NewHandler( + callbacks.WithOnStart(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + agentInput := adk.ConvAgentCallbackInput(input) + // 处理 Agent 启动事件 + return ctx + }), + callbacks.WithOnEnd(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { + agentOutput := adk.ConvAgentCallbackOutput(output) + // 处理 Agent 完成事件 + return ctx + }), + ), +)) +``` + +详见 [Eino ADK: Agent Callback](/zh/docs/eino/core_modules/eino_adk/adk_agent_callback) + +--- + +## 3. Language Setting + +> 💡 +> **功能**: 全局语言设置 + +支持全局设置 ADK 的语言偏好,影响内置提示词和消息的语言。 + +**使用方式**: + +```go +adk.SetLanguage(adk.LanguageChinese) // 设置为中文 +adk.SetLanguage(adk.LanguageEnglish) // 设置为英文(默认) +``` + +--- + +## 中间件使用建议 + +> 💡 +> **推荐组合**: 以下中间件可组合使用,覆盖大部分长对话场景 + +```go +handlers := []adk.ChatModelAgentMiddleware{ + patchMW, // 1. 修补悬空工具调用 + reductionMW, // 2. 压缩工具输出 + summarizationMW, // 3. 摘要对话历史 +} + +agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ + Model: model, + Handlers: handlers, +}) +``` + +--- + +## Breaking Changes + +> 💡 +> 升级到 v0.8 前,请查阅 Breaking Changes 文档了解所有不兼容变更 + +📚 **完整文档**: [Eino v0.8 不兼容更新](/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_不兼容更新) + +**变更概览**: + + + + + + + + + +
    类型变更项
    API 变更
    ShellBackend
    Shell
    接口重命名
    行为变更
    AgentEvent
    发送机制改为 Middleware
    行为变更
    ReadRequest.Offset
    从 0-based 改为 1-based
    行为变更
    FileInfo.Path
    不再保证为绝对路径
    行为变更
    WriteRequest
    文件存在时从报错改为覆盖
    行为变更
    GrepRequest.Pattern
    从字面量改为正则表达式
    + +## 升级指南 + +详细的迁移步骤和代码示例请参考:[Eino v0.8 不兼容更新](/zh/docs/eino/release_notes_and_migration/Eino_v0.8._-adk_middlewares/Eino_v0.8_不兼容更新) + +**快速检查清单**: + +1. 检查是否使用了 `ShellBackend` / `StreamingShellBackend` 接口(需重命名) +2. 检查 `ReadRequest.Offset` 使用(0-based → 1-based) +3. 检查 `GrepRequest.Pattern` 使用(字面量 → 正则表达式,特殊字符需转义) +4. 检查是否依赖 `WriteRequest` 的"文件存在报错"行为 +5. 检查是否依赖 `FileInfo.Path` 为绝对路径 +6. 如有自定义 ChatModel/Tool Decorator/Wrapper,考虑迁移到 `ChatModelAgentMiddleware` +7. 运行测试验证功能正常 diff --git a/content/zh/docs/eino/release_notes_and_migration/_index.md b/content/zh/docs/eino/release_notes_and_migration/_index.md new file mode 100644 index 00000000000..ecf415421d4 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/_index.md @@ -0,0 +1,65 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: 发布记录 & 迁移指引' +weight: 8 +--- + +# 版本管理规范 + +Go SDK 项目通常遵循 [语义化版本控制](https://semver.org/lang/zh-CN/)(Semver)的规范。Semver 的版本号由三部分组成,格式为: + +> 💡 +> v{MAJOR}.{MINOR}.{PATCH} + +- **MAJOR**:主版本号,表示有重大更新或不兼容的 API 变更。 +- **MINOR**:次版本号,表示新增功能,且与之前的版本保持向后兼容。 +- **PATCH**:修订号,表示向后兼容的 bug 修复。 + +此外,语义化版本控制也支持预发布版本和元数据标签,用于标记**预发版**、**尝鲜版**等非正式版本。其格式为: + +> 💡 +> v{MAJOR}.{MINOR}.{PATCH}-{PRERELEASE}+{BUILD} + +- **PRERELEASE**:预发布版本标识,例如 `alpha`、`beta`、`rc`(Release Candidate)。 +- **BUILD**:构建元数据(可选),通常用于标识特定的构建,例如 CI 构建号等。 + +**Eino 遵循以上的语义化版本版本规范,会有如下几种版本类型:** + + + + + + +
    版本类型版本号格式版本说明备注
    稳定版(Stable Release)格式:
  • v{MAJOR}.{MINOR}.{PATCH}
  • 示例:
  • v0.1.1
  • v1.2.3
  • 发布稳定版本时,确保 API 的稳定性和向后兼容性。
  • 严格遵守语义化版本控制:只有在引入重大不兼容变化时才提升主版本号(MAJOR),新增功能时提升次版本号(MINOR),只修复 bug 时提升修订号(PATCH)。
  • 确保在发布前进行全面的单元测试、集成测试以及性能测试。
  • 在发布时提供详细的发布说明(Release Notes),列出重要的变更、修复、特性以及迁移指南(如有)。
  • 预发版(Pre-release)
  • Alpha
  • Beta
  • RC (Release Candidate)
  • 格式:
  • v{MAJOR}.{MINOR}.{PATCH}-{alpha/beta/rc}.{num}
  • 示例:
  • v0.1.1-beta.1
  • v1.2.3-rc.1
  • v1.2.3-rc.2
  • Alpha:内部测试版,功能不一定完备,可能会有较多的 bug,不建议用于生产环境。
  • Beta:功能基本完备,但仍可能存在 bug,适合公开测试,不建议用于生产环境。
  • RC(Release Candidate):候选发布版,功能完成且基本稳定,建议进行最后的全面测试。在没有严重 bug 的情况下,RC 版本会转化为稳定版。一般来说,RC 版本的最后一个版本会转换成稳定版本
  • 尝鲜版(Canary/Experimental/Dev)格式:
  • v{MAJOR}.{MINOR}.{PATCH}-{dev}.{num}
  • 示例:
  • v0.1.1-dev.1
  • v1.2.3-dev.2
  • 尝鲜版是非常不稳定的版本,通常用于测试新功能或架构的早期实现。这些版本可能会包含实验性功能,甚至会在未来被移除或大幅修改。
  • 尝鲜版一般是在仓库的实验性分支上进行
  • 一般来说,在字节内部用不到此种版本类型,可能在开源社区中使用
    + +# 关于 V0、V1、Vn(n>1)的一些潜在共识 + + + + + +
    标题说明备注
    V0大版本内部的不稳定性
  • v0.x.x 表示该库仍处于不稳定状态,可能在 MINOR 版本的迭代中,引入不兼容更改,API 发生变化,不承诺向后兼容性。
  • 用户在使用这些版本时应该预期到 API 可能会发生变化
  • 版本号的提升不会严格遵守语义化版本控制的规则
  • v0.x.x 的设计目标是快速迭代,允许开发者在 API 不稳定的情况下发布库版本,并收集用户反馈。
  • V1、Vn(n>1)大版本内部的稳定性
  • v1.0.0 表示该库达到了稳定状态,API 设计已经成熟,承诺向后兼容性,即未来的
    v1.x.x
    版本会保证不引入不兼容的更改。
  • 严格遵循语义化版本控制
  • 不兼容的 API 变更 将需要通过主版本号(MAJOR)的提升才能发布。例如,需要将版本号提升到
    v2.0.0
  • 向后兼容的功能更新 将通过次版本号(MINOR)的提升来发布,例如
    v1.1.0
  • 向后兼容的 bug 修复 将通过修订号(PATCH)的提升来发布,例如
    v1.0.1
  • + +> 💡 +> 当前因 Eino 初次发布,虽然 Eino 的 API 已初具稳态,但未经过大规模业务验证,MAJOR 版本暂时定为 V0,经过至少 50+ 业务线验证后,会提升版本至 V1 + +# Release Notes 文档结构 + +- 一个 {MAJOR}.{MINOR} 次版本单独一篇文档 + - 命名格式:“Eino: v1.2.* {标题描述}” +- {MAJOR}.{MINOR} 次版本中记录这个版本下的所有 ChangeLog +- 次版本的子目录中,可选放置每个 PATCH 的详细介绍 + +``` +. +├── v1.0.* +│   └── bug_fix_1_x.txt +├── v0.2.* +├── v0.1.* +    ├── bug_fix_1_xxx.txt +    ├── bug_fix_2_xxxxx.txt +    └── bug_fix_3_xxxxxxx.txt +``` diff --git a/content/zh/docs/eino/release_notes_and_migration/v01_first_release.md b/content/zh/docs/eino/release_notes_and_migration/v01_first_release.md new file mode 100644 index 00000000000..f9fadbde982 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/v01_first_release.md @@ -0,0 +1,96 @@ +--- +Description: "" +date: "2025-01-06" +lastmod: "" +tags: [] +title: 'Eino: v0.1.*-first release' +weight: 1 +--- + +## v0.1.6 + +> 发版时间:2024-11-04 + +### Features + +- NewTool、InferTool 的泛型形参支持 struct +- 废弃 WithGraphRunOption(),新增 WithRuntimeMaxSteps 方法 +- 调整 ToolsNode 的 NodeName,新增 TooCallbackInput/Output 结构体 +- Flow 中新增 MultiQuery、Router 两种 Retriever +- 新增 document.Loader、document.Transformer 两种组件抽象 +- Message MultiPart 中新增 file_url, audio_url, video_url 三种类型 + +### BugFix + +- 存在 InputKey 的节点中,如果输入的 map 中,不存在 InputKey 时,报错处理 +- Message Stream 合并时(ConcatMessage),调整 Name、Type、ID 的组合方式 + +## v0.1.5 + +> 发版时间:2024-10-25 + +### Features + +- ConcatMessages 时,校验 Message 流中,是否存在 nil chunk,如果存在则报错,从而让流的生产方,不能塞入 nil message +- Message.ToolCall 中增加 Type 字段,表达 ToolType,并在 ConcatMessages 增加 ToolType 的增量合并 + +## v0.1.4 + +> 发版时间:2024-10-23 + +### Features + +- 调整 Chain 的实现,基于 Graph[I, O] 封装 Chain +- 当上游输出节点是接口,且下游输入节点是这个接口的实现时,支持尝试把 上游输出接口断言成下游输入的实例。 + - 例如新增支持如下场景: Node1[string, any] -> Node2[string, int], 这种场景下,之前直接在 Compile 时报错,当前会尝试把 any 断言成 string,如果可断言成功,则继续执行。 +- schema.Message 增加 Type 字段 + +### BugFix + +- 修正 Tool 工具执行时的 Eino Callback 的 RunInfo 信息 + - 修正 RunInfo 的 Component 和 Type 字段 + - ToolName 作为 RunInfo.Name +- Passthrough 节点允许设置 OutputKey + +## v0.1.3 + +> 发版时间:2024-10-17 + +### BugFix + +- 修复 ToolsNode 返回 Message 时额外塞入了 ToolCalls 导致模型报错 + - 引入此问题是 v0.1.1 的 react agent 支持 tool return directly 时,扩展了 ToolsNode 返回的信息。本次更换了另一种方式实现 tool return directly,不会再导致此问题。 + +## v0.1.2 + +> 发版时间:2024-10-14 + +### Features + +- StreamReader.Copy() 方法重新调整优化成 Goroutine Free 的 Copy 方式。避免业务忘记 StreamReader.Close() 导致的 Goroutine 泄漏的问题 +- 校验检查:Passthrough 节点,不允许添加 InputKey/OutputKey +- Compile Callback Option 中,支持 InputKey、OutputKey 的回传 + +## v0.1.1 + +> 发版时间:2024-10-14 + +### Features + +- React Agent 支持 Tool ReturnDirectly 的静态配置 + +### BugFix + +- Revert Stream Copy 的新逻辑。 + - 因实现 Goroutine Free 的 Stream Copy,引入了 Recv 可能出现夯死的情况。预计下个 Patch 修复此问题 + +## v0.1.0 + +> 发版时间:2024-10-12 + +### Features + +- 支持 ChatTemplate/ChatModel/Tool/LoaderAndSplitter/Indexer/Retriever/Embedding 多种组件的抽象和实现 +- 支持 Graph/Chain/StateGraph 等多种编排工具 +- 根据输入、输出是否为流式,支持 4 种交互模式,并内置了 Stream 工具 +- 灵活易扩展的切面设计 diff --git a/content/zh/docs/eino/release_notes_and_migration/v02_second_release.md b/content/zh/docs/eino/release_notes_and_migration/v02_second_release.md new file mode 100644 index 00000000000..427669e1eb5 --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/v02_second_release.md @@ -0,0 +1,124 @@ +--- +Description: "" +date: "2026-03-02" +lastmod: "" +tags: [] +title: 'Eino: v0.2.*-second release' +weight: 2 +--- + +## v0.2.6 + +> 发版时间:2024-11-27 + +### Features + +- 增加流式的 Pre、Post StateHandler +- 支持 StateChain +- 新增 MessageParser 节点,将 ChatModel 输出的 Message 转换成业务定制结构体 + - Parse(ctx context.Context, m *Message) (T, error) +- 针对 Chain AppendNode 时,支持 WithNodeKey() + +### BugFix + +- 修复 ConcatMessage 时,由于没有深 Copy 导致的首个 Chunk 被修改的问题。 +- ConcatMessage 时,FinishReason 只保留最后一个 Chunk 的有效值 + +## v0.2.5 + +> 发版时间:2024-11-21 + +### BugFix + +- 修复 Gonja 禁用 include 等关键字导致的 panic 问题 + +## v0.2.4 + +> 发版时间:2024-11-20 + +### Features + +- Eino Message ResponseMeta 中增加 TokenUsage 字段 +- Eino Message ToolsCall 按照 index 进行排序 + +### BugFix + +## v0.2.3 + +> 发版时间:2024-11-12 + +### Features + +- Graph 调用时,支持 context 的 Timeout 和 Cancel + +### BugFix + +- FinishReason 可能在任意一个包中返回,不建设一定再最后一个包返回 +- callbacks.HandlerBuilder 不再提供默认的 Needed() 方法, 此方法默认返回 false,在内嵌 callbacks.HandlerBuilder 场景,会导致所有的切面函数失效 + +## v0.2.2 + +> 发版时间:2024-11-12 + +### Features + +- Message 中增加 FinishReason 字段 +- 增加 GetState[T]() 方法,可在节点中获取 State 结构体 +- Lazy Init Gonjia SDK + +### BugFix + +## v0.2.1 + +> 发版时间:2024-11-07 + +### BugFix + +- Fixed the SSTI vulnerability in the Jinja chat template [langchaingo 存在 gonja 模板注入](https://bytedance.larkoffice.com/docx/UvqxdlFfSoTIr1xtsQ5cIZTVn2b) + +## v0.2.0 + +> 发版时间:2024-11-07 + +### Features + +- Callback API 重构(兼容更新) + + - 面向组件实现者:隐藏并废弃 callbacks.Manager,提供更简单的注入 callback 切面的工具函数。 + - 面向 Handler 实现者:提供 callbacks.Handler 快速实现的模版方法,封装了组件类型判断、input/output 类型断言和转换等细节,用户只需要提供特定组件的特定 callback 方法的具体实现。 + - 运行机制:针对一次运行的某个具体的 callback 切面时机,根据组件类型和 Handler 具体实现的方法,额外筛选出需要执行的具体的 handler。 +- 新增 Host Multi-Agent:实现 Host 模式的 Multi-Agent,即 Host 做意图识别后跳转到各个 Specialist Agent 做具体的生成。 +- React Agent API 变更(不兼容) + + - 去掉 AgentCallback 定义,改为通过 BuildAgentCallback 工具函数,快速注入 ChatModel 和 Tool 的 CallbackHandlers。使用姿势: + + ```go + func BuildAgentCallback(modelHandler *model.CallbackHandler, toolHandler *tool.CallbackHandler) callbacks.Handler { + return template.NewHandlerHelper().ChatModel(modelHandler).Tool(toolHandler).Handler() + } + ``` + + - 从而做到 AgentCallback 与组件的语义对齐,可以返回 ctx,可以使用扩展后的 tool.CallbackInput, tool.CallbackOutput。 + - 去掉 react.Option 定义。React Agent 改为使用 Agent 通用的 agent.Option 定义,方便在 multi-agent 层面组合编排。 + + - 不再需要 WithAgentCallback 来注入特殊的 AgentCallback,新的使用姿势: + + ``` + agent.WithComposeOptions(compose.WithCallbacks(xxxCallbackHandler)) + ``` +- 新增 Document Parser 接口定义:作为 Loader 组件的依赖,负责将 io.Reader 解析为 Document,并提供了根据文件扩展名进行解析的 ExtParser 实现。 + +### BugFix + +- 修复 embedding.GetCommonOptions 和 indexer.GetCommonOptions 未对 apply 做判空可能导致的空指针异常。 +- Graph 运行时,preProcessor 和 postProcessor 使用当前 ctx。 + +## v0.2.0-dev.1 + +> 发版时间:2024-11-05 + +### Features + +- 初步设计并支持 Checkpoint 机制,尝鲜试用 + +### BugFix diff --git a/content/zh/docs/eino/release_notes_and_migration/v03_tiny_break_change.md b/content/zh/docs/eino/release_notes_and_migration/v03_tiny_break_change.md new file mode 100644 index 00000000000..a952551237d --- /dev/null +++ b/content/zh/docs/eino/release_notes_and_migration/v03_tiny_break_change.md @@ -0,0 +1,39 @@ +--- +Description: "" +date: "2025-01-06" +lastmod: "" +tags: [] +title: 'Eino: v0.3.*-tiny break change' +weight: 3 +--- + +## v0.3.1-内场封板 + +> 发版时间:2024-12-17 +> +> 后续将只维护开源版,会提供便捷迁移脚本,帮助大家从内场版本迁移至开源版本 + +### Features + +- 精简 Eino 对外暴露的概念 + - Graph、Chain 支持 State,去除 StateGraph、StateChain + - 去除 GraphKey 的概念 +- 优化流合并时的,MultiReader 的读取性能 + +### BugFix + +- 修复 Chain 中 AddNode 时,遗漏的 Error 检查 + +## v0.3.0 + +> 发版时间:2024-12-09 + +### Features + +- schema.ToolInfo.ParamsOneOf 修改成指针。 支持 ParamsOneOf 为 nil 的场景 +- Compile Callback 中支持返回 GenStateFn +- 支持全局的 Compile Callback + +### BugFix + +- CallOption DesignateNode 时,不应该执行 Graph 切面,只执行指定节点的切面 diff --git a/static/img/eino/A737bctqLoOzNrxbK8Hc5ccmnEb.png b/static/img/eino/A737bctqLoOzNrxbK8Hc5ccmnEb.png new file mode 100644 index 00000000000..70909a76491 Binary files /dev/null and b/static/img/eino/A737bctqLoOzNrxbK8Hc5ccmnEb.png differ diff --git a/static/img/eino/GzIObeN6roy2SAxpEXBcMqrRnYb.png b/static/img/eino/GzIObeN6roy2SAxpEXBcMqrRnYb.png new file mode 100644 index 00000000000..d0994449c34 Binary files /dev/null and b/static/img/eino/GzIObeN6roy2SAxpEXBcMqrRnYb.png differ diff --git a/static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png b/static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png new file mode 100644 index 00000000000..21d758852a4 Binary files /dev/null and b/static/img/eino/HcwAb6W1JofCzhx2JQ8cniHlnpc.png differ diff --git a/static/img/eino/N9ZzwvvuWhya0vbIzLEcMx6DnMP.png b/static/img/eino/N9ZzwvvuWhya0vbIzLEcMx6DnMP.png new file mode 100644 index 00000000000..997eeaf21aa Binary files /dev/null and b/static/img/eino/N9ZzwvvuWhya0vbIzLEcMx6DnMP.png differ diff --git a/static/img/eino/TXVlwT7Iohh1EtbEeC6cIptxnZd.png b/static/img/eino/TXVlwT7Iohh1EtbEeC6cIptxnZd.png new file mode 100644 index 00000000000..0d005ed243f Binary files /dev/null and b/static/img/eino/TXVlwT7Iohh1EtbEeC6cIptxnZd.png differ diff --git a/static/img/eino/callback_apmplus.gif b/static/img/eino/callback_apmplus.gif deleted file mode 100644 index b653a3c9e3e..00000000000 Binary files a/static/img/eino/callback_apmplus.gif and /dev/null differ diff --git a/static/img/eino/eino_adk_deep_agent_definition.png b/static/img/eino/eino_adk_deep_agent_definition.png deleted file mode 100644 index a77a837b8ee..00000000000 Binary files a/static/img/eino/eino_adk_deep_agent_definition.png and /dev/null differ diff --git a/static/img/eino/eino_adk_quick_start_agent_types.png b/static/img/eino/eino_adk_quick_start_agent_types.png index 64915e887e2..de96fda45b2 100644 Binary files a/static/img/eino/eino_adk_quick_start_agent_types.png and b/static/img/eino/eino_adk_quick_start_agent_types.png differ diff --git a/static/img/eino/eino_callback_langfuse_usage.gif b/static/img/eino/eino_callback_langfuse_usage.gif deleted file mode 100644 index ae981d59b2c..00000000000 Binary files a/static/img/eino/eino_callback_langfuse_usage.gif and /dev/null differ diff --git a/static/img/eino/eino_mcp_prompt.png b/static/img/eino/eino_mcp_prompt.png deleted file mode 100644 index f4649d95ed5..00000000000 Binary files a/static/img/eino/eino_mcp_prompt.png and /dev/null differ diff --git a/static/img/eino/eino_mcp_tool_architecture.png b/static/img/eino/eino_mcp_tool_architecture.png deleted file mode 100644 index 871d46a1268..00000000000 Binary files a/static/img/eino/eino_mcp_tool_architecture.png and /dev/null differ