diff --git a/content/zh/docs/eino/_index.md b/content/zh/docs/eino/_index.md index 008d8e7db1..33ffd2fb9b 100644 --- a/content/zh/docs/eino/_index.md +++ b/content/zh/docs/eino/_index.md @@ -1,6 +1,6 @@ --- Description: Eino 是基于 Golang 的 AI 应用开发框架 -date: "2025-01-15" +date: "2025-01-20" lastmod: "" linktitle: Eino menu: diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md index 8c8e2f7e4d..9188158019 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/call_option_capabilities.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: CallOption 能力与规范' @@ -10,7 +10,7 @@ weight: 0 **CallOption**: 对 Graph 编译产物进行调用时,直接传递数据给特定的一组节点(Component、Implementation、Node)的渠道 - 和 节点 Config 的区别: 节点 Config 是实例粒度的配置,也就是从实例创建到实例消除,Config 中的值一旦确定就不需要改变了 - CallOption:是请求粒度的配置,不同的请求,其中的值是不一样的。更像是节点入参,但是这个入参是直接由 Graph 的入口直接传入,而不是上游节点传入。 -- 举例:LarkDocLoader 中,需要提供请求粒度的 RefreshToken,这个 RefreshToken 每个用户每次使用后,都需要更换 +- 举例:给一个 ChatModel 节点传入 Temperature 配置;给一个 Lambda 节点传入自定义 option。 ## 组件 CallOption 形态 @@ -31,12 +31,12 @@ eino/components/model // 抽象实现所在代码位置 eino-ext/components/model -├── maas -│   ├── call_option.go -│   └── Implementation.go -├── openai +├── claude +│   ├── option.go // Component 的一种实现的 CallOption 入参 +│   └── chatmodel.go +├── ollama │   ├── call_option.go // Component 的一种实现的 CallOption 入参 -│   ├── Implementation.go +│   ├── chatmodel.go ``` ### Model 抽象 @@ -60,11 +60,18 @@ type ChatModel interface { } // 此结构体是【组件抽象CallOption】的统一定义。 组件的实现可根据自己的需要取用【组件抽象CallOption】的信息 +// Options is the common options for the model. type Options struct { - Temperature float32 - MaxTokens int - Model string - TopP float32 + // Temperature is the temperature for the model, which controls the randomness of the model. + Temperature *float32 + // MaxTokens is the max number of tokens, if reached the max tokens, the model will stop generating, and mostly return an finish reason of "length". + MaxTokens *int + // Model is the model name. + Model *string + // TopP is the top p for the model, which controls the diversity of the model. + TopP *float32 + // Stop is the stop words for the model, which controls the stopping condition of the model. + Stop []string } // Option is the call option for ChatModel component. @@ -78,34 +85,47 @@ type Option struct { implSpecificOptFn any } +// WithTemperature is the option to set the temperature for the model. func WithTemperature(temperature float32) Option { return Option{ apply: func(opts *Options) { - opts.Temperature = temperature + opts.Temperature = &temperature }, } } +// WithMaxTokens is the option to set the max tokens for the model. func WithMaxTokens(maxTokens int) Option { return Option{ apply: func(opts *Options) { - opts.MaxTokens = maxTokens + opts.MaxTokens = &maxTokens }, } } +// WithModel is the option to set the model name. func WithModel(name string) Option { return Option{ apply: func(opts *Options) { - opts.Model = name + opts.Model = &name }, } } +// WithTopP is the option to set the top p for the model. func WithTopP(topP float32) Option { return Option{ apply: func(opts *Options) { - opts.TopP = topP + opts.TopP = &topP + }, + } +} + +// WithStop is the option to set the stop words for the model. +func WithStop(stop []string) Option { + return Option{ + apply: func(opts *Options) { + opts.Stop = stop }, } } @@ -156,149 +176,116 @@ func GetImplSpecificOptions[T any](base *T, opts ...Option) *T { } ``` -### OpenAI 实现 +### Claude 实现 -> 组件的实现均类似 OpenAI 的实现 -> -> 注:此处为样例,eino-ext/components/model 中暂时没有此场景 +[https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/option.go](https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/option.go) ```go -package openai +package claude import ( "github.com/cloudwego/eino/components/model" ) -// openAIOptions 实现粒度的 CallOption 配置 -type requestOptions struct { - APIKey string - Stop []string - PresencePenalty float32 +type options struct { + TopK *int32 } -// openai 下的 WithXX() 方法,只能对 openai 这一种实现生效 -func WithAPIKey(apiKey string) model.Option { - return model.WrapImplSpecificOptFn[requestOptions](func(o *requestOptions) { - o.APIKey = apiKey - }) -} - -func WithStop(stop []string) model.Option { - return model.WrapImplSpecificOptFn[requestOptions](func(o *requestOptions) { - o.Stop = stop - }) -} - -func WithPresencePenalty(presencePenalty float32) model.Option { - return model.WrapImplSpecificOptFn[requestOptions](func(o *requestOptions) { - o.PresencePenalty = presencePenalty +func WithTopK(k int32) model.Option { + return model.WrapImplSpecificOptFn(func(o *options) { + o.TopK = &k }) } ``` -model/openai/Implementation.go +[https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/claude.go](https://github.com/cloudwego/eino-ext/blob/main/components/model/claude/claude.go) ```go -type ChatModel struct {} +func (c *claude) genMessageNewParams(input []*schema.Message, opts ...model.Option) (anthropic.MessageNewParams, error) { + if len(input) == 0 { + return anthropic.MessageNewParams{}, fmt.Errorf("input is empty") + } -func (cm *ChatModel) Generate(ctx context.Context, in []*schema.Message, opts ...model.Option) ( - - cmOpts := model.GetCommonOptions(&model.Options{ - Model: "gpt-3.5-turbo", - MaxTokens: 1024, - Temperature: 0.7, - }, opts...) - - implOpts := model.GetImplSpecificOptions[requestOptions](&requestOptions{ - Stop: nil, - PresencePenalty: 1, + commonOptions := model.GetCommonOptions(&model.Options{ + Model: &c.model, + Temperature: c.temperature, + MaxTokens: &c.maxTokens, + TopP: c.topP, + Stop: c.stopSequences, }, opts...) + claudeOptions := model.GetImplSpecificOptions(&options{TopK: c.topK}, opts...) - // 在这里开发 OpenAI 的 Model 逻辑 - _ = cmOpts - _ = implOpts + // omit mulple lines... + return nil, nil } +``` + +## 编排中的 CallOption -func (cm *ChatModel) Stream(ctx context.Context, in []*schema.Message, - opts ...model.Option) (outStream *schema.StreamReader[*schema.Message], err error) { - // 同 Generate 接口 +[https://github.com/cloudwego/eino/blob/main/compose/runnable.go](https://github.com/cloudwego/eino/blob/main/compose/runnable.go) + +Graph 编译产物是 Runnable + +```go +type Runnable[I, O any] interface { + Invoke(ctx context.Context, input I, opts ...Option) (output O, err error) + Stream(ctx context.Context, input I, opts ...Option) (output *schema.StreamReader[O], err error) + Collect(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output O, err error) + Transform(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output *schema.StreamReader[O], err error) } ``` -### Graph 编译产物 +Runnable 各方法均接收 compose.Option 列表。 -> Graph 的编译产物是 Runnable[I, O] +[https://github.com/cloudwego/eino/blob/main/compose/graph_call_options.go](https://github.com/cloudwego/eino/blob/main/compose/graph_call_options.go) -option.go +包括 graph run 整体的配置,各类组件的配置,特定 Lambda 的配置等。 ```go +// Option is a functional option type for calling a graph. type Option struct { - options []any - nodeHandler []callbacks.Handler - keys []string + options []any + handler []callbacks.Handler - graphHandler []callbacks.Handler - graphOption []GraphRunOption -} + paths []*NodePath -// 可指定 NodeKey 定点生效 CallOption -func (o Option) DesignateNode(key ...string) Option { - o.keys = append(o.keys, key...) - return o + maxRunSteps int } -func WithChatModelOption(opts ...model.Option) Option { - o := make([]any, 0, len(opts)) - for i := range opts { - o = append(o, opts[i]) - } - return Option{ - options: o, - keys: make([]string, 0), +// DesignateNode set the key of the node which will the option be applied to. +// notice: only effective at the top graph. +// e.g. +// +// embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small")) +// runnable.Invoke(ctx, "input", embeddingOption.DesignateNode("my_embedding_node")) +func (o Option) DesignateNode(key ...string) Option { + nKeys := make([]*NodePath, len(key)) + for i, k := range key { + nKeys[i] = NewNodePath(k) } + return o.DesignateNodeWithPath(nKeys...) } -``` - -runnable.go -```go -type Runnable[I, O any] interface { - Invoke(ctx context.Context, input I, opts ...Option) (output O, err error) - Stream(ctx context.Context, input I, opts ...Option) (output *schema.StreamReader[O], err error) - Collect(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output O, err error) - Transform(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output *schema.StreamReader[O], err error) +// DesignateNodeWithPath sets the path of the node(s) to which the option will be applied to. +// You can make the option take effect in the subgraph by specifying the key of the subgraph. +// e.g. +// DesignateNodeWithPath({"sub graph node key", "node key within sub graph"}) +func (o Option) DesignateNodeWithPath(path ...*NodePath) Option { + o.paths = append(o.paths, path...) + return o } -``` - -Graph 调用 -```go -g := NewGraph[map[string]any, *schema.Message]() - -_nodeOfModel := &openai.ChatModel{}_ - -err = g.AddChatModelNode("openAIModel", _nodeOfModel_) - -r, err := g.Compile() - -// 默认情况下,WithXXX() 的 Option 方法是按照 Component 的类型进行分发的 -// 同一个 WithXXX() 会对同一种 Component 的不同实例同时生效 -// 必要情况下可通过指定 NodeKey,仅针对一个 Node 生效 WithXXX() 方法 -out, err = r.Invoke(ctx, in, WithChatModelOption( - openai.WithAKSK("ak", "sk"), - openai.WithURL("url"), - ), - // 这组 CallOption 仅针对 openAIModel 这个节点生效 - WithChatModelOption( - model.WithModel("gpt-3.5-turto"), - openai.WithAPIKey("xxxx"), - ).DesignateNode("openAIModel"), - ) +// WithEmbeddingOption is a functional option type for embedding component. +// e.g. +// +// embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small")) +// runnable.Invoke(ctx, "input", embeddingOption) +func WithEmbeddingOption(opts ...embedding.Option) Option { + return withComponentOption(opts...) +} ``` -## 编排中的 CallOption - -CallOption 可以按需分配给 Graph 中不同的节点。 +compose.Option 可以按需分配给 Graph 中不同的节点。 ![](/img/eino/graph_runnable_after_compile.png) 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 1dbda21c92..d4f7ac0929 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,19 +1,12 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Callback 用户手册' weight: 0 --- -> 💡 -> TL;DR -> -> 长文,用意是“明确的、无歧义的、充分的”说明 Eino Callback 设计、实现和使用方式的各方面,可用作解决某个具体问题的工具参考,也可以作为入门后想要更进一步了解细节的一个途径。 -> -> 快速入门请移步 :[Eino: 公共切面 - Callbacks](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callbacks_common_aspects) - ## 解决的问题 Component(包括 Lambda)、Graph 编排共同解决“把业务逻辑定义出来”的问题。而 logging, tracing, metrics, 上屏展示等横切面性质的功能,需要有机制把功能注入到 Component(包括 Lambda)、Graph 中。 @@ -121,8 +114,6 @@ type CallbackInput struct { Messages []*schema.Message // Tools is the tools to be used in the model. Tools []*schema.ToolInfo - // ToolChoice is the tool choice, which controls the tool to be used in the model. - ToolChoice any // string / *schema.ToolInfo // Config is the config for the model. Config *Config // Extra is the extra information for the callback. @@ -194,6 +185,8 @@ Graph 会为内部所有的 Node 自动注入 RunInfo。机制是每个 Node 的 ## 触发方式 +![](/img/eino/graph_node_callback_run_place.png) + ### 组件实现内部触发(Component Callback) 在组件实现的代码中,调用 callbacks 包中的 `OnStart(), OnEnd(), OnError(), OnStartWithStreamInput(), OnEndWithStreamInput()`。以 Ark 的 ChatModel 实现为例,在 Generate 方法中: @@ -444,3 +437,15 @@ Handler 内不建议修改 input / output。原因是: 不同 Handler 之间,没有执行顺序的保证,因此不建议通过上面的机制在不同 Handler 间传递信息。本质上是无法保证某一个 Handler 返回的 context,一定会进入下一个 Handler 的函数执行中。 如果需要在不同 Handler 之间传递信息,建议的方式是在最外层的 context(如 graph 执行时传入的 context)中,设置一个全局的、请求维度的变量作为公共信息的存取空间,在各个 Handler 中按需读取和更新这个公共变量。在有 stream 的情况下,可能需要格外注意和保证这个公共变量的并发安全。 + +### 流切记要 Close + +以存在 ChatModel 这种具有真流输出的节点为例,当存在 Callback 切面时,ChatModel 的输出流: +- 既要被下游节点作为输入来消费,又要被 Callback 切面来消费 +- 一个流中的一个帧(Chunk),只能被一个消费方消费到,即流不是广播模型 + +所以此时需要将流进行复制,其复制关系如下: + +![](/img/eino/graph_stream_chunk_copy.png) + +- 如果其中一个 Callback n 没有 Close 对应的流,可能导致原始 Stream 无法 Close 和释放资源。 diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callbacks_common_aspects.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callbacks_common_aspects.md deleted file mode 100644 index 9196d78973..0000000000 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/callbacks_common_aspects.md +++ /dev/null @@ -1,402 +0,0 @@ ---- -Description: "" -date: "2025-01-15" -lastmod: "" -tags: [] -title: 'Eino: 公共切面 - Callbacks' -weight: 0 ---- - -## 切面 - -Eino 切面(Eino Callback)是 Eino 框架对开发提供的扩展 Eino 框架,丰富横向治理的能力 - -可以从以下几个方面,认识 Eino 切面: - -- 切面位置:即能在哪些点位添加切面逻辑 -- 切面注入:可通过哪些方式,注入对应的切面逻辑 -- 切面角色 - - 切面机制:由 Eino 框架提供了,扩展 Eino 自身功能的机制 - - 切面扩展者:基于 Eino 的扩展能力,设计和提供各种各样的独立于 Graph 执行的扩展能力 - - 例如:Langfuse Trace 等 - - 切面使用者:真正使用切面扩展能力的终端业务 - - 例如:针对业务编排的 Graph 图,添加 Langfuse Trace 切面能力。方便对编排产物的执行进行观测 - -### 切面位置 - -![](/img/eino/callback_in_graph.png) - -- 组件切面(Component Callback):组件自带的切面 - - 实现在组件内部的切面执行点位,而不是在加入到 Node 时,由 Node 在组件之外添加的切面点位 - - 组件切面既可应用于 **Graph 编排场景**,也可应用于**组件的独立使用**场景 - - 组件切面、节点切面一般二选一。 - - 当组件声明自己提供组件切面时,节点切面会被自动关闭。 -- 节点切面(Node Callback):组件加入到节点时,由 Node 在组件之外添加的切面点位 - - 由于是在组件之外,只能见到组件的 input/output,无法感知组件运行时的内部状态 - - 仅可应用于 **Graph 编排场景**,在组件独立使用时,无法生效 -- 图切面(Graph Callback):把整张图视为一个整体,在图的前后添加的切面点位 - -切面生效位置有三种: - -- **所有图**的 Graph Callback 和 Node Callback 生效 -- **指定图及其嵌套子图**的 Graph Callback 和 Node Callback 生效 -- **指定图的指定节点**的 Node Callback 生效 - -### 切面注入 - -切面的注入方式,有以下三种: - -- 全局注入:对所有图的所有节点生效 -- 请求粒度注入:在请求时注入,仅对本次请求所经过的节点均生效 -- 组件实例注入:针对实现了 Component Callback 的组件,可针对该组件实例,单独注入切面 - -> 注意:不同的注入时机,其对应的 Callback 的生效位置有所不同 - -#### 全局注入(进程粒度) - -- 生效位置: **所有图**的所有节点或组件的 Callback 均生效 + 所有图的 Graph Callback 生效 - - 针对节点、组件哪一个生效的问题,取决于组件是否声明自己实现了 Component Callback。若声明,则仅 Component Callback 生效;若未声明,则 Node Callback 生效 -- 生效时机: Graph 编译产物的**任意一次执行** -- 注入方式: - - 通过 callbacks.InitCallbackHandlers() 注入。一个进程中,仅最后一次调用生效。建议放在 main 入口函数中进行初始化 - -```go -import "github.com/cloudwego/eino/callbacks" - -func main() { - // 设置全局生效的 Callback Handlers。 - // 一个进程内仅最后一次调用的 callbacks.InitCallbackHandlers() 会生效 - callbacks.InitCallbackHandlers([]callbacks.Handler{&loggerCallbacks{}}) -} - -type loggerCallbacks struct{ - *callbacks.HandlerBuilder -} - -func (l *loggerCallbacks) OnStart(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { - logs.Infof("name: %v, type: %v, component: %v, input: %v", info.Name, info.Type, info.Component, input) - return ctx -} - -func (l *loggerCallbacks) OnEnd(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context { - logs.Infof("name: %v, type: %v, component: %v, output: %v", info.Name, info.Type, info.Component, output) - return ctx -} -``` - -#### 请求粒度注入 - -- 生效的切面位置: **本地调用涉及图及其嵌套子图**的 Graph Callback 和 Node Callback -- 生效时机: Graph 编译产物的本次调用 -- 注入时机: - - 调用 Graph 编译产物 Runnable 方法时,例如调用 Invoke()、Stream()方法,通过 Graph Call Option 传入。 - - GraphCallOption:compose.WithCallbacks(handler) - -**Graph 示例:** - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino/callbacks" - "github.com/cloudwego/eino/compose" - "github.com/cloudwego/eino/schema" -) - -func main() { - - ctx := context.Background() - - // create an instance of your implementation of callbacks.Handler - handler := &callbacks.HandlerBuilder{} - - // create an instance of Graph - // input type is 1st Graph Node's input type, that is ChatTemplate's input type: map[string]any - // output type is last Graph Node's output type, that is ToolsNode's output type: []*schema.Message - g := compose.NewGraph[[]*schema.Message, *schema.Message]() - - // add node and edge here - // err = g.AddChatModelNode("chat_model", chatTpl) - // if err != nil { - // logs.Errorf("AddChatTemplateNode failed, err=%v", err) - // return - // } - - // compile Graph[I, O] to Runnable[I, O] - r, err := g.Compile() - if err != nil { - logs.Errorf("Compile failed, err=%v", err) - return - } - - _, err = r.Invoke(ctx, - []*schema.Message{ - schema.SystemMessage("you are a helpful assistant"), - schema.UserMessage("我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产"), - }, - compose.WithCallbacks(handler), - ) - - _, err = r.Stream(ctx, []*schema.Message{ - schema.SystemMessage("you are a helpful assistant"), - schema.UserMessage("我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产"), - }, - compose.WithCallbacks(handler), - ) -} -``` - -**Chain 示例:** - -> 使用方式和 Graph 是一模一样的 - -```go -package main - -import ( - "context" - - "github.com/cloudwego/eino/callbacks" - "github.com/cloudwego/eino/compose" - "github.com/cloudwego/eino/schema" -) - -func main() { - - ctx := context.Background() - - // create an instance of your implementation of callbacks.Handler - handler := &callbacks.HandlerBuilder{} - - // create an instance of Graph - // input type is 1st Graph Node's input type, that is ChatTemplate's input type: map[string]any - // output type is last Graph Node's output type, that is ToolsNode's output type: []*schema.Message - ch := compose.NewChain[[]*schema.Message, *schema.Message]() - - // append node here - // err = ch.AppendChatModel(chatTpl) - // if err != nil { - // logs.Errorf("AddChatTemplateNode failed, err=%v", err) - // return - // } - - // compile Graph[I, O] to Runnable[I, O] - r, err := ch.Compile() - if err != nil { - logs.Errorf("Compile failed, err=%v", err) - return - } - - _, err = r.Invoke(ctx, - []*schema.Message{ - schema.SystemMessage("you are a helpful assistant"), - schema.UserMessage("我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产"), - }, - compose.WithCallbacks(handler), - ) - - _, err = r.Stream(ctx, []*schema.Message{ - schema.SystemMessage("you are a helpful assistant"), - schema.UserMessage("我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产"), - }, - compose.WithCallbacks(handler), - ) - -} -``` - -## 定制切面 - -从定制切面的场景出发,会有两种定制需求: - -- **切面数据消费**:消费各切面点位上报的切面信息,实现业务定制化的横向治理能力 -- **组件切面上报**:实现一个组件时,定制切面上报的点位,定制上报的切面数据内容 - -### 切面数据消费 - -1. 切面数据的消费接口定义 - - CallbackInput 在接口定义中是一个 any, 推荐采用组件抽象定义的结构体进行上报。以 model 组件为例,推荐使用 model.CallbackInput{} - - CallbackOutput 同 CallbackInput,推荐采用组件抽象定义的结构体上报。 - - 采用推荐的预定义的结构体,有利于切面消费数据时,解析和理解数据内容 - -```go -// RunInfo is the info of run node. -type RunInfo struct { - Name string - Type string - Component components.Component -} - -// CallbackInput is the input of the callback. -// the type of input is defined by the component. -// using type Assert or convert func to convert the input to the right type you want. -// e.g. -// -// CallbackInput in components/model/interface.go is: -// type CallbackInput struct { -// Messages []*schema.Message -// Config *Config -// Extra map[string]any -// } -// -// and provide a func of model.ConvCallbackInput() to convert CallbackInput to *model.CallbackInput -// in callback handler, you can use the following code to get the input: -// -// modelCallbackInput := model.ConvCallbackInput(in) -// if modelCallbackInput == nil { -// // is not a model callback input, just ignore it -// return -// } -type CallbackInput any - -type CallbackOutput any - -type Handler interface { - OnStart(ctx context.Context, info *RunInfo, input CallbackInput) context.Context - OnEnd(ctx context.Context, info *RunInfo, output CallbackOutput) context.Context - - OnError(ctx context.Context, info *RunInfo, err error) context.Context - - OnStartWithStreamInput(ctx context.Context, info *RunInfo, - input *schema.StreamReader[CallbackInput]) context.Context - OnEndWithStreamOutput(ctx context.Context, info *RunInfo, - output *schema.StreamReader[CallbackOutput]) context.Context -} -``` - -1. 定义一个结构体,实现上面的 Handler 接口 - - **WARN****:OnStartWithStreamInput、OnEndWithStreamOutput 中的两个 input/output 流****必须要要关闭****,否则会导致流无法关闭回收,从而导致 Goroutine 或内存缓****慢泄露****。 ** - - 即在这两个函数中一定要调用:input.Close()、output.Close() - - 考虑到用户可能仅消费部分接口,并且对应的两个流式接口又必须要求 Close(),推荐采用实例化 callbacks.HandlerBuilder{} 的方式,可选实现其中的某几个 OnXXXFn 字段 - -```go -import ( - "context" - - "github.com/cloudwego/eino/callbacks" - "github.com/cloudwego/eino/components/model" -) - -var callbackHandler1 = &callbacks.HandlerBuilder{ - - OnErrorFn: func(ctx context.Context, info *callbacks.RunInfo, err error) context.Context { - return ctx - }, - - OnStartFn: func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { - // 以 ChatModel 为例,将 input 转换为 model.CallbackInput{} - in := model.ConvCallbackInput(info) - _ = in - return ctx - }, - - OnStartWithStreamInputFn: func(ctx context.Context, info *callbacks.RunInfo, input *schema.StreamReader[callbacks.CallbackInput]) context.Context { - // 必须要关闭这个流,否则会导致 Goroutine 溢出 - defer input.Close() - // implement - return ctx - }, - - OnEndWithStreamOutputFn: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[callbacks.CallbackOutput]) context.Context { - // 必须要关闭这个流,否则会导致 Goroutine 溢出 - defer output.Close() - // implement - return ctx - }, -} -``` - -1. 按照 [切面注入](/zh/docs/eino/core_modules/chain_and_graph_orchestration/callbacks_common_aspects) 章节,选择合适的方式,将定制的消费切面注入到 Graph/Chain 即可 - -#### WARN:Callback 流切记要 Close - -以存在 ChatModel 这种具有真流输出的节点为例,当存在 Callback 切面时,ChatModel 的输出流: -- 既要被下游节点作为输入来消费,又要被 Callback 切面来消费 -- 一个流中的一个帧(Chunk),只能被一个消费方消费到,即流不是广播模型 - -所以此时需要将流进行复制,其复制关系如下: - -![](/img/eino/stream_copy_in_callback.png) - -- 如果其中一个 Callback n 没有 Close 对应的流,因此 Stream Coper 可能一直阻塞生产,无法退出,从而导致 Stream Coper 的资源无法及时释放。 - -### 组件切面上报 - -当用户定制实现一个组件时,可能因为需要 定制切面上报点位、定制切面上报内容等原因,导致不使用 Node Callback,而是选择定制实现 Component Callback。 -- Node Callback 仅能上报组件抽象的输入输出信息,无法获取更多的组件内部信息。 - -组件切面定制上报逻辑的示例,以 ChatModel 为例: - -```go -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "runtime/debug" - "time" - - - "github.com/cloudwego/eino/callbacks" - "github.com/cloudwego/eino/components/model" - "github.com/cloudwego/eino/schema" -) - - -type ChatModel struct {} - -func (cm *ChatModel) Generate(ctx context.Context, in []*schema.Message, opts ...model.Option) ( - outMsg *schema.Message, err error) { - - var ( - // 从 ctx 中获取 Node 执行时,由 Eino 框架注入的 CallbackManager(cbm) - cbm, cbmOK = callbacks.ManagerFromCtx(ctx) - ) - - defer func() { - // 如果 cbm 存在,并且产生错误,则在此处上报错误信息 - if err != nil && cbmOK { - _ = cbm.OnError(ctx, err) - } - }() - - // TODO: 在这里处理用户请求参数 - - // 如果 cbm 存在,则在组件逻辑真正执行前,上报请求信息 - if cbmOK { - ctx = cbm.OnStart(ctx, &model.CallbackInput{ - Messages: in, - Config: reqConf, - }) - } - - resp, err := cm.cli.CreateChatCompletion(ctx, *req) - if err != nil { - return nil, err - } - - // TODO: 在这里处理响应信息,并实现转换 - - // 在组件逻辑执行结束后,上报组件的处理结果 - if cbmOK { - _ = cbm.OnEnd(ctx, &model.CallbackOutput{ - Message: outMsg, - Config: reqConf, - TokenUsage: usage, - }) - } - - return outMsg, nil -} -``` - -注:当涉及到流式数据的切面上报时,需要将原来的流,Copy 出两份,一份作为输出,一份留作为 Callback 消费(CallbackManager 会根据 Callback Handler 的数量再次进行 Copy)。 针对流的相关操作,可参考 [Stream 流](/zh/docs/eino/overview) 章节 - -## 常见问题 - -- 调试环境问题 -- 采样率问题 -- 为什么出现了 goroutine 泄露? diff --git a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md index d14f8c8b7c..3fd948f9b8 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/chain_graph_introduction.md @@ -1,69 +1,99 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Chain/Graph 编排介绍' weight: 1 --- +> 本文所有代码样例都在:[https://github.com/cloudwego/eino-examples/tree/main/compose](https://github.com/cloudwego/eino-examples/tree/main/compose) + ## Graph 编排 ### Graph ```go +package main + import ( + "context" + "fmt" + "io" + + "github.com/cloudwego/eino/components/model" + "github.com/cloudwego/eino/components/prompt" "github.com/cloudwego/eino/compose" + "github.com/cloudwego/eino/schema" ) const ( - _nodeOfModel _= "model" - _nodeOfPrompt _= "prompt" + nodeOfModel = "model" + nodeOfPrompt = "prompt" ) func main() { ctx := context.Background() g := compose.NewGraph[map[string]any, *schema.Message]() - + pt := prompt.FromMessages( - schema.HumanMessage("what's the weather in {location}?"), + schema.FString, + schema.UserMessage("what's the weather in {location}?"), ) - - err := g.AddChatTemplateNode(_nodeOfPrompt_, pt) - assert.NoError(t, err) - - cm := &chatModel{ - msgs: []*schema.Message{ - { - Role: schema._AI_, - Content: "the weather is good", - }, - }, + + _ = g.AddChatTemplateNode(nodeOfPrompt, pt) + _ = g.AddChatModelNode(nodeOfModel, &mockChatModel{}, compose.WithNodeName("ChatModel")) + _ = g.AddEdge(compose.START, nodeOfPrompt) + _ = g.AddEdge(nodeOfPrompt, nodeOfModel) + _ = g.AddEdge(nodeOfModel, compose.END) + + r, err := g.Compile(ctx, compose.WithMaxRunSteps(10)) + if err != nil { + panic(err) } - - err = g.AddChatModelNode(_nodeOfModel_, cm, WithNodeName("MockChatModel")) - assert.NoError(t, err) - - err = g.AddEdge(_START_, _nodeOfPrompt_) - assert.NoError(t, err) - - err = g.AddEdge(_nodeOfPrompt_, _nodeOfModel_) - assert.NoError(t, err) - - err = g.AddEdge(_nodeOfModel_, _END_) - assert.NoError(t, err) - - r, err := g.Compile(WithMaxRunSteps(10)) - assert.NoError(t, err) - + in := map[string]any{"location": "beijing"} ret, err := r.Invoke(ctx, in) - assert.NoError(t, err) - t.Logf("invoke result: %v", ret) - + fmt.Println("invoke result: ", ret) + // stream s, err := r.Stream(ctx, in) - assert.NoError(t, err) + if err != nil { + panic(err) + } + + defer s.Close() + for { + chunk, err := s.Recv() + if err != nil { + if err == io.EOF { + break + } + panic(err) + } + + fmt.Println("stream chunk: ", chunk) + } +} + +type mockChatModel struct{} + +func (m *mockChatModel) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) { + return schema.AssistantMessage("the weather is good", nil), nil +} + +func (m *mockChatModel) Stream(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.StreamReader[*schema.Message], error) { + sr, sw := schema.Pipe[*schema.Message](0) + go func() { + defer sw.Close() + sw.Send(schema.AssistantMessage("the weather is", nil), nil) + sw.Send(schema.AssistantMessage("good", nil), nil) + }() + return sr, nil +} + +func (m *mockChatModel) BindTools(tools []*schema.ToolInfo) error { + panic("implement me") } ``` @@ -88,37 +118,35 @@ import ( "github.com/cloudwego/eino/components/tool/utils" "github.com/cloudwego/eino/compose" "github.com/cloudwego/eino/schema" + + "github.com/cloudwego/eino-examples/internal/gptr" + "github.com/cloudwego/eino-examples/internal/logs" ) func main() { - accessKey := os.Getenv("OPENAI_API_KEY") + openAIBaseURL := os.Getenv("OPENAI_BASE_URL") + openAIAPIKey := os.Getenv("OPENAI_API_KEY") + modelName := os.Getenv("MODEL_NAME") ctx := context.Background() - - callbacks.InitCallbackHandlers([]callbacks.Handler{&loggerCallbacks{}}) - const ( - _messageHistories _= "message_histories" - _messageUserQuery _= "user_query" - ) + callbacks.InitCallbackHandlers([]callbacks.Handler{&loggerCallbacks{}}) // 1. create an instance of ChatTemplate as 1st Graph Node systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的` - chatTpl := prompt.FromMessages(schema._FString_, + chatTpl := prompt.FromMessages(schema.FString, schema.SystemMessage(systemTpl), - schema.MessagesPlaceholder(_messageHistories_, true), - schema.MessagesPlaceholder(_messageUserQuery_, false), + schema.MessagesPlaceholder("message_histories", true), + schema.UserMessage("{user_query}"), ) - baseURL := "https://search.bytedance.net/gpt/openapi/online/multimodal/crawl" - modelConf := &openai.ChatModelConfig{ - BaseURL: baseURL, - APIKey: accessKey, + BaseURL: openAIBaseURL, + APIKey: openAIAPIKey, ByAzure: true, - Model: "gpt-4o-2024-05-13", - Temperature: 0.7, + Model: modelName, + Temperature: gptr.Of(float32(0.7)), APIVersion: "2024-06-01", } @@ -134,7 +162,7 @@ func main() { &schema.ToolInfo{ Name: "user_info", Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息", - Params: map[string]*schema.ParameterInfo{ + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ "name": { Type: "string", Desc: "用户的姓名", @@ -143,7 +171,7 @@ func main() { Type: "string", Desc: "用户的邮箱", }, - }, + }), }, func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) { return &userInfoResponse{ @@ -160,6 +188,7 @@ func main() { logs.Errorf("Get ToolInfo failed, err=%v", err) return } + // 4. bind ToolInfo to ChatModel. ToolInfo will remain in effect until the next BindTools. err = chatModel.BindForcedTools([]*schema.ToolInfo{info}) if err != nil { @@ -177,9 +206,9 @@ func main() { } const ( - _nodeKeyOfTemplate _= "template" - _nodeKeyOfChatModel _= "chat_model" - _nodeKeyOfTools _= "tools" + nodeKeyOfTemplate = "template" + nodeKeyOfChatModel = "chat_model" + nodeKeyOfTools = "tools" ) // 6. create an instance of Graph @@ -188,50 +217,22 @@ func main() { g := compose.NewGraph[map[string]any, []*schema.Message]() // 7. add ChatTemplate into graph - err = g.AddChatTemplateNode(_nodeKeyOfTemplate_, chatTpl) - if err != nil { - logs.Errorf("AddChatTemplateNode failed, err=%v", err) - return - } + _ = g.AddChatTemplateNode(nodeKeyOfTemplate, chatTpl) // 8. add ChatModel into graph - err = g.AddChatModelNode(_nodeKeyOfChatModel_, chatModel) - if err != nil { - logs.Errorf("AddChatModelNode failed, err=%v", err) - return - } + _ = g.AddChatModelNode(nodeKeyOfChatModel, chatModel) // 9. add ToolsNode into graph - err = g.AddToolsNode(_nodeKeyOfTools_, toolsNode) - if err != nil { - logs.Errorf("AddToolsNode failed, err=%v", err) - return - } + _ = g.AddToolsNode(nodeKeyOfTools, toolsNode) // 10. add connection between nodes - err = g.AddEdge(compose._START_, _nodeKeyOfTemplate_) - if err != nil { - logs.Errorf("AddEdge failed,start=%v, end=%v, err=%v", compose._START_, _nodeKeyOfTemplate_, err) - return - } + _ = g.AddEdge(compose.START, nodeKeyOfTemplate) - err = g.AddEdge(_nodeKeyOfTemplate_, _nodeKeyOfChatModel_) - if err != nil { - logs.Errorf("AddEdge failed,start=%v, end=%v, err=%v", _nodeKeyOfTemplate_, _nodeKeyOfChatModel_, err) - return - } + _ = g.AddEdge(nodeKeyOfTemplate, nodeKeyOfChatModel) - err = g.AddEdge(_nodeKeyOfChatModel_, _nodeKeyOfTools_) - if err != nil { - logs.Errorf("AddEdge failed,start=%v, end=%v, err=%v", _nodeKeyOfChatModel_, _nodeKeyOfTools_, err) - return - } + _ = g.AddEdge(nodeKeyOfChatModel, nodeKeyOfTools) - err = g.AddEdge(_nodeKeyOfTools_, compose._END_) - if err != nil { - logs.Errorf("AddEdge failed,start=%v, end=%v, err=%v", _nodeKeyOfTools_, compose._END_, err) - return - } + _ = g.AddEdge(nodeKeyOfTools, compose.END) // 9. compile Graph[I, O] to Runnable[I, O] r, err := g.Compile(ctx) @@ -241,10 +242,8 @@ func main() { } out, err := r.Invoke(ctx, map[string]any{ - _messageHistories_: []*schema.Message{}, - _messageUserQuery_: []*schema.Message{ - schema.UserMessage("我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产"), - }, + "message_histories": []*schema.Message{}, + "user_query": "我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产", }) if err != nil { logs.Errorf("Invoke failed, err=%v", err) @@ -304,21 +303,24 @@ import ( "context" "errors" "io" - "log" + "runtime/debug" "strings" "unicode/utf8" "github.com/cloudwego/eino/compose" "github.com/cloudwego/eino/schema" + "github.com/cloudwego/eino/utils/safe" + + "github.com/cloudwego/eino-examples/internal/logs" ) func main() { ctx := context.Background() const ( - _nodeOfL1 _= "invokable" - _nodeOfL2 _= "streamable" - _nodeOfL3 _= "transformable" + nodeOfL1 = "invokable" + nodeOfL2 = "streamable" + nodeOfL3 = "transformable" ) type testState struct { @@ -345,18 +347,15 @@ func main() { return out, nil } - err := sg.AddLambdaNode(_nodeOfL1_, l1, + _ = sg.AddLambdaNode(nodeOfL1, l1, compose.WithStatePreHandler(l1StateToInput), compose.WithStatePostHandler(l1StateToOutput)) - if err != nil { - log.Printf("sg.AddLambdaNode failed, err=%v", err) - return - } l2 := compose.StreamableLambda(func(ctx context.Context, input string) (output *schema.StreamReader[string], err error) { outStr := "StreamableLambda: " + input sr, sw := schema.Pipe[string](utf8.RuneCountInString(outStr)) + // nolint: byted_goroutine_recover go func() { for _, field := range strings.Fields(outStr) { sw.Send(field+" ", nil) @@ -372,11 +371,7 @@ func main() { return out, nil } - err = sg.AddLambdaNode(_nodeOfL2_, l2, compose.WithStatePostHandler(l2StateToOutput)) - if err != nil { - log.Printf("sg.AddLambdaNode failed, err=%v", err) - return - } + _ = sg.AddLambdaNode(nodeOfL2, l2, compose.WithStatePostHandler(l2StateToOutput)) l3 := compose.TransformableLambda(func(ctx context.Context, input *schema.StreamReader[string]) ( output *schema.StreamReader[string], err error) { @@ -385,6 +380,16 @@ func main() { sr, sw := schema.Pipe[string](20) go func() { + + defer func() { + panicErr := recover() + if panicErr != nil { + err := safe.NewPanicErr(panicErr, debug.Stack()) + logs.Errorf("panic occurs: %v\n", err) + } + + }() + for _, field := range strings.Fields(prefix) { sw.Send(field+" ", nil) } @@ -395,8 +400,8 @@ func main() { if err == io.EOF { break } - // _TODO: how to trace this kind of error in the goroutine of processing sw_ -_ _sw.Send(chunk, err) + // TODO: how to trace this kind of error in the goroutine of processing sw + sw.Send(chunk, err) break } @@ -411,59 +416,39 @@ _ _sw.Send(chunk, err) l3StateToOutput := func(ctx context.Context, out string, state *testState) (string, error) { state.ms = append(state.ms, out) - log.Printf("state result: ") + logs.Infof("state result: ") for idx, m := range state.ms { - log.Printf(" %vth: %v", idx, m) + logs.Infof(" %vth: %v", idx, m) } return out, nil } - err = sg.AddLambdaNode(_nodeOfL3_, l3, compose.WithStatePostHandler(l3StateToOutput)) - if err != nil { - log.Printf("sg.AddLambdaNode failed, err=%v", err) - return - } + _ = sg.AddLambdaNode(nodeOfL3, l3, compose.WithStatePostHandler(l3StateToOutput)) - err = sg.AddEdge(compose._START_, _nodeOfL1_) - if err != nil { - log.Printf("sg.AddEdge failed, err=%v", err) - return - } + _ = sg.AddEdge(compose.START, nodeOfL1) - err = sg.AddEdge(_nodeOfL1_, _nodeOfL2_) - if err != nil { - log.Printf("sg.AddEdge failed, err=%v", err) - return - } + _ = sg.AddEdge(nodeOfL1, nodeOfL2) - err = sg.AddEdge(_nodeOfL2_, _nodeOfL3_) - if err != nil { - log.Printf("sg.AddEdge failed, err=%v", err) - return - } + _ = sg.AddEdge(nodeOfL2, nodeOfL3) - err = sg.AddEdge(_nodeOfL3_, compose._END_) - if err != nil { - log.Printf("sg.AddEdge failed, err=%v", err) - return - } + _ = sg.AddEdge(nodeOfL3, compose.END) run, err := sg.Compile(ctx) if err != nil { - log.Printf("sg.Compile failed, err=%v", err) + logs.Errorf("sg.Compile failed, err=%v", err) return } out, err := run.Invoke(ctx, "how are you") if err != nil { - log.Printf("run.Invoke failed, err=%v", err) + logs.Errorf("run.Invoke failed, err=%v", err) return } - log.Printf("invoke result: %v", out) + logs.Infof("invoke result: %v", out) stream, err := run.Stream(ctx, "how are you") if err != nil { - log.Printf("run.Stream failed, err=%v", err) + logs.Errorf("run.Stream failed, err=%v", err) return } @@ -474,11 +459,11 @@ _ _sw.Send(chunk, err) if errors.Is(err, io.EOF) { break } - log.Printf("stream.Recv() failed, err=%v", err) + logs.Infof("stream.Recv() failed, err=%v", err) break } - log.Printf(chunk) + logs.Tokenf("%v", chunk) } stream.Close() @@ -488,10 +473,10 @@ _ _sw.Send(chunk, err) stream, err = run.Transform(ctx, sr) if err != nil { - log.Printf("run.Transform failed, err=%v", err) + logs.Infof("run.Transform failed, err=%v", err) return } - + for { chunk, err := stream.Recv() @@ -499,11 +484,11 @@ _ _sw.Send(chunk, err) if errors.Is(err, io.EOF) { break } - log.Printf("stream.Recv() failed, err=%v", err) + logs.Infof("stream.Recv() failed, err=%v", err) break } - log.Printf(chunk) + logs.Infof("%v", chunk) } stream.Close() } @@ -527,23 +512,21 @@ import ( "github.com/cloudwego/eino/components/prompt" "github.com/cloudwego/eino/compose" "github.com/cloudwego/eino/schema" -) - -func init() { - apiKey := os.Getenv("OPENAI_API_KEY") - baseURL := os.Getenv("OPENAI_BASE_URL") // using azure - if apiKey == "" || baseURL != "" { - return - } -} + "github.com/cloudwego/eino-examples/internal/gptr" + "github.com/cloudwego/eino-examples/internal/logs" +) func main() { + openAPIBaseURL := os.Getenv("OPENAI_BASE_URL") + openAPIAK := os.Getenv("OPENAI_API_KEY") + modelName := os.Getenv("MODEL_NAME") + ctx := context.Background() - // 分支节点 - const _randLimit _= 2 + // build branch func + const randLimit = 2 branchCond := func(ctx context.Context, input map[string]any) (string, error) { // nolint: byted_all_nil_return - if rand.Intn(_randLimit_) == 1 { + if rand.Intn(randLimit) == 1 { return "b1", nil } @@ -551,7 +534,7 @@ func main() { } b1 := compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) { - fmt.Println("hello in branch lambda 01") + logs.Infof("hello in branch lambda 01") if kvs == nil { return nil, fmt.Errorf("nil map") } @@ -561,7 +544,7 @@ func main() { }) b2 := compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) { - fmt.Println("hello in branch lambda 02") + logs.Infof("hello in branch lambda 02") if kvs == nil { return nil, fmt.Errorf("nil map") } @@ -570,7 +553,7 @@ func main() { return kvs, nil }) - // 并发节点 + // build parallel node parallel := compose.NewParallel() parallel. AddLambda("role", compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (string, error) { @@ -586,8 +569,17 @@ func main() { return "你的叫声是怎样的?", nil })) - // 顺序节点 - cm, err := openai.NewChatModel(context.Background(), nil) + modelConf := &openai.ChatModelConfig{ + BaseURL: openAPIBaseURL, + APIKey: openAPIAK, + ByAzure: true, + Model: modelName, + Temperature: gptr.Of(float32(0.7)), + APIVersion: "2024-06-01", + } + + // create chat model node + cm, err := openai.NewChatModel(context.Background(), modelConf) if err != nil { log.Panic(err) return @@ -595,25 +587,25 @@ func main() { rolePlayerChain := compose.NewChain[map[string]any, *schema.Message]() rolePlayerChain. - AppendChatTemplate(prompt.FromMessages(schema._FString_, schema.SystemMessage(`You are a {role}.`), schema.UserMessage(`{input}`))). + AppendChatTemplate(prompt.FromMessages(schema.FString, schema.SystemMessage(`You are a {role}.`), schema.UserMessage(`{input}`))). AppendChatModel(cm) - // =========== 构建 chain =========== + // =========== build chain =========== chain := compose.NewChain[map[string]any, string]() chain. AppendLambda(compose.InvokableLambda(func(ctx context.Context, kvs map[string]any) (map[string]any, error) { // do some logic to prepare kv as input val for next node // just pass through - fmt.Println("in view lambda: ", kvs) + logs.Infof("in view lambda: %v", kvs) return kvs, nil })). AppendBranch(compose.NewChainBranch(branchCond).AddLambda("b1", b1).AddLambda("b2", b2)). // nolint: byted_use_receiver_without_nilcheck + AppendPassthrough(). AppendParallel(parallel). AppendGraph(rolePlayerChain). AppendLambda(compose.InvokableLambda(func(ctx context.Context, m *schema.Message) (string, error) { // do some logic to check the output or something - fmt.Println("in view of messages: ", m.Content) - + logs.Infof("in view of messages: %v", m.Content) return m.Content, nil })) @@ -630,7 +622,6 @@ func main() { return } - fmt.Println("output is : ", output) - + logs.Infof("output is : %v", output) } ``` 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 b02ddb4271..eb983437fb 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: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: 编排的设计理念' @@ -127,7 +127,6 @@ parallel 节点的输出一定是一个 `map[string]any`,其中的 key 则是 ```go func TestParallel() { chain := compose.NewChain[map[string]any, map[string]*schema.Message]() - templateNode := &fakeTemplateNode{} // input: map[string]any, output: []*schema.Message parallel := compose.NewParallel() model01 := &fakeChatModel{} // input: []*schema.Message, output: *schema.Message @@ -171,58 +170,72 @@ Workflow 的类型对齐的维度,由整体的 Input & Output 改成了字段 ### invoke 和 stream 下的类型对齐方式 -在 eino 中,编排的结果是 graph 或 chain,若要运行,则需要使用 `Compile()` 来生成一个 `Runnable` 接口。 +在 Eino 中,编排的结果是 graph 或 chain,若要运行,则需要使用 `Compile()` 来生成一个 `Runnable` 接口。 -Runnable 的一个重要作用就是提供了 `invoke`、`stream`、`collect`、`transform` 几种调用方式的降级兼容。 +Runnable 的一个重要作用就是提供了 `I``nvoke`、`S``tream`、`C``ollect`、`T``ransform` 四种调用方式。 > 上述几种调用方式的介绍以及详细的 Runnable 介绍可以查看: [Eino: 基础概念介绍](/zh/docs/eino/overview) -以我们最常见的 invoke 和 stream 模式为例,其接口签名如下: +假设我们有一个 `Graph[[]*schema.Message, []*schema.Message]`,里面有一个 ChatModel 节点,一个 Lambda 节点,Compile 之后是一个 `Runnable[[]*schema.Message, []*schema.Message]`。 ```go -type Runnable[I, O any] interface { - Invoke(ctx context.Context, input I, opts ...Option) (output O, err error) - Stream(ctx context.Context, input I, opts ...Option) (output *schema.StreamReader[O], err error) -} -``` - -以 chat model 的场景为例,Runnable 加上 I,O 类型后签名如下: - -```go -type Runnable interface { - Invoke(ctx context.Context, input []*schema.Message, opts ...Option) (output *schema.Message, err error) - Stream(ctx context.Context, input []*schema.Message, opts ...Option) (output *schema.StreamReader[*schema.Message], err error) -} -``` - -在 Invoke 模式下,返回的 output 的类型为 `*schema.Message`; 在 Stream 模式下,其返回的 output 类型必须为 `*schema.StreamReader[*schema.Message]`。也就是 stream 的每一帧的类型和 invoke 的结果类型是相同的。 - -一般来说,Stream 得到的每一帧合并起来应当和 invoke 的结果相同,在上面这个场景中,也即要求: - -```go -func TestInvokeAndStream() { - var r Runnable[[]*schema.Message, *schema.Message] - - reader, err := r.Stream(...) - allFrames := make([]*schema.Message, 0) +package main + +import ( + "context" + "io" + "testing" + + "github.com/cloudwego/eino/compose" + "github.com/cloudwego/eino/schema" + "github.com/stretchr/testify/assert" +) + +func TestTypeMatch(t *testing.T) { + ctx := context.Background() + + g1 := compose.NewGraph[[]*schema.Message, string]() + _ = g1.AddChatModelNode("model", &mockChatModel{}) + _ = g1.AddLambdaNode("lambda", compose.InvokableLambda(func(_ context.Context, msg *schema.Message) (string, error) { + return msg.Content, nil + })) + _ = g1.AddEdge(compose.START, "model") + _ = g1.AddEdge("model", "lambda") + _ = g1.AddEdge("lambda", compose.END) + + runner, err := g1.Compile(ctx) + assert.NoError(t, err) + + c, err := runner.Invoke(ctx, []*schema.Message{ + schema.UserMessage("what's the weather in beijing?"), + }) + assert.NoError(t, err) + assert.Equal(t, "the weather is good", c) + + s, err := runner.Stream(ctx, []*schema.Message{ + schema.UserMessage("what's the weather in beijing?"), + }) + assert.NoError(t, err) + + var fullStr string for { - frame, err := reader.Recev() - ... - allFrames = append(allFrames, frame) - ... + chunk, err := s.Recv() + if err != nil { + if err == io.EOF { + break + } + panic(err) + } + + fullStr += chunk } - - invokeRes, err := r.Invoke(...) - - // allFrames 合并后需要和 invokeRes 相同 + assert.Equal(t, c, fullStr) } ``` -在 stream 模式下,`合并帧` 是一个非常常见的操作,例如在和大模型的交互中,可以把已经接收到的所有帧拼接起来(Concatenate),得到一个完整的输出。 - -另外,在框架中,当一个仅提供了 Stream 接口的节点,被编排后使用 Invoke 调用,框架则会把 Stream 降级为 Invoke,此时的操作是 底层调用开发者提供的 Stream 接口,获取完整的帧后,把所有帧合并,得到的结果再流转到下一节点。 这个过程中,也是使用的 `拼接``帧` 。 +当我们以 Stream 方式调用上面编译好的 Runnable 时,model 节点会输出 `*schema.StreamReader[*Message]`,但是 lambda 节点是 InvokableLambda,只接收非流式的 `*schema.Message` 作为输入。这也符合类型对齐规则,因为 Eino 框架会自动把流式的 Message 拼接成完整的 Message。 -拼接时,会先把 `*StreamReader[T] ` 中的所有元素取出来转成 `[]T`。框架内已经内置支持了如下类型的拼接: +在 stream 模式下,`拼接``帧` 是一个非常常见的操作,拼接时,会先把 `*StreamReader[T] ` 中的所有元素取出来转成 `[]T`,再尝试把 `[]T` 拼接成一个完整的 `T`。框架内已经内置支持了如下类型的拼接: - `*schema.Message`: 详情见 `schema.ConcatMessages()` - `string`: 实现逻辑等同于 `+=` @@ -251,7 +264,7 @@ func concatTStreamForTest(items []*tStreamConcatItemForTest) (*tStreamConcatItem return &tStreamConcatItemForTest{s: s}, nil } -func init() { +func Init() { // 注册到全局的拼接方法中 compose.RegisterStreamChunkConcatFunc(concatTStreamForTest) } @@ -269,16 +282,6 @@ eino 的 Graph 类型对齐检查,会在 `err = graph.AddEdge("node1", "node2" 这种场景适用于开发者能自行处理好上下游类型对齐的情况,可根据不同类型选择下游执行节点。 -## 大模型场景的编排 - -eino 在编排中的是以大模型应用为核心场景的编排系统,因此在 eino 的编排设计中,是直接把 `component` 作为了编排的直接主体,封装了在大模型应用中最常用的组件,详细的 API 查看: [Eino: 基础概念介绍](/zh/docs/eino/overview) - -大多数情况下,业务的实现应当把自己的组件实现为上述组件中的一种,或者直接使用 eino-ext 中已经封装好的组件。 - -当然,除了上述标准的组件外,还有很多场景我们需要实现一些自定义的代码逻辑,在 eino 中,这就是 `Lambda` 组件。这是一个很泛化的组件,可以基于这个基础组件实现几乎所有的需求,实际上,在 eino 内部,上述的组件也都是使用 lambda 来实现的。 - -> 更多信息可以参考: [Eino: Components 抽象&实现](/zh/docs/eino/core_modules/components) - ## 带有明确倾向性的设计选择 ### 扇入与合并 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 e559bfb6b9..d2251ec4dd 100644 --- a/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md +++ b/content/zh/docs/eino/core_modules/chain_and_graph_orchestration/stream_programming_essentials.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: Eino 流式编程要点 @@ -44,6 +44,7 @@ type ChatModel interface { Generate(ctx context.Context**, **input []*schema.Message**, **opts ...Option) (*schema.Message**, **error) Stream(ctx context.Context**, **input []*schema.Message**, **opts ...Option) ( *schema.StreamReader[*schema.Message]**, **error) + // other methods omitted... } ``` @@ -101,7 +102,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/components/_index.md b/content/zh/docs/eino/core_modules/components/_index.md index efafb2eea8..6c06618b7a 100644 --- a/content/zh/docs/eino/core_modules/components/_index.md +++ b/content/zh/docs/eino/core_modules/components/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Components 组件' @@ -44,17 +44,17 @@ weight: 1 3. Embedding 之后将数据索引进行存储的组件抽象: `Indexer` -> 详见 [Eino: Indexer & Retriever 使用说明](/zh/docs/eino/core_modules/components/indexer_and_retriever_guide) +> 详见 [Eino: Indexer 使用说明](/zh/docs/eino/core_modules/components/indexer_guide) 4. 将语义相关文本文档进行索引和召回的组件抽象: `Retriever` -> 详见 [Eino: Indexer & Retriever 使用说明](/zh/docs/eino/core_modules/components/indexer_and_retriever_guide) +> 详见 [Eino: Retriever 使用说明](/zh/docs/eino/core_modules/components/retriever_guide) **决策执行类组件**: 1. 大模型能够做决策并调用工具的组件抽象:`ToolsNode` -> 详见 [Eino: Tool & ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tool_and_tools_node_guide) +> 详见 [Eino: ToolsNode 使用说明](/zh/docs/eino/core_modules/components/tools_node_guide) **自定义组件:** 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 652ba5c0b6..d9bd5bc861 100644 --- a/content/zh/docs/eino/core_modules/components/chat_model_guide.md +++ b/content/zh/docs/eino/core_modules/components/chat_model_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: ChatModel 使用说明' @@ -57,8 +57,6 @@ type ChatModel interface { ### **Message 结构体 ** -> ### **[TODO, 放到专门的结构体说明文档中]** - ```go type Message struct { // Role 表示消息的角色(system/user/assistant/tool) 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 2b430cd979..52bf30f06c 100644 --- a/content/zh/docs/eino/core_modules/components/chat_template_guide.md +++ b/content/zh/docs/eino/core_modules/components/chat_template_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: ChatTemplate 使用说明' @@ -40,20 +40,19 @@ type ChatTemplate interface { Prompt 组件内置支持三种模板化方式: -1. String 格式 (schema.FString) +1. FString 格式 (schema.FString) - 使用 `{variable}` 语法进行变量替换 - 简单直观,适合基础文本替换场景 - 示例:`"你是一个{role},请帮我{task}。"` -2. Template 格式 (schema.FTemplate) +2. GoTemplate 格式 (schema.GoTemplate) - 使用 Go 标准库的 text/template 语法 - 支持条件判断、循环等复杂逻辑 - 示例:`"{{if .expert}}作为专家{{end}}请{{.action}}"` -3. Jinja2 格式 (schema.FJinja2) +3. Jinja2 格式 (schema.Jinja2) - 使用 Jinja2 模板语法 - - 支持丰富的过滤器和控制结构 - 示例:`"{% if level == 'expert' %}以专家的角度{% endif %}分析{{topic}}"` ### **公共 Option** diff --git a/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md b/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md index ede90f81e8..a51a0d0f55 100644 --- a/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md +++ b/content/zh/docs/eino/core_modules/components/document_loader_guide/document_parser_interface_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-06" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Document Parser 接口使用说明' @@ -19,7 +19,14 @@ Document Parser 是一个用于解析文档内容的工具包。它不是一个 ### **Parser 接口** +> 代码位置:eino/components/document/parser/interface.go + ```go +import ( + "github.com/cloudwego/eino/schema" +) + +// Parser is a document parser, can be used to parse a document from a reader. type Parser interface { Parse(ctx context.Context, reader io.Reader, opts ...Option) ([]*schema.Document, error) } @@ -59,49 +66,76 @@ type Options struct { 最基础的文本解析器,将输入内容直接作为文档内容: +> 代码位置:eino-examples/components/document/parser/textparser + ```go -// 使用示例 -docs, err := TextParser{}.Parse(ctx, strings.NewReader("hello world")) -if err != nil { - return err -} -fmt.Println(docs[0].Content) // 输出: hello world +import "github.com/cloudwego/eino/components/document/parser" + +textParser := parser.TextParser{} +docs, _ := textParser.Parse(ctx, strings.NewReader("hello world")) + +logs.Infof("text content: %v", docs[0].Content) ``` ### **ExtParser** 基于文件扩展名的解析器,可以根据文件扩展名自动选择合适的解析器: +> 代码位置:eino-examples/components/document/parser/extparser + ```go -// 创建扩展解析器 -parser, err := NewExtParser(ctx, &ExtParserConfig{ - // 注册特定扩展名的解析器 - Parsers: map[string]Parser{ - ".html": html.NewParser(&html.ParserConfig{ - // HTML 解析器的配置 - RemoveScript: true, // 移除脚本标签 - RemoveStyle: true, // 移除样式标签 - }), - ".pdf": pdf.NewParser(&pdf.ParserConfig{ - // PDF 解析器的配置 - ExtractImages: false, // 不提取图片 - }), - }, - // 设置默认解析器,用于处理未知格式 - FallbackParser: TextParser{}, -}) -if err != nil { - return err -} +package main + +import ( + "context" + "os" -// 使用解析器 -file, _ := os.Open("./document.html") -docs, err := parser.Parse(ctx, file, - WithURI("./document.html"), // 必须提供 URI 以便选��正确的解析器 - WithExtraMeta(map[string]any{ - "source": "local", - }), + "github.com/cloudwego/eino-ext/components/document/parser/html" + "github.com/cloudwego/eino-ext/components/document/parser/pdf" + "github.com/cloudwego/eino/components/document/parser" + + "github.com/cloudwego/eino-examples/internal/gptr" + "github.com/cloudwego/eino-examples/internal/logs" ) + +func main() { + ctx := context.Background() + + textParser := parser.TextParser{} + + htmlParser, _ := html.NewParser(ctx, &html.Config{ + Selector: gptr.Of("body"), + }) + + pdfParser, _ := pdf.NewPDFParser(ctx, &pdf.Config{}) + + // 创建扩展解析器 + extParser, _ := parser.NewExtParser(ctx, &parser.ExtParserConfig{ + // 注册特定扩展名的解析器 + Parsers: map[string]parser.Parser{ + ".html": htmlParser, + ".pdf": pdfParser, + }, + // 设置默认解析器,用于处理未知格式 + FallbackParser: textParser, + }) + + // 使用解析器 + filePath := "./testdata/test.html" + file, _ := os.Open(filePath) + + docs, _ := extParser.Parse(ctx, file, + // 必须提供 URI ExtParser 选择正确的解析器进行解析 + parser.WithURI(filePath), + parser.WithExtraMeta(map[string]any{ + "source": "local", + }), + ) + + for idx, doc := range docs { + logs.Infof("doc_%v content: %v", idx, doc.Content) + } +} ``` ### 其他实现 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 aa6a5c7f23..c704f75e00 100644 --- a/content/zh/docs/eino/core_modules/components/document_transformer_guide.md +++ b/content/zh/docs/eino/core_modules/components/document_transformer_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-06" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Document Transformer 使用说明' @@ -39,8 +39,6 @@ type Transformer interface { ### **Document 结构体** -【TODO】 可把所有的公共结构体放到一个特定的地方 - ```go type Document struct { // ID 是文档的唯一标识符 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 1a3b7050ea..4c776fdaa1 100644 --- a/content/zh/docs/eino/core_modules/components/embedding_guide.md +++ b/content/zh/docs/eino/core_modules/components/embedding_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Embedding 使用说明' @@ -60,7 +60,7 @@ WithModel(model string) Option ```go // 初始化 embedder (以 openai 为例) -embedder, err := openai.NewEmbedder(ctx, &openai.EmbedderConfig{ +embedder, err := openai.NewEmbedding(ctx, &openai.Config{ // 配置参数 }) if err != nil { 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 57649787b0..db71b142f0 100644 --- a/content/zh/docs/eino/core_modules/components/indexer_guide.md +++ b/content/zh/docs/eino/core_modules/components/indexer_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Indexer 使用说明' @@ -142,7 +142,7 @@ result, err := runnable.Invoke(ctx, docs, compose.WithCallbacks(helper)) ## **已有实现** -1. Volc VikingDB Indexer: 基于火山引擎 VikingDB 实现的向量数据库索引器 [Indexer - VikingDB](/zh/docs/eino/ecosystem_integration/indexer_volc_vikingdb) +1. Volc VikingDB Indexer: 基于火山引擎 VikingDB 实现的向量数据库索引器 [Indexer - VikingDB](/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb) ## **自行实现参考** 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 72113a58bb..abfb1bba0d 100644 --- a/content/zh/docs/eino/core_modules/components/retriever_guide.md +++ b/content/zh/docs/eino/core_modules/components/retriever_guide.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Retriever 使用说明' @@ -174,7 +174,7 @@ result, err := runnable.Invoke(ctx, "查询内容", compose.WithCallbacks(helper ## **已有实现** -- Volc VikingDB Retriever: 基于火山引擎 VikingDB 的检索实现 [Retriever - VikingDB](/zh/docs/eino/ecosystem_integration/retriever_volc_vikingdb) +- Volc VikingDB Retriever: 基于火山引擎 VikingDB 的检索实现 [Retriever - VikingDB](/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb) ## **自行实现参考** diff --git a/content/zh/docs/eino/core_modules/devops/_index.md b/content/zh/docs/eino/core_modules/devops/_index.md new file mode 100644 index 0000000000..3542f237fd --- /dev/null +++ b/content/zh/docs/eino/core_modules/devops/_index.md @@ -0,0 +1,10 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: 'EinoDev: 应用开发工具链' +weight: 4 +--- + +🚀 Eino 是 Go AI 集成组件的研发框架,提供了 AI 应用相关的常用组件以及集成组件编排能力,为了更好的辅助开发者使用 Eino,我们提供了 「GoLand Eino IDE 插件」 ,现在就安装插件 ( [EinoDev 插件安装指南](/zh/docs/eino/core_modules/devops/ide_plugin_guide)),助你高效开发 🚀 diff --git a/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md new file mode 100644 index 0000000000..7a694874d8 --- /dev/null +++ b/content/zh/docs/eino/core_modules/devops/ide_plugin_guide.md @@ -0,0 +1,64 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: Eino IDE 插件安装指南 +weight: 1 +--- + +## 背景 + +> [Eino: 概述](/zh/docs/eino/overview) +> +> **🚀 Eino 是 Go AI 集成组件的研发框架,提供了 AI 应用相关的常用组件以及集成组件编排能力,为了更好的辅助开发者使用 Eino,我们提供了 GoLand Eino IDE 插件,助你高效开发 🚀** + +## 简介 + +![](/img/eino/eino_dev_ability_introduction_page.png) + +## 安装插件 + + + + +
+1. 进入GoLand,点击设置,选择Plugin 插件 + + + +1. 通过Marketplace,搜索Eino Dev 插件并按照 + + +
+ +> 💡 +> **插件安装完毕可以在 IDE 右侧插件列表中 EinoDev 调试插件图标啦,接下来就可以体验插件提供的调试与编排能力啦** + +![](/img/eino/eino_dev_enter_page.png) + +## 功能简介 + +### **EinoDev Graph 编排** + + + + +
+ + + +
+ +### **EinoDev Graph 调试** + + + + +
+ + + +
+ +## diff --git a/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md new file mode 100644 index 0000000000..8cf8091753 --- /dev/null +++ b/content/zh/docs/eino/core_modules/devops/visual_debug_plugin_guide.md @@ -0,0 +1,272 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: EinoDev 可视化调试插件功能指南 +weight: 3 +--- + +# 简介 + +> 💡 +> 对使用 Eino 框架编写的编排产物(Graph,Chain)进行可视化调试,包括: +> +> 1. 编排产物可视化渲染; +> 2. 从可操作的任意节点开始,mock 输入进行调试。 + +# 快速开始 + +## 下载 eino-example + +> github 仓库:_[https://github.com/cloudwego/eino-examples](https://github.com/cloudwego/eino-examples)_ + +```bash +# HTTPS +git clone https://github.com/cloudwego/eino-examples.git + +# SSH +git clone git@github.com:cloudwego/eino-examples.git +``` + +## 安装依赖 + +在项目目录下依次执行以下指令 + +```bash +# 1. Pull latest devops repository +go get github.com/cloudwego/eino-ext/devops@latest + +# 2. Cleans and updates go.mod and go.sum +go mod tidy +``` + +## 运行 Demo + +进入 `eino-examples/devops/debug/main.go`,运行 `main.go`。因为插件会同时在本地启动一个 HTTP 服务用于连接用户服务进程,所以会弹出接入网络警告,点击允许。 +![](/img/eino/eino_debug_enter_config_page.png) + +## 配置调试地址 + + + + +
+ +1.点击左侧或正中间调试功能进入调试配置 + + + + +2.点击配置调试地址 + + +
+ + + + +
+ +3.填入 127.0.0.1:52538 + + + + +4.点击确认进入调试界面,选择要调试的 Graph + + +
+ +## 开始调试 + + + + +
+ +1.点击
Test Run
从 start 节点开始执行 + + +
+ +2.输入
"hello eino"
,点击确认 + + +
+ + + + +
+ +3.在调试区域展示有各个节点的输入和输出 + + + + +4.点击 Input 和 Output 切换查看节点信息 + + +
+ +# 功能一览 + +## 本地或远程调试 + +目标调试编排产物无论是在本地电脑还是在远程服务器,都可以通过配置 IP:Port ,主动连接到目标调试对象所在的服务器。 +![](/img/eino/eino_debug_run_config_page.png) + +## 编排拓扑可视化 + +支持 Graph 和 Chain 编排拓扑可视化。 +![](/img/eino/eino_debug_list_nodes_page.png) + +## 从任意节点开始调试 + +![](/img/eino/eino_debug_test_run_of_one_node_page.png) + +## 查看节点执行结果 + +每个节点执行结果都会按执行顺序展示在调试区域,包括:输入、输出、执行耗时 +![](/img/eino/eino_debug_run_detail_v2_page.png) + +# 从零开始调试 + +## 使用 Eino 进行编排 + +插件支持对 Graph 和 Chain 的编排产物进行调试,假设你已经有编排代码如下 + +```go +func RegisterSimpleGraph(ctx context.Context) { + g := compose.NewGraph[string, string]() + _ = g.AddLambdaNode("node_1", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) { + return input + " process by node_1,", nil + })) + _ = g.AddLambdaNode("node_2", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) { + return input + " process by node_2,", nil + })) + _ = g.AddLambdaNode("node_3", compose.InvokableLambda(func(ctx context.Context, input string) (output string, err error) { + return input + " process by node_3,", nil + })) + + _ = g.AddEdge(compose.START, "node_1") + _ = g.AddEdge("node_1", "node_2") + _ = g.AddEdge("node_2", "node_3") + _ = g.AddEdge("node_3", compose.END) + + _, err := g.Compile(ctx) + if err != nil { + logs.Errorf("compile graph failed, err=%v", err) + return + } +} +``` + +## 安装依赖 + +在项目目录下依次执行以下指令 + +```bash +# 1. Pull latest devops repository +go get github.com/cloudwego/eino-ext/devops@latest + +# 2. Cleans and updates go.mod and go.sum +go mod tidy +``` + +## 调用调试初始化函数 + +因为调试需要在用户主进程中启动一个 HTTP 服务,以用作与本地调试插件交互,所以用户需要主动调用一次 _github.com/cloudwego/eino-ext/devops_ 中的 `Init()` 来启动调试服务。 + +> 💡 +> 注意事项 +> +> 1. 确保目标调试的编排产物至少执行过一次 `Compile()`。 +> 2. `devops.Init()` 的执行必须要在 Graph/Chain 调用 `Compile()` 之前。 +> 3. 用户需要保证 `devops.Init()` 执行后主进程不能退出。 + +如在 `main()` 函数中增加调试服务启动代码 + +```go +// 1.调用调试服务初始化函数 +err := devops.Init(ctx) +if err != nil { + logs.Errorf("[eino dev] init failed, err=%v", err) + return +} + +// 2.编译目标调试的编排产物 +RegisterSimpleGraph(ctx) +``` + +## 运行用户进程 + +在本地电脑或者远程环境中运行你的进程,并保证主进程不会退出。 + +在 _github.com/cloudwego/eino-examples/devops/debug/main.go__ _中,`main()` 代码如下 + +```go +func main() { + ctx := context.Background() + // Init eino devops server + err := devops.Init(ctx) + if err != nil { + logs.Errorf("[eino dev] init failed, err=%v", err) + return + } + + // Register chain, graph and state_graph for demo use + chain.RegisterSimpleChain(ctx) + graph.RegisterSimpleGraph(ctx) + graph.RegisterSimpleStateGraph(ctx) + + // Blocking process exits + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + <-sigs + + // Exit + logs.Infof("[eino dev] shutting down\n") +} +``` + +## 配置调试地址 + +- **IP**:用户进程所在服务器的 IP 地址。 + - 用户进程运行在本地电脑,则填写 `127.0.0.1`; + - 用户进程运行在远程服务器上,则填写远程服务器的 IP 地址,兼容 IPv4 和 IPv6 。 +- **Port**:调试服务监听的端口,默认是 `52538`,可通过 WithDevServerPort 这一 option 方法进行修改 + +> 💡 +> 注意事项 +> +> - 本地电脑调试:系统可能会弹出网络接入警告,允许接入即可。 +> - 远程服务器调试:需要你保证端口可访问。 + +IP 和 Port 配置完成后,点击确认,调试插件会自动连接到目标调试服务器。如果成功连接,连接状态指示器会变成绿色。 +![](/img/eino/eino_debug_ip_port_show_page.png) + +## 选择目标调试编排产物 + +确保你目标调试的编排产物至少执行过一次 `Compile()`。因为调试设计是面向编排产物实例,所以如果多次执行 `Compile()`,会在调试服务中注册多个编排产物,继而在选择列表中看到多个可调试目标。 +![](/img/eino/eino_debug_list_graph_and_show_page.png) + +## 开始调试 + +调试支持从任意节点开始调试,包括 start 节点和其他中间节点。 + +1. 从 START 节点开始调试:直接点击 Test Run,然后输入 mock 的 input(如果 input 是复杂结构的话,会自动对 input 的结构进行推断)然后点击确定,开始执行你的 graph,每个 node 的结果会在下方显示。 + ![](/img/eino/eino_debug_enter_test_run_page.png) + ![](/img/eino/eino_debug_run_input_mock_data_2_page.png) +2. 从任意的可操作节点开始调试:比如,从第二个 node 开始执行。 + ![](/img/eino/eino_debug_test_run_from_node_page.png) + ![](/img/eino/eino_debug_run_of_mock_input_of_page.png) + +## 查看执行结果 + +从 START 节点开始调试,点击 Test Run 后,在插件下方查看调试结果。 +![](/img/eino/eino_debug_test_run_result_page.png) + +从任意的可操作节点进行调试,在插件下方查看调试结果。 +![](/img/eino/eino_debug_run_detail_page.png) diff --git a/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md b/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md new file mode 100644 index 0000000000..4d55a0c769 --- /dev/null +++ b/content/zh/docs/eino/core_modules/devops/visual_orchestration_plugin_guide.md @@ -0,0 +1,132 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: EinoDev 可视化编排插件功能指南 +weight: 2 +--- + +# 简介 + +> 💡 +> Goland 提供的 Eino 可视化编排插件, 在 GoLand 中可以通过组件拖拽实现 Graph 的编排生成代码,并支持导入导出 + +## 初认插件 + +### 插件功能介绍 + +![](/img/eino/eino_orchestration_describtion_page.png) + +## 编排组件介绍 + +## 图 ( Graph ) + +- 与 Eino 中的 Graph 概念一致,指最终由插件侧生成的 Graph,可在以下界面添加 Graph。 +- 点击添加插件,则弹出创建对话框,根据字段说明补充配置信息,即可生成一个 Graph 编排对象。 + + + + + +
+ + + + + +
+ +### 节点 ( Node ) + +- 与 Eino 中的 Node 一致,创建 Graph 完成后,通过界面右上角 AddNodes ,添加不同类型 Node 到画布。 +- 添加到 Graph 中 Node 插件会默认填写 NodeKey ,此外可展开 More config 为 Node 配置可选配置。 + + + + + +
+ + + + + +
+ +### 组件 ( Component ) + +- Component 是组成 Node 的必要信息,不同的 Component 对应不同的 Node 类型,并且提供了内置的官方 Official Components 与 Custom Components 。 +- 完成添加 Node 操作后,可按需配置组件的 Runtime Config 信息。 + + + + +
+ + + +
+ +### 插槽 ( Slot ) + +- 不同类型的 Component 的生成会依赖其他组件,将其作为自身配置依赖的一部分,这部分依赖被称作插槽( Slot )。 +- 比如官方提供的 volc_vikingDB 组件,其依赖了 Embeding Component 作为插槽;再比如官方提供的 ToolsNode 组件,其依赖了多个 Tool Component。 + + + + +
+ + + +
+ +## 开始编排 + +### 初始化插件 + +点击进入 Eino Dev 插件,会展示如下界面,可点击图中圈选框进入编排。 +![](/img/eino/eino_orchestration_enter_page.png) + +### 创建并编排 Graph + +- 界面左下角新增 Graph,在弹窗对话框填写 Graph 相关配置,生成 Graph 画布。 +- 按需从 AddNodes 选择合适的 Node 组件,添加的画布。 +- 依据业务编排逻辑将 Node 组件连接,完成 Graph 业务编排逻辑。 + + + + + +
+ + + + + +
+ +- 点击 “Generate as code” 并填写指定路径,将编排的 Graph 生成代码并保存到指定路径。 + + + + +
+ + + +
+ +- 特别的当添加的 Component 为 Graph 类型时,添加的 嵌套 Graph 可展开做 Node 组件的配置,配置完成后,通过顶层面包屑路径跳回首页界面。 + + + + +
+ + + +
+ +## diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/_index.md b/content/zh/docs/eino/core_modules/flow_integration_components/_index.md index a1793a3535..d93d7bcf79 100644 --- a/content/zh/docs/eino/core_modules/flow_integration_components/_index.md +++ b/content/zh/docs/eino/core_modules/flow_integration_components/_index.md @@ -1,19 +1,15 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: Flow 集成组件' weight: 3 --- -大模型应用是存在**通用场景和模式**的,若把这些场景进行抽象,就能提供一些可以帮助开发者快速构建大模型应用的工具。Eino 的 Flow 模块就是在做这件事。 +大模型应用是存在**通用场景和模式**的,若把这些场景进行抽象,就能提供一些可以帮助开发者快速构建大模型应用的模版。Eino 的 Flow 模块就是在做这件事。 -目前 Eino 已经集成了 `react agent`、`host multi agent` 两个常用的模式。 +目前 Eino 已经集成了 `react agent`、`host multi agent` 两个常用的 Agent 模式,以及 MultiQueryRetriever, ParentIndexer 等。 - React Agent: [Eino: React Agent 使用手册](/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual) - Multi Agent: [Eino Tutorial: Host Multi-Agent ](/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting) - -> 另外一些常用的模式例如 `retriever agent`、`chat agent`、`summary agent`、`DB agent` 等等,如果你有这类需求,希望 Eino 提供对他们的封装,请联系并和我们交流 - -# 子目录 diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md b/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md index 5cb916a094..b3ce12d1f0 100644 --- a/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md +++ b/content/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-17" lastmod: "" tags: [] title: 'Eino Tutorial: Host Multi-Agent ' @@ -11,9 +11,11 @@ Host Multi-Agent 是一个 Host 做意图识别后,跳转到某个专家 agent 以一个简单的“日记助手”做例子:可以写日记、读日记、根据日记回答问题。 +完整样例参见:[https://github.com/cloudwego/eino-examples/tree/main/flow/agent/multiagent/host/journal](https://github.com/cloudwego/eino-examples/tree/main/flow/agent/multiagent/host/journal) + Host: -```Go +```go func newHost(ctx context.Context, baseURL, apiKey, modelName string) (*host.Host, error) { chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ BaseURL: baseURL, diff --git a/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md b/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md index 232016d0c0..6980a67331 100644 --- a/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md +++ b/content/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-16" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: React Agent 使用手册' @@ -14,15 +14,19 @@ Eino React Agent 是实现了 [React 逻辑](https://react-lm.github.io/) 的智 > 💡 > 代码实现详见:[实现代码目录](https://github.com/cloudwego/eino/tree/main/flow/agent/react) +Example 代码路径:[https://github.com/cloudwego/eino-examples/blob/main/flow/agent/react/react.go](https://github.com/cloudwego/eino-examples/blob/main/flow/agent/react/react.go) + ## 节点拓扑&数据流图 -react agent 底层使用 `compose.StateGraph` 作为编排方案,仅有 2 个节点: ChatModel、Tools,中间运行过程中的所有历史消息都会放入 state 中,在将所有历史消息传递给 ChatModel 之前,会 copy 消息交由 MessageModifier 进行处理,处理的结果再传递给 ChatModel。直到 ChatModel 返回的消息中不再有 tool call,则返回最终消息。 +react agent 底层使用 `compose.Graph` 作为编排方案,一般来说有 2 个节点: ChatModel、Tools,中间运行过程中的所有历史消息都会放入 state 中,在将所有历史消息传递给 ChatModel 之前,会 copy 消息交由 MessageModifier 进行处理,处理的结果再传递给 ChatModel。直到 ChatModel 返回的消息中不再有 tool call,则返回最终消息。 ![](/img/eino/react_agent_graph.png) +当 Tools 列表中至少有一个 Tool 配置了 ReturnDirectly 时,ReAct Agent 结构会更复杂:在 ToolsNode 之后会增加一个 Branch,判断是否调用了一个 ReturnDirectly 的 Tool,如果是,直接 END,否则照旧进入 ChatModel。 + ## 初始化 -提供了 ReactAgent 初始化函数,必填参数为 Model 和 ToolsConfig,选填参数为 MessageModifier 和 MaxStep. +提供了 ReactAgent 初始化函数,必填参数为 Model 和 ToolsConfig,选填参数为 MessageModifier, MaxStep, ToolReturnDirectly 和 StreamToolCallChecker. ```bash go get github.com/cloudwego/eino-ext/components/model/openai@latest @@ -73,7 +77,7 @@ type ChatModel interface { } ``` -目前,eino 提供了 openai 和 ark 的实现,只要底层模型支持 tool call 即可。 +目前,eino 提供了 openai, ark 等实现,只要底层模型支持 tool call 即可。 ```bash go get github.com/cloudwego/eino-ext/components/model/openai@latest diff --git a/content/zh/docs/eino/ecosystem_integration/_index.md b/content/zh/docs/eino/ecosystem_integration/_index.md index 084f26711e..9204b2f675 100644 --- a/content/zh/docs/eino/ecosystem_integration/_index.md +++ b/content/zh/docs/eino/ecosystem_integration/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-06" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: 生态集成' @@ -11,7 +11,7 @@ weight: 0 ### ChatModel -- openai: [ChatModel OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) +- 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) @@ -19,35 +19,35 @@ weight: 0 #### Loader -- file: [[🚧]Loader - local file](/zh/docs/eino/ecosystem_integration/document/loader_local_file) -- s3: [[🚧]Loader - amazon s3](/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3) -- web url: [[🚧]Loader - web url](/zh/docs/eino/ecosystem_integration/document/loader_web_url) +- file: [Loader - local file](/zh/docs/eino/ecosystem_integration/document/loader_local_file) +- s3: [Loader - amazon s3](/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3) +- web url: [Loader - web url](/zh/docs/eino/ecosystem_integration/document/loader_web_url) #### Parser -- html: [[🚧]Parser - html](/zh/docs/eino/ecosystem_integration/document/parser_html) -- pdf: [[🚧]Parser - pdf](/zh/docs/eino/ecosystem_integration/document/parser_pdf) +- html: [Parser - html](/zh/docs/eino/ecosystem_integration/document/parser_html) +- pdf: [Parser - pdf](/zh/docs/eino/ecosystem_integration/document/parser_pdf) #### Transformer -- markdown splitter: [[🚧]Splitter - markdown](/zh/docs/eino/ecosystem_integration/document/splitter_markdown) -- recursive splitter: [[🚧]Splitter - recursive](/zh/docs/eino/ecosystem_integration/document/splitter_recursive) -- semantic splitter: [[🚧]Splitter - semantic](/zh/docs/eino/ecosystem_integration/document/splitter_semantic) +- markdown splitter: [Splitter - markdown](/zh/docs/eino/ecosystem_integration/document/splitter_markdown) +- recursive splitter: [Splitter - recursive](/zh/docs/eino/ecosystem_integration/document/splitter_recursive) +- semantic splitter: [Splitter - semantic](/zh/docs/eino/ecosystem_integration/document/splitter_semantic) ### Embedding -- ark: [[🚧]Embedding - ARK](/zh/docs/eino/ecosystem_integration/embedding/embedding_ark) -- openai: [[🚧]Embedding - OpenAI](/zh/docs/eino/ecosystem_integration/embedding/embedding_openai) +- ark: [Embedding - ARK](/zh/docs/eino/ecosystem_integration/embedding/embedding_ark) +- openai: [Embedding - OpenAI](/zh/docs/eino/ecosystem_integration/embedding/embedding_openai) ### Indexer -- volc vikingdb: [[🚧]Indexer - volc VikingDB](/zh/docs/eino/ecosystem_integration/indexer_volc_vikingdb) +- volc vikingdb: [Indexer - volc VikingDB](/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb) ### Retriever -- volc vikingdb: [[🚧]Retriever - volc VikingDB](/zh/docs/eino/ecosystem_integration/retriever_volc_vikingdb) +- volc vikingdb: [Retriever - volc VikingDB](/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb) ### Tools -- googlesearch: [[🚧]Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch) -- duckduckgo search: [[🚧]Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search) +- googlesearch: [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch) +- duckduckgo search: [Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search) diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md b/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md new file mode 100644 index 0000000000..7347689761 --- /dev/null +++ b/content/zh/docs/eino/ecosystem_integration/callbacks/_index.md @@ -0,0 +1,10 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: Callbacks +weight: 0 +--- + + diff --git a/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md new file mode 100644 index 0000000000..4544af248b --- /dev/null +++ b/content/zh/docs/eino/ecosystem_integration/callbacks/callback_langfuse.md @@ -0,0 +1,39 @@ +--- +Description: "" +date: "2025-01-20" +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**.InitCallbackHandlers([]**callbacks**._Handler_{cbh}) // 设置langfuse为全局callback + + g := NewGraph[string,string]() + /* + * compose and run graph + */ + + flusher() // 等待所有trace上报完成后退出 +} +``` + +可以在 Langfuse project 中查看 trace: +![](/img/eino/eino_callback_langfuse_usage.gif) diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md index 7ed67e3028..1c7a689cb0 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: ChatModel - Ollama @@ -18,7 +18,9 @@ Ollama 模型是 ChatModel 接口的一个实现,用于与 Ollama 本地大语 Ollama 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下: ```go -model, err := NewChatModel(ctx, &ChatModelConfig{ +import "github.com/cloudwego/eino-ext/components/model/ollama" + +model, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{ // 基础配置 BaseURL: "http://localhost:11434", // Ollama 服务地址 Timeout: 30 * time.Second, // 请求超时时间 diff --git a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md index beac147621..e68b67a50b 100644 --- a/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md +++ b/content/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: ChatModel - OpenAI @@ -22,6 +22,8 @@ OpenAI 模型是 ChatModel 接口的一个实现,用于与 OpenAI 的 GPT 系 OpenAI 模型通过 `NewChatModel` 函数进行初始化,主要配置参数如下: ```go +import "github.com/cloudwego/eino-ext/components/model/openai" + func main() { model, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ // Azure OpenAI Service 配置(可选) 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 index 5ac99e004b..7817230955 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3.md +++ b/content/zh/docs/eino/ecosystem_integration/document/loader_amazon_s3.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Loader - amazon s3 @@ -22,13 +22,19 @@ Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,提供 S3 文档加载器通过 `NewS3Loader` 函数进行初始化,主要配置参数如下: ```go -loader, err := NewS3Loader(ctx, &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 -}) +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 + }) +} ``` 配置参数说明: @@ -71,15 +77,14 @@ import ( "context" "github.com/aws/aws-sdk-go-v2/aws" - s3loader "github.com/cloudwego/eino-ext/components/document/loader/s3" + "github.com/cloudwego/eino-ext/components/document/loader/s3" "github.com/cloudwego/eino/components/document" ) func main() { ctx := context.Background() - - // 初始化加��器 - loader, err := s3loader.NewS3Loader(ctx, &s3loader.LoaderConfig{ + + 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"), diff --git a/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md b/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md index a01cd45863..fcdd008412 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md +++ b/content/zh/docs/eino/ecosystem_integration/document/loader_local_file.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Loader - local file @@ -27,10 +27,16 @@ local file 文件加载器是 Document Loader 接口的一个实现,用于从 本地文件加载器通过 `NewFileLoader` 函数进行初始化,主要配置参数如下: ```go -loader, err := NewFileLoader(ctx, &FileLoaderConfig{ - UseNameAsID: true, // 是否使用文件名作为文档ID - Parser: &parser.TextParser{}, // 可选:指定自定义解析器 -}) +import ( + "github.com/cloudwego/eino/components/document/loader/file" +) + +func main() { + loader, err := file.NewFileLoader(ctx, &FileLoaderConfig{ + UseNameAsID: true, // 是否使用文件名作为文档ID + Parser: &parser.TextParser{}, // 可选:指定自定义解析器 + }) +} ``` 配置参数说明: @@ -70,7 +76,7 @@ package main import ( "context" - fileloader "github.com/cloudwego/eino-ext/components/document/loader/file" + file "github.com/cloudwego/eino-ext/components/document/loader/file" "github.com/cloudwego/eino/components/document" ) @@ -78,7 +84,7 @@ func main() { ctx := context.Background() // 初始化加载器 - loader, err := fileloader.NewFileLoader(ctx, &fileloader.FileLoaderConfig{ + loader, err := file.NewFileLoader(ctx, &file.FileLoaderConfig{ UseNameAsID: true, }) if err != nil { @@ -97,9 +103,9 @@ func main() { for _, doc := range docs { println(doc.Content) // 访问元数据 - fileName := doc.MetaData[fileloader.MetaKeyFileName] - extension := doc.MetaData[fileloader.MetaKeyExtension] - source := doc.MetaData[fileloader.MetaKeySource] + fileName := doc.MetaData[file.MetaKeyFileName] + extension := doc.MetaData[file.MetaKeyExtension] + source := doc.MetaData[file.MetaKeySource] } } ``` 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 index 648160f8ca..00cd396511 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/loader_web_url.md +++ b/content/zh/docs/eino/ecosystem_integration/document/loader_web_url.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Loader - web url @@ -26,11 +26,17 @@ URL 文档加载器具有以下特点: URL 文档加载器通过 `NewLoader` 函数进行初始化,主要配置参数如下: ```go -loader, err := NewLoader(ctx, &LoaderConfig{ - Parser: parser, // 可选:自定义解析器,默认使用 HTML 解析器 - Client: httpClient, // 可选:自定义 HTTP 客户端 - RequestBuilder: requestBuilder, // 可选:自定义请求构建器 -}) +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, + }) +} ``` 配置参数说明: @@ -65,7 +71,7 @@ package main import ( "context" - urlloader "github.com/cloudwego/eino-ext/components/document/loader/url" + "github.com/cloudwego/eino-ext/components/document/loader/url" "github.com/cloudwego/eino/components/document" ) @@ -73,7 +79,7 @@ func main() { ctx := context.Background() // 使用默认配置初始化加载器 - loader, err := urlloader.NewLoader(ctx, nil) + loader, err := url.NewLoader(ctx, nil) if err != nil { panic(err) } @@ -103,7 +109,7 @@ import ( "net/http" "time" - urlloader "github.com/cloudwego/eino-ext/components/document/loader/url" + "github.com/cloudwego/eino-ext/components/document/loader/url" "github.com/cloudwego/eino/components/document" ) @@ -127,7 +133,7 @@ func main() { } // 初始化加载器 - loader, err := urlloader.NewLoader(ctx, &urlloader.LoaderConfig{ + loader, err := url.NewLoader(ctx, &url.LoaderConfig{ Client: client, RequestBuilder: requestBuilder, }) diff --git a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md b/content/zh/docs/eino/ecosystem_integration/document/parser_html.md index d2a875e707..5197b8e95d 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/parser_html.md +++ b/content/zh/docs/eino/ecosystem_integration/document/parser_html.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Parser - html @@ -29,7 +29,11 @@ HTML 解析器具有以下特点: HTML 解析器通过 `NewParser` 函数进行初始化,主要配置参数如下: ```go -parser, err := NewParser(ctx, &Config{ +import ( + "github.com/cloudwego/eino-ext/components/document/parser/html" +) + +parser, err := html.NewParser(ctx, &html.Config{ Selector: &selector, // 可选:内容选择器,默认为 body }) ``` @@ -61,7 +65,7 @@ import ( "context" "strings" - htmlparser "github.com/cloudwego/eino-ext/components/document/parser/html" + "github.com/cloudwego/eino-ext/components/document/parser/html" "github.com/cloudwego/eino/components/document/parser" ) @@ -69,7 +73,7 @@ func main() { ctx := context.Background() // 初始化解析器 - p, err := htmlparser.NewParser(ctx, nil) // 使用默认配置 + p, err := html.NewParser(ctx, nil) // 使用默认配置 if err != nil { panic(err) } @@ -105,9 +109,9 @@ func main() { // 使用解析结果 doc := docs[0] println("内容:", doc.Content) - println("标题:", doc.MetaData[htmlparser.MetaKeyTitle]) - println("描述:", doc.MetaData[htmlparser.MetaKeyDesc]) - println("语言:", doc.MetaData[htmlparser.MetaKeyLang]) + println("标题:", doc.MetaData[html.MetaKeyTitle]) + println("描述:", doc.MetaData[html.MetaKeyDesc]) + println("语言:", doc.MetaData[html.MetaKeyLang]) } ``` @@ -119,7 +123,7 @@ package main import ( "context" - htmlparser "github.com/cloudwego/eino-ext/components/document/parser/html" + "github.com/cloudwego/eino-ext/components/document/parser/html" ) func main() { @@ -127,7 +131,7 @@ func main() { // 指定只提取 id 为 content 的元素内容 selector := "#content" - p, err := htmlparser.NewParser(ctx, &htmlparser.Config{ + p, err := html.NewParser(ctx, &html.Config{ Selector: &selector, }) if err != nil { diff --git a/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md b/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md index c2d85ba051..0ba183b0f0 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md +++ b/content/zh/docs/eino/ecosystem_integration/document/parser_pdf.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Parser - pdf @@ -36,9 +36,15 @@ PDF 解析器具有以下特点: PDF 解析器通过 `NewPDFParser` 函数进行初始化,主要配置参数如下: ```go -parser, err := NewPDFParser(ctx, &Config{ - ToPages: true, // 是否按页面分割文档 -}) +import ( + "github.com/cloudwego/eino-ext/components/document/parser/pdf" +) + +func main() { + parser, err := pdf.NewPDFParser(ctx, &pdf.Config{ + ToPages: true, // 是否按页面分割文档 + }) +} ``` 配置参数说明: @@ -69,7 +75,7 @@ import ( "context" "os" - pdfparser "github.com/cloudwego/eino-ext/components/document/parser/pdf" + "github.com/cloudwego/eino-ext/components/document/parser/pdf" "github.com/cloudwego/eino/components/document/parser" ) @@ -77,7 +83,7 @@ func main() { ctx := context.Background() // 初始化解析器 - p, err := pdfparser.NewPDFParser(ctx, &pdfparser.Config{ + p, err := pdf.NewPDFParser(ctx, &pdf.Config{ ToPages: false, // 不按页面分割 }) if err != nil { @@ -95,7 +101,7 @@ func main() { docs, err := p.Parse(ctx, file, parser.WithURI("document.pdf"), parser.WithExtraMeta(map[string]any{ - "source": "local", + "source": "./document.pdf", }), ) if err != nil { diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md index 8a116824d5..776b19f0bd 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md +++ b/content/zh/docs/eino/ecosystem_integration/document/splitter_recursive.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Splitter - recursive @@ -134,8 +134,6 @@ splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ "\n\n", // 空行(段落分隔) "\n", // 换行 "。", // 句号 - ";", // 分号 - ",", // 逗号 }, }) ``` diff --git a/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md b/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md index 85b2d1706f..e0c059f10b 100644 --- a/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md +++ b/content/zh/docs/eino/ecosystem_integration/document/splitter_semantic.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Splitter - semantic @@ -18,7 +18,7 @@ weight: 0 1. 首先使用基本分隔符(如换行符、句号等)将文档分割成始片段 2. 使用向量嵌入模型为每个片段生成语义向量 3. 计算相邻片段之间的余弦相似度 -4. 根据相似度阈值(由百分位数控制)决定是否在两个片段之间进行分割 +4. 根据相似度阈值决定是否在两个片段之间进行分割 5. 对小于最小大小的片段进行合并 ## **使用方式** diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md index 3b6e456e32..22396aaa8d 100644 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md +++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_ark.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Embedding - ARK @@ -18,7 +18,9 @@ Ark Embedding 是 Eino Embedding 接口的一个实现,用于将文本转换 Ark 向量嵌入器通过 `NewEmbedder` 函数进行初始化,主要配置参数如下: ```go -embedder, err := NewEmbedder(ctx, &EmbeddingConfig{ +import "github.com/cloudwego/eino-ext/components/embedding/ark" + +embedder, err := ark.NewEmbedder(ctx, &ark.EmbeddingConfig{ // 认证配置(二选一) APIKey: "your-api-key", // 使用 API Key 认证 // 或使用 AK/SK 认证 diff --git a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md index 7b5d37db57..e8883842f1 100644 --- a/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md +++ b/content/zh/docs/eino/ecosystem_integration/embedding/embedding_openai.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Embedding - OpenAI @@ -22,7 +22,9 @@ OpenAI 向量嵌入器是 Eino Embedding 接口的一个实现,用于将文本 OpenAI 向量嵌入器通过 `NewEmbedder` 函数进行初始化,主要配置参数如下: ```go -embedder, err := NewEmbedder(ctx, &EmbeddingConfig{ +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", @@ -32,8 +34,7 @@ embedder, err := NewEmbedder(ctx, &EmbeddingConfig{ ByAzure: true, BaseURL: "https://your-resource.openai.azure.com", APIVersion: "2023-05-15", - - // 可选:高级配置 + EncodingFormat: &format, // 编码格式 Dimensions: &dimension, // 向量维度 User: &user, // 用户标识 diff --git a/content/zh/docs/eino/ecosystem_integration/indexer/_index.md b/content/zh/docs/eino/ecosystem_integration/indexer/_index.md new file mode 100644 index 0000000000..b37e9ddda2 --- /dev/null +++ b/content/zh/docs/eino/ecosystem_integration/indexer/_index.md @@ -0,0 +1,10 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: Indexer +weight: 0 +--- + +Indexer 为把文本进行索引存储,一般使用 [Embedding](/zh/docs/eino/ecosystem_integration/embedding) 做语义化索引,也可做分词索引等,以便于 [Retriever](/zh/docs/eino/ecosystem_integration/retriever) 中召回使用 diff --git a/content/zh/docs/eino/ecosystem_integration/indexer_volc_vikingdb.md b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md similarity index 92% rename from content/zh/docs/eino/ecosystem_integration/indexer_volc_vikingdb.md rename to content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md index 0c89a0351d..e8567464b7 100644 --- a/content/zh/docs/eino/ecosystem_integration/indexer_volc_vikingdb.md +++ b/content/zh/docs/eino/ecosystem_integration/indexer/indexer_volc_vikingdb.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Indexer - volc VikingDB @@ -25,8 +25,10 @@ weight: 0 火山引擎 VikingDB 索引器通过 `NewIndexer` 函数进行初始化,主要配置参数如下: ```go -indexer, err := NewIndexer(ctx, &IndexerConfig{ - Host: "api.volcengineapi.com", // 服务地址 +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 @@ -35,7 +37,7 @@ indexer, err := NewIndexer(ctx, &IndexerConfig{ Collection: "your-collection", // 集合名称 - EmbeddingConfig: EmbeddingConfig{ + EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ UseBuiltin: true, // 是否使用内置向量化 ModelName: "text2vec-base", // 模型名称 UseSparse: true, // 是否使用稀疏向量 @@ -65,7 +67,7 @@ func main() { // 初始化索引器 idx, err := volcvikingdb.NewIndexer(ctx, &volcvikingdb.IndexerConfig{ - Host: "api.volcengineapi.com", + Host: "api-vikingdb.volces.com", Region: "cn-beijing", AK: "your-ak", SK: "your-sk", @@ -130,7 +132,7 @@ func main() { // 初始化索引器 idx, err := volcvikingdb.NewIndexer(ctx, &volcvikingdb.IndexerConfig{ - Host: "api.volcengineapi.com", + Host: "api-vikingdb.volces.com", Region: "cn-beijing", AK: "your-ak", SK: "your-sk", diff --git a/content/zh/docs/eino/ecosystem_integration/retriever/_index.md b/content/zh/docs/eino/ecosystem_integration/retriever/_index.md new file mode 100644 index 0000000000..cd054e9756 --- /dev/null +++ b/content/zh/docs/eino/ecosystem_integration/retriever/_index.md @@ -0,0 +1,10 @@ +--- +Description: "" +date: "2025-01-20" +lastmod: "" +tags: [] +title: Retriever +weight: 0 +--- + +Retriever 用于把 [Indexer](/zh/docs/eino/ecosystem_integration/indexer) 构建索引之后的内容进行召回,在 AI 应用中,一般使用 [Embedding](/zh/docs/eino/ecosystem_integration/embedding) 进行语义相似性召回。 diff --git a/content/zh/docs/eino/ecosystem_integration/retriever_volc_vikingdb.md b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md similarity index 91% rename from content/zh/docs/eino/ecosystem_integration/retriever_volc_vikingdb.md rename to content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md index d46b7010b2..b6d3f719dc 100644 --- a/content/zh/docs/eino/ecosystem_integration/retriever_volc_vikingdb.md +++ b/content/zh/docs/eino/ecosystem_integration/retriever/retriever_volc_vikingdb.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Retriever - volc VikingDB @@ -18,9 +18,11 @@ weight: 0 火山引擎 VikingDB 检索器通过 `NewRetriever` 函数进行初始化,主要配置参数如下: ```go -retriever, err := NewRetriever(ctx, &RetrieverConfig{ +import "github.com/cloudwego/eino-ext/components/retriever/volc_vikingdb" + +retriever, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ // 服务配置 - Host: "api.volcengineapi.com", // 服务地址 + Host: "api-vikingdb.volces.com", // 服务地址 Region: "cn-beijing", // 区域 AK: "your-ak", // 访问密钥 ID SK: "your-sk", // 访问密钥密码 @@ -32,7 +34,7 @@ retriever, err := NewRetriever(ctx, &RetrieverConfig{ Index: "index-name", // 索引名称 // 向量化配置 - EmbeddingConfig: EmbeddingConfig{ + EmbeddingConfig: volc_vikingdb.EmbeddingConfig{ UseBuiltin: true, // 是否使用内置向量化 ModelName: "model-name",// 模型名称 UseSparse: true, // 是否使用稀疏向量 @@ -80,7 +82,7 @@ func main() { // 初始化检索器 r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api.volcengineapi.com", + Host: "api-vikingdb.volces.com", Region: "cn-beijing", AK: "your-ak", SK: "your-sk", @@ -136,7 +138,7 @@ func main() { // 初始化检索器 r, err := volc_vikingdb.NewRetriever(ctx, &volc_vikingdb.RetrieverConfig{ - Host: "api.volcengineapi.com", + Host: "api-vikingdb.volces.com", Region: "cn-beijing", AK: "your-ak", SK: "your-sk", diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md index baa7cde1a6..9550d910e2 100644 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md +++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-06" +date: "2025-01-20" lastmod: "" tags: [] title: Tool - DuckDuckGoSearch @@ -18,7 +18,9 @@ DuckDuckGo 搜索工具是 Tool InvokableTool 接口的一个实现,用于通 DuckDuckGo 搜索工具通过 `NewTool` 函数进行初始化,主要配置参数如下: ```go -tool, err := NewTool(ctx, &Config{ +import "github.com/cloudwego/eino-ext/components/tool/duckduckgo" + +tool, err := duckduckgo.NewTool(ctx, &duckduckgo.Config{ ToolName: "duckduckgo_search", // 工具名称 ToolDesc: "search web for information by duckduckgo", // 工具描述 Region: ddgsearch.RegionWT, // 搜索地区 diff --git a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md b/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md index 3954fc6fa4..0ab0ea98d3 100644 --- a/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md +++ b/content/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Tool - Googlesearch @@ -18,7 +18,9 @@ Google 搜索工具是 Eino InvokableTool 接口的一个实现,用于通过 G Google 搜索工具通过 `NewGoogleSearchTool` 函数进行初始化,主要配置参数如下: ```go -tool, err := NewGoogleSearchTool(ctx, &Config{ +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 @@ -58,7 +60,7 @@ func main() { ctx := context.Background() // 初始化搜索工具 - searchTool, err := googlesearch.NewGoogleSearchTool(ctx, &googlesearch.Config{ + searchTool, err := googlesearch.NewTool(ctx, &googlesearch.Config{ APIKey: "your-api-key", SearchEngineID: "your-engine-id", Lang: "zh-CN", diff --git a/content/zh/docs/eino/overview/_index.md b/content/zh/docs/eino/overview/_index.md index 07e03aa430..bba9fff4b4 100644 --- a/content/zh/docs/eino/overview/_index.md +++ b/content/zh/docs/eino/overview/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-16" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: 概述' @@ -24,8 +24,6 @@ Eino 可在 AI 应用开发周期中的不同阶段,规范、简化和提效 - Deployment: 提供丰富的对 AI 应用的评测能力 - Maintenance: 提供丰富的切面对 AI 应用进行观测、监控 -![](/img/eino/eino_project_structure_and_modules.png) - 完整 API Reference:[https://pkg.go.dev/github.com/cloudwego/eino](https://pkg.go.dev/github.com/cloudwego/eino) ## 快速上手 @@ -83,6 +81,8 @@ runnable.Stream(ctx, []*Message{UserMessage("help me plan my weekend")}) 现在,我们来创建一个 Workflow,它能在字段级别灵活映射输入与输出: +![](/img/eino/graph_node_type1.png) + ```go wf := NewWorkflow[[]*Message, *Message]() wf.AddChatModelNode("model", model).AddInput(NewMapping(START)) @@ -439,10 +439,9 @@ func (g *graph) AddLambdaNode(key string, node *Lambda, opts ...GraphAddNodeOpt) return g.addNode(key, toLambdaNode(key, node, opts...)) } -// AddGraphNode add one kind of Graph[I, O]、Chain[I, O]、StateChain[I, O, S] as a node. +// AddGraphNode add one kind of Graph[I, O]、Chain[I, O] as a node. // for Graph[I, O], comes from NewGraph[I, O]() // for Chain[I, O], comes from NewChain[I, O]() -// for StateGraph[I, O, S], comes from NewStateGraph[I, O, S]() func (g *graph) AddGraphNode(key string, node AnyGraph, opts ...GraphAddNodeOpt) error { return g.addNode(key, toAnyGraphNode(key, node, opts...)) } @@ -498,7 +497,7 @@ func (g *graph) AddBranch(startNode string, branch *GraphBranch) (err error) {} ###### **Parallel** - 将多个 Node 平行并联, 形成多个节点并发执行的节点 -- 无 AddParallel 方法,通过 AddEdge 构建并联的多条拓扑路径,以次形成 **Parallel ** +- 无 AddParallel 方法,通过 AddEdge 构建并联的多条拓扑路径,以此形成 **Parallel ** ![](/img/eino/input_keys_output_keys_in_parallel.png) @@ -553,7 +552,7 @@ parallel := NewParallel() parallel.AddChatModel("output_key01", chat01) parallel.AddChatModel("output_key01", chat02) -chain := NewChain[any,any]() +chain := NewChain[[]*schema.Message,*schema.Message]() chain.AppendParallel(parallel) ``` @@ -572,14 +571,17 @@ chain.AppendParallel(parallel) // that wraps the provided cond to handle type assertions and error checking. // eg. -condition := func(ctx context.Context, in string, opts ...any) (endNode string, err error) { +condition := func(ctx context.Context, in string) (endNode string, err error) { // logic to determine the next node - return "some_next_node_key", nil + if len(in) == 0 { + return "node_1", nil + } + return "node_2", nil } cb := NewChainBranch[string](condition) -cb.AddPassthrough("next_node_key_01", xxx) // node in branch, represent one path of branch -cb.AddPassthrough("next_node_key_02", xxx) // node in branch +cb.AddLambda("node_1", lambda1) // node in branch, represent one path of branch +cb.AddLambda("node_2", lambda2) // node in branch chain := NewChain[string, string]() chain.AppendBranch(cb) diff --git a/content/zh/docs/eino/overview/eino_open_source.md b/content/zh/docs/eino/overview/eino_open_source.md index 5b07913dba..67e23b87b4 100644 --- a/content/zh/docs/eino/overview/eino_open_source.md +++ b/content/zh/docs/eino/overview/eino_open_source.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 大语言模型应用开发框架 —— Eino 正式开源! @@ -177,7 +177,7 @@ Eino 框架的设计开发过程,扎根于 “满足真实需求” 与 “实 项目地址:[https://github.com/cloudwego/eino](https://github.com/cloudwego/eino),[https://github.com/cloudwego/eino-ext](https://github.com/cloudwego/eino-ext) -项目官网:[https://www.cloudwego.io](https://www.cloudwego.io) +项目官网:__[https://www.cloudwego.io](https://www.cloudwego.io)__ 扫描二维码加入飞书社群: ![](/img/eino/eino_lark_qr_code.png) diff --git a/content/zh/docs/eino/quick_start/_index.md b/content/zh/docs/eino/quick_start/_index.md index ac5a07290d..afc6571ffb 100644 --- a/content/zh/docs/eino/quick_start/_index.md +++ b/content/zh/docs/eino/quick_start/_index.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-15" +date: "2025-01-20" lastmod: "" tags: [] title: 'Eino: 快速开始' @@ -33,16 +33,6 @@ AI 的应用中,最基础的场景就是 prompt + chat model 的场景,这 - [Agent-让大模型拥有双手](/zh/docs/eino/quick_start/agent_llm_with_tools) -### 示例:用编排构建复杂自定义应用 - -人工智能(AI) 的一项历史使命,就是把人从一些重复性的劳动中解放出来,而几乎任何一项劳动都是由多个流程和工序组合而成的,用 AI 完成这些相互串联的工作,这就是 “工作流”。由各种 AI 组件组合、编排而成的工作流,才是真正生产场景中的应用形态。 - -Eino 中,提供了以组件为第一编排对象,同时提供具有极强扩展能力的 Lambda 节点作为编排对象,能够实现快速上手和定制扩展的双优势。Eino 的编排还有一些其他特点: 编排过程中最重要的话题 “数据流” 在 Eino 中被强化,callbacks 提供了观测和调试的基础能力,call option 为运行时的扩展性提供了无限可能... - -这个示例中,我们将实现一个应用了编排能力的示例,结合 callbacks 和 call option 来实现观测和请求粒度的扩展能力。 - -- [复杂业务逻辑的利器-编排](/zh/docs/eino/quick_start/complex_business_logic_orchestration) - ## 下一步探索 - 理解 Eino 的核心模块和概念: [Eino: 核心模块](/zh/docs/eino/core_modules),这是你自如玩转使用 Eino 做应用开发的关键信息。 diff --git a/content/zh/docs/eino/quick_start/agent_llm_with_tools.md b/content/zh/docs/eino/quick_start/agent_llm_with_tools.md index e3257405d1..013f651746 100644 --- a/content/zh/docs/eino/quick_start/agent_llm_with_tools.md +++ b/content/zh/docs/eino/quick_start/agent_llm_with_tools.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-07" +date: "2025-01-20" lastmod: "" tags: [] title: Agent-让大模型拥有双手 @@ -119,8 +119,8 @@ import ( ) func main() { -// 创建 duckduckgo Search 工具 -searchTool, err := duckduckgo.NewTool(ctx, &duckduckgo.Config{}) + // 创建 duckduckgo Search 工具 + searchTool, err := duckduckgo.NewTool(ctx, &duckduckgo.Config{}) if err != nil { log.Fatal(err) } @@ -150,10 +150,10 @@ func main() { func main() { // 初始化 tools todoTools := []tool.BaseTool{ - getAddTodoTool(), // 使用 NewTool 方式 + getAddTodoTool(), // 使用 NewTool 方式 updateTool, // 使用 InferTool 方式 - &ListTodoTool{}, - searchTool, // 使用结构体实现方式, 此处未实现底层逻辑 + &ListTodoTool{}, // 使用结构体实现方式, 此处未实现底层逻辑 + searchTool, } // 创建并配置 ChatModel @@ -205,10 +205,10 @@ func main() { } // 运行示例 - resp, err := agent.Invoke(context.Background(), []*schema.Message{ + resp, err := agent.Invoke(ctx, []*schema.Message{ { - Role: schema.User, - Content: "添加一个学习 Eino 的 TODO,同时搜索一下 cloudwego/eino 的仓库地址", + Role: schema.User, + Content: "添加一个学习 Eino 的 TODO,同时搜索一下 cloudwego/eino 的仓库地址", }, }) if err != nil { @@ -250,4 +250,3 @@ Agent 是 AI 技术发展的重要方向。它不仅能够理解用户意图, - 快速开始 - [实现一个最简 LLM 应用-ChatModel](/zh/docs/eino/quick_start/simple_llm_application) - - [复杂业务逻辑的利器-编排](/zh/docs/eino/quick_start/complex_business_logic_orchestration) 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 3e79287e4c..3de1635f3c 100644 --- a/content/zh/docs/eino/quick_start/simple_llm_application.md +++ b/content/zh/docs/eino/quick_start/simple_llm_application.md @@ -1,6 +1,6 @@ --- Description: "" -date: "2025-01-16" +date: "2025-01-20" lastmod: "" tags: [] title: 实现一个最简 LLM 应用 @@ -10,7 +10,7 @@ weight: 1 本指南将帮助你快速上手使用 Eino 框架中的 ChatModel 构建一个简单的 LLM 应用。我们将通过实现一个"程序员鼓励师"的例子,来展示如何使用 ChatModel。 > 💡 -> 本文中示例的代码片段详见:[flow/eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go) +> 本文中示例的代码片段详见:[flow/eino-examples/quickstart/chat](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat) ## **ChatModel 简介** @@ -33,65 +33,64 @@ ChatModel 是 Eino 框架中对对话大模型的抽象,它提供了统一的 让我们通过实现一个程序员鼓励师来学习如何使用 ChatModel。这个助手不仅能提供技术建议,还能在程序员感到难过时给予心理支持。 -### **1. 创建对话模板** +### 创建对话模板并生成消息 -Eino 提供了强大的模板化功能来构建要输入给大模型的消息。你可以使用占位符来插入变量和模板消息: +Eino 提供了强大的模板化功能来构建要输入给大模型的消息: -1. 变量占位符:在消息中插入变量,支持三种格式: +1. 模版渲染,支持三种模版格式: - - FString: `{variable}` - - Jinja2: `{{variable}}` - - GoTemplate: `{{``.variable}}` -2. 消息占位符:用于插入一组消息(如对话历史) + - FString:Python 风格的简单字符串格式化(例如:"你好,{name}!") + - Jinja2:支持丰富表达式的 Jinja2 风格模板(例如:"你好,{{name}}!") + - GoTemplate:Go 语言内置的 text/template 格式(例如:"你好,{{.name}}!") +2. 消息占位符:支持插入一组消息(如对话历史) ```go -// optional=false 表示必需的消息列表,找不到对应变量会报错 +// optional=false 表示必需的消息列表,在模版输入中找不到对应变量会报错 schema.MessagesPlaceholder("chat_history", false) ``` > 更详细的组件介绍可参考: [Eino: ChatTemplate 使用说明](/zh/docs/eino/core_modules/components/chat_template_guide) -下面是完整的模板创建代码: +下面是完整的 FString 格式 + 消息占位符的对话模板创建及使用代码: ```go +// eino-examples/quickstart/chat/template.go + import ( + "context" + "github.com/cloudwego/eino/components/prompt" "github.com/cloudwego/eino/schema" ) -func main() { - // 创建模板,使用 FString 格式 - template := prompt.FromMessages(schema.FString, - // 系统消息模板 - schema.SystemMessage("你是一个{role}。你需要用{style}的语气回答问题。你的目标是帮助程序员保持积极乐观的心态,提供技术建议的同时也要关注他们的心理健康。"), - - // 插入需要的对话历史(新对话的话这里不填) - schema.MessagesPlaceholder("chat_history", true), - - // 用户消息模板 - schema.UserMessage("问题: {question}"), - ) - - // 使用模板生成消息 - messages, err := template.Format(context.Background(), map[string]any{ - "role": "程序员鼓励师", - "style": "积极、温暖且专业", - "question": "我的代码一直报错,感觉好沮丧,该怎么办?", - // 对话历史(这个例子里模拟两轮对话历史) - "chat_history": []*schema.Message{ - schema.UserMessage("你好"), - schema.AssistantMessage("嘿!我是你的程序员鼓励师!记住,每个优秀的程序员都是从 Debug 中成长起来的。有什么我可以帮你的吗?", nil), - schema.UserMessage("我觉得自己写的代码太烂了"), - schema.AssistantMessage("每个程序员都经历过这个阶段!重要的是你在不断学习和进步。让我们一起看看代码,我相信通过重构和优化,它会变得更好。记住,Rome wasn't built in a day,代码质量是通过持续改进来提升的。", nil), - }, - }) - if err != nil { - log.Fatal(err) - } -} +// 创建模板,使用 FString 格式 +template := prompt.FromMessages(schema.FString, + // 系统消息模板 + schema.SystemMessage("你是一个{role}。你需要用{style}的语气回答问题。你的目标是帮助程序员保持积极乐观的心态,提供技术建议的同时也要关注他们的心理健康。"), + + // 插入需要的对话历史(新对话的话这里不填) + schema.MessagesPlaceholder("chat_history", true), + + // 用户消息模板 + schema.UserMessage("问题: {question}"), +) + +// 使用模板生成消息 +messages, err := template.Format(context.Background(), map[string]any{ + "role": "程序员鼓励师", + "style": "积极、温暖且专业", + "question": "我的代码一直报错,感觉好沮丧,该怎么办?", + // 对话历史(这个例子里模拟两轮对话历史) + "chat_history": []*schema.Message{ + schema.UserMessage("你好"), + schema.AssistantMessage("嘿!我是你的程序员鼓励师!记住,每个优秀的程序员都是从 Debug 中成长起来的。有什么我可以帮你的吗?", nil), + schema.UserMessage("我觉得自己写的代码太烂了"), + schema.AssistantMessage("每个程序员都经历过这个阶段!重要的是你在不断学习和进步。让我们一起看看代码,我相信通过重构和优化,它会变得更好。记住,Rome wasn't built in a day,代码质量是通过持续改进来提升的。", nil), + }, +}) ``` -### **2. 创建并使用 ChatModel** +### 创建 ChatModel ChatModel 是 Eino 框架中最核心的组件之一,它提供了与各种大语言模型交互的统一接口。Eino 目前支持以下大语言模型的实现: @@ -104,101 +103,100 @@ ChatModel 是 Eino 框架中最核心的组件之一,它提供了与各种大 下面我们以 OpenAI 和 Ollama 为例,展示如何创建和使用 ChatModel: -#### **使用 OpenAI (和下方 ollama 2 选 1)** +#### **OpenAI (和下方 ollama 2 选 1)** ```go +// eino-examples/quickstart/chat/openai.go + import ( + "os" + "github.com/cloudwego/eino-ext/components/model/openai" ) -func main() { - // 创建 OpenAI ChatModel, 假设使用 openai 官方服务。 - chatModel, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{ - Model: "gpt-4o", // 使用的模型版本 - APIKey: "", // OpenAI API 密钥 - - // 可选的 Azure OpenAI 配置 - ByAzure: true, // 是否使用 Azure OpenAI - BaseURL: "", - }) - if err != nil { - log.Fatal(err) - } - - // 使用 Generate 获取完整回复 - response, err := chatModel.Generate(context.Background(), messages) - if err != nil { - log.Fatal(err) - } - - fmt.Println(response.Content) // 输出模型回复 -} +chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{ + Model: "gpt-4o", // 使用的模型版本 + APIKey: os.Getenv("OPENAI_API_KEY"), // OpenAI API 密钥 +}) ``` -> OpenAI 相关信息,可以参考:[ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) +> OpenAI ChatModel 的详细信息可以参考:[ChatModel - OpenAI](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai) -#### **使用 Ollama(和上方 openai 2 选 1)** +#### **Ollama(和上方 openai 2 选 1)** Ollama 支持在本地运行开源模型,适合对数据隐私有要求或需要离线使用的场景。 ```go +// eino-examples/quickstart/chat/ollama.go + import ( "github.com/cloudwego/eino-ext/components/model/ollama" ) -func main() { - // 创建 Ollama ChatModel - chatModel, err := ollama.NewChatModel(context.Background(), &ollama.ChatModelConfig{ - BaseURL: "http://localhost:11434", // Ollama 服务地址 - Model: "llama2", // 模型名称 - }) - if err != nil { - log.Fatal(err) - } - - // 使用 Generate 获取完整回复 - response, err := chatModel.Generate(context.Background(), messages) - if err != nil { - log.Fatal(err) - } - fmt.Println(response.Content) // 输出模型回复 -} + +chatModel, err := ollama.NewChatModel(ctx, &ollama.ChatModelConfig{ + BaseURL: "http://localhost:11434", // Ollama 服务地址 + Model: "llama2", // 模型名称 +}) ``` > OpenAI 相关信息,可以参考:[ChatModel - Ollama](/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama) 无论使用哪种实现,ChatModel 都提供了一致的接口,这意味着你可以轻松地在不同的模型之间切换,而无需修改大量代码。 -### **3. 处理流式响应** +### 运行 ChatModel + +经过前两步得到 ChatModel 的输入 messages 和初始化完成后的 ChatModel 实例后,可以开始尝试运行 ChatModel 了。Eino ChatModel 提供了两种运行模式:输出完整消息(generate)和输出消息流(stream): -在实际应用中,有很多场景需要使用流式响应,主要的场景例如「提升用户体验」:像 ChatGPT 一样逐字输出,让用户能够更早看到响应开始。 +```go +// eino-examples/quickstart/chat/generate.go -对于需要流式输出的场景,可以使用 ChatModel 的 Stream 方法: +/*** create messages +* messages, err := xxx +*/ + +/*** create chat model +* chatModel, err := xxx +*/ + +result, err := chatModel.Generate(ctx, messages) +streamResult, err := chatModel.Stream(ctx, messages) +``` + +在实际应用中,有很多场景需要使用流式响应,主要的场景例如「提升用户体验」:stream 运行模式让 ChatModel 提供类似打字机的输出效果,使用户更早得到模型响应。 + +Eino 中对流式输出的处理方式如下: ```go -func main() { - // 使用 Stream 获取流式响应 - stream, err := chatModel.Stream(context.Background(), messages) - if err != nil { - log.Fatal(err) - } - - // 处理流式响应 +// eino-examples/quickstart/chat/stream.go + +import ( + "io" + "log" + + "github.com/cloudwego/eino/schema" +) + +func reportStream(sr *schema.StreamReader[*schema.Message]) { + defer sr.Close() + + i := 0 for { - chunk, err := stream.Recv() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - - // 处理响应片段 - fmt.Print(chunk.Content) + message, err := sr.Recv() + if err == io.EOF { // 流式输出结束 + return + } + if err != nil { + log.Fatalf("recv failed: %v", err) + } + log.Printf("message[%d]: %+v\n", i, message) + i++ } } ``` +完整实现参见:[flow/eino-examples/quickstart/chat/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chat/main.go) + ## **总结** 本示例通过一个程序员鼓励师的案例,展示了如何使用 Eino 框架构建 LLM 应用。从 ChatModel 的创建到消息模板的使用,再到实际的对话实现,相信你已经对 Eino 框架有了基本的了解。无论是选择 OpenAI、Ollama 还是其他模型实现,Eino 都提供了统一且简单的使用方式。希望这个示例能帮助你快速开始构建自己的 LLM 应用。 @@ -207,4 +205,3 @@ func main() { - 快速开始 - [Agent-让大模型拥有双手](/zh/docs/eino/quick_start/agent_llm_with_tools) - - [复杂业务逻辑的利器-编排](/zh/docs/eino/quick_start/complex_business_logic_orchestration) diff --git a/static/img/eino/edge_of_parallel.png b/static/img/eino/edge_of_parallel.png index 75959f8258..3c5666136d 100644 Binary files a/static/img/eino/edge_of_parallel.png and b/static/img/eino/edge_of_parallel.png differ diff --git a/static/img/eino/eino_callback_langfuse_usage.gif b/static/img/eino/eino_callback_langfuse_usage.gif new file mode 100644 index 0000000000..0e83e3aac5 Binary files /dev/null and b/static/img/eino/eino_callback_langfuse_usage.gif differ diff --git a/static/img/eino/eino_debug_config_2_page.png b/static/img/eino/eino_debug_config_2_page.png new file mode 100644 index 0000000000..e2c95f20fe Binary files /dev/null and b/static/img/eino/eino_debug_config_2_page.png differ diff --git a/static/img/eino/eino_debug_config_3_page.png b/static/img/eino/eino_debug_config_3_page.png new file mode 100644 index 0000000000..7cf784c9ee Binary files /dev/null and b/static/img/eino/eino_debug_config_3_page.png differ diff --git a/static/img/eino/eino_debug_enter_config_page.png b/static/img/eino/eino_debug_enter_config_page.png new file mode 100644 index 0000000000..90e164f8f4 Binary files /dev/null and b/static/img/eino/eino_debug_enter_config_page.png differ diff --git a/static/img/eino/eino_debug_enter_page.png b/static/img/eino/eino_debug_enter_page.png new file mode 100644 index 0000000000..db669f7fc5 Binary files /dev/null and b/static/img/eino/eino_debug_enter_page.png differ diff --git a/static/img/eino/eino_debug_enter_test_run_2_page.png b/static/img/eino/eino_debug_enter_test_run_2_page.png new file mode 100644 index 0000000000..ea1909129c Binary files /dev/null and b/static/img/eino/eino_debug_enter_test_run_2_page.png differ diff --git a/static/img/eino/eino_debug_enter_test_run_page.png b/static/img/eino/eino_debug_enter_test_run_page.png new file mode 100644 index 0000000000..0ae302c50a Binary files /dev/null and b/static/img/eino/eino_debug_enter_test_run_page.png differ diff --git a/static/img/eino/eino_debug_index_page.png b/static/img/eino/eino_debug_index_page.png new file mode 100644 index 0000000000..a622778f5c Binary files /dev/null and b/static/img/eino/eino_debug_index_page.png differ diff --git a/static/img/eino/eino_debug_ip_port_show_page.png b/static/img/eino/eino_debug_ip_port_show_page.png new file mode 100644 index 0000000000..ba3345fd0e Binary files /dev/null and b/static/img/eino/eino_debug_ip_port_show_page.png differ diff --git a/static/img/eino/eino_debug_list_graph_and_show_page.png b/static/img/eino/eino_debug_list_graph_and_show_page.png new file mode 100644 index 0000000000..1cc1d72af6 Binary files /dev/null and b/static/img/eino/eino_debug_list_graph_and_show_page.png differ diff --git a/static/img/eino/eino_debug_list_nodes_page.png b/static/img/eino/eino_debug_list_nodes_page.png new file mode 100644 index 0000000000..0e8c98448d Binary files /dev/null and b/static/img/eino/eino_debug_list_nodes_page.png differ diff --git a/static/img/eino/eino_debug_run_config_page.png b/static/img/eino/eino_debug_run_config_page.png new file mode 100644 index 0000000000..cd3a63cb58 Binary files /dev/null and b/static/img/eino/eino_debug_run_config_page.png differ diff --git a/static/img/eino/eino_debug_run_detail_page.png b/static/img/eino/eino_debug_run_detail_page.png new file mode 100644 index 0000000000..5a26595d63 Binary files /dev/null and b/static/img/eino/eino_debug_run_detail_page.png differ diff --git a/static/img/eino/eino_debug_run_detail_v2_page.png b/static/img/eino/eino_debug_run_detail_v2_page.png new file mode 100644 index 0000000000..2bb196776c Binary files /dev/null and b/static/img/eino/eino_debug_run_detail_v2_page.png differ diff --git a/static/img/eino/eino_debug_run_input_mock_data_2_page.png b/static/img/eino/eino_debug_run_input_mock_data_2_page.png new file mode 100644 index 0000000000..fd60975f08 Binary files /dev/null and b/static/img/eino/eino_debug_run_input_mock_data_2_page.png differ diff --git a/static/img/eino/eino_debug_run_input_mock_data_page.png b/static/img/eino/eino_debug_run_input_mock_data_page.png new file mode 100644 index 0000000000..6a6675238f Binary files /dev/null and b/static/img/eino/eino_debug_run_input_mock_data_page.png differ diff --git a/static/img/eino/eino_debug_run_of_mock_input_of_page.png b/static/img/eino/eino_debug_run_of_mock_input_of_page.png new file mode 100644 index 0000000000..5d6a784222 Binary files /dev/null and b/static/img/eino/eino_debug_run_of_mock_input_of_page.png differ diff --git a/static/img/eino/eino_debug_run_page.png b/static/img/eino/eino_debug_run_page.png new file mode 100644 index 0000000000..6c01fcc267 Binary files /dev/null and b/static/img/eino/eino_debug_run_page.png differ diff --git a/static/img/eino/eino_debug_test_run_detail_page.png b/static/img/eino/eino_debug_test_run_detail_page.png new file mode 100644 index 0000000000..524226e0eb Binary files /dev/null and b/static/img/eino/eino_debug_test_run_detail_page.png differ diff --git a/static/img/eino/eino_debug_test_run_from_node_page.png b/static/img/eino/eino_debug_test_run_from_node_page.png new file mode 100644 index 0000000000..bdc9c414da Binary files /dev/null and b/static/img/eino/eino_debug_test_run_from_node_page.png differ diff --git a/static/img/eino/eino_debug_test_run_of_mock_data_page.png b/static/img/eino/eino_debug_test_run_of_mock_data_page.png new file mode 100644 index 0000000000..0ca96de8c1 Binary files /dev/null and b/static/img/eino/eino_debug_test_run_of_mock_data_page.png differ diff --git a/static/img/eino/eino_debug_test_run_of_one_node_page.png b/static/img/eino/eino_debug_test_run_of_one_node_page.png new file mode 100644 index 0000000000..c0a4fb2fec Binary files /dev/null and b/static/img/eino/eino_debug_test_run_of_one_node_page.png differ diff --git a/static/img/eino/eino_debug_test_run_result_page.png b/static/img/eino/eino_debug_test_run_result_page.png new file mode 100644 index 0000000000..82fc7e0882 Binary files /dev/null and b/static/img/eino/eino_debug_test_run_result_page.png differ diff --git a/static/img/eino/eino_dev_ability_introduction_page.png b/static/img/eino/eino_dev_ability_introduction_page.png new file mode 100644 index 0000000000..99a33f5a32 Binary files /dev/null and b/static/img/eino/eino_dev_ability_introduction_page.png differ diff --git a/static/img/eino/eino_dev_chat_model_config.png b/static/img/eino/eino_dev_chat_model_config.png new file mode 100644 index 0000000000..ecf4be3674 Binary files /dev/null and b/static/img/eino/eino_dev_chat_model_config.png differ diff --git a/static/img/eino/eino_dev_chat_model_config2.png b/static/img/eino/eino_dev_chat_model_config2.png new file mode 100644 index 0000000000..11f2355e0a Binary files /dev/null and b/static/img/eino/eino_dev_chat_model_config2.png differ diff --git a/static/img/eino/eino_dev_enter_page.png b/static/img/eino/eino_dev_enter_page.png new file mode 100644 index 0000000000..4809955745 Binary files /dev/null and b/static/img/eino/eino_dev_enter_page.png differ diff --git a/static/img/eino/eino_install_page.png b/static/img/eino/eino_install_page.png new file mode 100644 index 0000000000..a4dfbd8978 Binary files /dev/null and b/static/img/eino/eino_install_page.png differ diff --git a/static/img/eino/eino_install_page_2_page.png b/static/img/eino/eino_install_page_2_page.png new file mode 100644 index 0000000000..92c7fe7f78 Binary files /dev/null and b/static/img/eino/eino_install_page_2_page.png differ diff --git a/static/img/eino/eino_orchestration_add_edges_page.png b/static/img/eino/eino_orchestration_add_edges_page.png new file mode 100644 index 0000000000..8b2941f798 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_edges_page.png differ diff --git a/static/img/eino/eino_orchestration_add_graph_2_page.png b/static/img/eino/eino_orchestration_add_graph_2_page.png new file mode 100644 index 0000000000..2c12272905 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_graph_2_page.png differ diff --git a/static/img/eino/eino_orchestration_add_graph_config_deatil_page.png b/static/img/eino/eino_orchestration_add_graph_config_deatil_page.png new file mode 100644 index 0000000000..ffd2c98169 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_graph_config_deatil_page.png differ diff --git a/static/img/eino/eino_orchestration_add_graph_page.png b/static/img/eino/eino_orchestration_add_graph_page.png new file mode 100644 index 0000000000..5905fc8fb0 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_graph_page.png differ diff --git a/static/img/eino/eino_orchestration_add_nodes_2_page.png b/static/img/eino/eino_orchestration_add_nodes_2_page.png new file mode 100644 index 0000000000..9855b287ec Binary files /dev/null and b/static/img/eino/eino_orchestration_add_nodes_2_page.png differ diff --git a/static/img/eino/eino_orchestration_add_nodes_3_page.png b/static/img/eino/eino_orchestration_add_nodes_3_page.png new file mode 100644 index 0000000000..8d49861c0c Binary files /dev/null and b/static/img/eino/eino_orchestration_add_nodes_3_page.png differ diff --git a/static/img/eino/eino_orchestration_add_nodes_page.png b/static/img/eino/eino_orchestration_add_nodes_page.png new file mode 100644 index 0000000000..dc4cb6d9c8 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_nodes_page.png differ diff --git a/static/img/eino/eino_orchestration_add_slot_page.png b/static/img/eino/eino_orchestration_add_slot_page.png new file mode 100644 index 0000000000..39eeb790a3 Binary files /dev/null and b/static/img/eino/eino_orchestration_add_slot_page.png differ diff --git a/static/img/eino/eino_orchestration_describtion_page.png b/static/img/eino/eino_orchestration_describtion_page.png new file mode 100644 index 0000000000..f637f67d4d Binary files /dev/null and b/static/img/eino/eino_orchestration_describtion_page.png differ diff --git a/static/img/eino/eino_orchestration_enter_page.png b/static/img/eino/eino_orchestration_enter_page.png new file mode 100644 index 0000000000..17d45b54fe Binary files /dev/null and b/static/img/eino/eino_orchestration_enter_page.png differ diff --git a/static/img/eino/eino_orchestration_gencode_config_page.png b/static/img/eino/eino_orchestration_gencode_config_page.png new file mode 100644 index 0000000000..86817152c3 Binary files /dev/null and b/static/img/eino/eino_orchestration_gencode_config_page.png differ diff --git a/static/img/eino/eino_orchestration_gencode_page.png b/static/img/eino/eino_orchestration_gencode_page.png new file mode 100644 index 0000000000..7c4f7657f8 Binary files /dev/null and b/static/img/eino/eino_orchestration_gencode_page.png differ diff --git a/static/img/eino/eino_orchestration_index_2_page.png b/static/img/eino/eino_orchestration_index_2_page.png new file mode 100644 index 0000000000..aef3504dc4 Binary files /dev/null and b/static/img/eino/eino_orchestration_index_2_page.png differ diff --git a/static/img/eino/eino_orchestration_index_page.png b/static/img/eino/eino_orchestration_index_page.png new file mode 100644 index 0000000000..91691e3b41 Binary files /dev/null and b/static/img/eino/eino_orchestration_index_page.png differ diff --git a/static/img/eino/eino_orchestration_node_add_slots__page.png b/static/img/eino/eino_orchestration_node_add_slots__page.png new file mode 100644 index 0000000000..8c0044ba26 Binary files /dev/null and b/static/img/eino/eino_orchestration_node_add_slots__page.png differ diff --git a/static/img/eino/eino_orchestration_node_config_2_page.png b/static/img/eino/eino_orchestration_node_config_2_page.png new file mode 100644 index 0000000000..f23989f9db Binary files /dev/null and b/static/img/eino/eino_orchestration_node_config_2_page.png differ diff --git a/static/img/eino/eino_orchestration_show_nodes_2_page.png b/static/img/eino/eino_orchestration_show_nodes_2_page.png new file mode 100644 index 0000000000..22ee3d0e2f Binary files /dev/null and b/static/img/eino/eino_orchestration_show_nodes_2_page.png differ diff --git a/static/img/eino/eino_orchestration_show_nodes_page.png b/static/img/eino/eino_orchestration_show_nodes_page.png new file mode 100644 index 0000000000..d13ffe4c92 Binary files /dev/null and b/static/img/eino/eino_orchestration_show_nodes_page.png differ diff --git a/static/img/eino/eino_orchestration_sub_graph_pos_page.png b/static/img/eino/eino_orchestration_sub_graph_pos_page.png new file mode 100644 index 0000000000..ef9a1baa6c Binary files /dev/null and b/static/img/eino/eino_orchestration_sub_graph_pos_page.png differ diff --git a/static/img/eino/eino_orchestration_subgraph_show_page.png b/static/img/eino/eino_orchestration_subgraph_show_page.png new file mode 100644 index 0000000000..8fce51379e Binary files /dev/null and b/static/img/eino/eino_orchestration_subgraph_show_page.png differ diff --git a/static/img/eino/graph_node_callback_run_place.png b/static/img/eino/graph_node_callback_run_place.png new file mode 100644 index 0000000000..b1d7bebfbc Binary files /dev/null and b/static/img/eino/graph_node_callback_run_place.png differ diff --git a/static/img/eino/graph_node_type1.png b/static/img/eino/graph_node_type1.png new file mode 100644 index 0000000000..dc664fe7ae Binary files /dev/null and b/static/img/eino/graph_node_type1.png differ diff --git a/static/img/eino/graph_nodes.png b/static/img/eino/graph_nodes.png index 61a3e1bffb..91632f3b91 100644 Binary files a/static/img/eino/graph_nodes.png and b/static/img/eino/graph_nodes.png differ diff --git a/static/img/eino/graph_runnable_after_compile.png b/static/img/eino/graph_runnable_after_compile.png index 7abfe7472f..7e7ae017b0 100644 Binary files a/static/img/eino/graph_runnable_after_compile.png and b/static/img/eino/graph_runnable_after_compile.png differ diff --git a/static/img/eino/graph_stream_chunk_copy.png b/static/img/eino/graph_stream_chunk_copy.png new file mode 100644 index 0000000000..220157f76b Binary files /dev/null and b/static/img/eino/graph_stream_chunk_copy.png differ diff --git a/static/img/eino/input_keys_output_keys_in_parallel.png b/static/img/eino/input_keys_output_keys_in_parallel.png index 6ac7eeebd4..0dae772451 100644 Binary files a/static/img/eino/input_keys_output_keys_in_parallel.png and b/static/img/eino/input_keys_output_keys_in_parallel.png differ diff --git a/static/img/eino/invoke_stream_transform_collect.png b/static/img/eino/invoke_stream_transform_collect.png index 571168d4a2..7217b54a3e 100644 Binary files a/static/img/eino/invoke_stream_transform_collect.png and b/static/img/eino/invoke_stream_transform_collect.png differ diff --git a/static/img/eino/metrics_token_usage_in_fornax.png b/static/img/eino/metrics_token_usage_in_fornax.png index d097f02430..fc70fc8290 100644 Binary files a/static/img/eino/metrics_token_usage_in_fornax.png and b/static/img/eino/metrics_token_usage_in_fornax.png differ diff --git a/static/img/eino/react_agent_graph.png b/static/img/eino/react_agent_graph.png index d72caf350e..9b1d0ccf31 100644 Binary files a/static/img/eino/react_agent_graph.png and b/static/img/eino/react_agent_graph.png differ diff --git a/static/img/eino/recommend_way_of_handler.png b/static/img/eino/recommend_way_of_handler.png index a2945b3667..c37e56c804 100644 Binary files a/static/img/eino/recommend_way_of_handler.png and b/static/img/eino/recommend_way_of_handler.png differ diff --git a/static/img/eino/run_way_branch_in_graph.png b/static/img/eino/run_way_branch_in_graph.png index c0d5e04870..cd21a45def 100644 Binary files a/static/img/eino/run_way_branch_in_graph.png and b/static/img/eino/run_way_branch_in_graph.png differ