Eino概述

Eino市场定位

Eino 基于明确的"组件"定义,提供强大的流程"编排",覆盖开发全流程,旨在帮助开发者以最快的速度实现最有深度的大模型应用。

产品特点

  • 内核稳定,API 简单易懂,有明确的上手路径,平滑的学习曲线。
  • 极致的扩展性,研发工作高度活跃,长期可持续。
  • 基于强类型语言 Golang,代码能看懂,易维护,高可靠。
  • 背靠字节跳动核心业务线的充分实践经验。
  • 提供开箱即用的配套工具。

项目地址:github.com/cloudwego/e...github.com/cloudwego/e...

快速认识Eino

Eino 是覆盖 devops 全流程的大模型应用开发框架,从最佳实践样例的 Eino Examples,到各环节的工具链,都是 Eino 的领域:

个人的认知当中,eino不仅仅是一个项目,而注重的是一个产品,这个产品整体上是以eino这个项目为主导,衍生出多个分支下的组件进行相辅相成

在目前初步认识eino的前提下,确定根据官方文档,主要是从这三个项目中进行展开,找到学习eino相关的突破口

eino的作用

那么 Eino 具体能做什么?首先,Eino 由一个个大模型领域的"组件"组成,比如最核心的是与大模型交互的 Chat Model:

go 复制代码
model, _ := ark.NewChatModel(ctx, config) // 创建一个豆包大模型
message, _ := model.Generate(ctx, []*Message{
    SystemMessage("you are a helpful assistant."),
    UserMessage("what does the future AI App look like?")}

以 ReAct Agent 为例:一个 ChatModel(大模型),"绑定"了 Tool(工具),接收输入的 Message,由 ChatModel 自主判断是否调用 Tool 或输出最终结果。Tool 执行结果会再次成为给到 ChatModel 的 Message,并作为下一轮自主判断的上下文。

go 复制代码
// 构建一个 ReAct Agent,编译为一个输入为 []*Message,输出为 *Message 的 Runnable

// 创建包含 state 的 Graph,用户存储请求维度的 Message 上下文
graph = NewGraph[[]*Message, *Message](
   WithGenLocalState(func(ctx context.Context) *state {
      return &state{Messages: make([]*Message, 0, config.MaxStep+1)}
   }))

// 将一个轮次中的上下文和响应,存储到 Graph 的临时状态中
modelPreHandle = func(ctx context.Context, input []*Message, state *state) ([]*Message, error) {
    state.Messages = append(state.Messages, input...)
    return state.Messages, nil
}

_ = graph.AddChatModelNode(nodeKeyModel, chatModel, WithStatePreHandler(modelPreHandle))

_ = graph.AddEdge(START, nodeKeyModel)

_ = graph.AddToolsNode(nodeKeyTools, toolsNode)

// chatModel 的输出可能是多个 Message 的流
// 这个 StreamGraphBranch 根据流的首个包即可完成判断,降低延迟
modelPostBranch = NewStreamGraphBranch(
   func(_ context.Context, sr *schema.StreamReader[*Message]) (endNode string, err error) {
      defer sr.Close()

      if msg, err := sr.Recv(); err != nil {
         return "", err
      } else if len(msg.ToolCalls) == 0 {
         return END, nil
      }

      return nodeKeyTools, nil
   }, map[string]bool{nodeKeyTools: true, END: true})

_ =  graph.AddBranch(nodeKeyModel, modelPostBranch)

// toolsNode 执行结果反馈给 chatModel
_ = graph.AddEdge(nodeKeyTools, nodeKeyModel)

// 编译 Graph:类型检查、callback 注入、自动流式转换、生成执行器
agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))

在上面这几十行代码的背后,Eino 自动做了一些事情:

  • 类型检查,在 compile 时确保相邻的节点的类型对齐。

  • 流式封装,编译出的 Runnable 既可以 Invoke 调用,也可以 Stream 调用,无论内部的 Tool 是否支持流。

  • 并发管理,对 state 这个公共状态的读写是并发安全的。

  • 横切面注入,如果某个组件(比如一个 tool)没有实现 callbacks 注入,则 Eino 自动注入。

  • Option 分配,编译出的 Runnable 可以灵活接收并把 option 分配给指定的节点。

Eino的独特优势

Eino尽力去做到,既需要封装领域内"不变"的通用核心要素 ,又需要基于最新进展敏捷的横向和纵向扩展

另一方面,目前较为主流的框架如 LangChain,LlamaIndex 等,都基于 Python,虽然能借助 Python 较为丰富的生态快速实现多样的功能,但是同时也继承了 Python 作为动态语言所带来的"弱类型检验"和"长期维护成本高"等问题。在大模型应用快速进入大规模线上运行阶段的当下,基于 Golang 这一强类型语言而实现的高可靠性高可维护性,逐渐具有更大的价值。

在Python垄断的主流框架之下,更希望使用Golang这样的强类型语言去对大模型进行开发

内核稳定

每类组件作为一个 interface,有完善、稳定的定义:具体的输入输出类型,明确的运行时 option,以及明确的流处理范式。(在对于开发规范和项目整体设计是要有明确的规定)

敏捷扩展

每类组件都可以横向扩展出不同的实现,比如 ChatModel 组件可以有 OpenAI、Gemini、Claude 等不同的实现等。这些具体的实现,在实现组件 interface 从而可作为组件参与编排的基础上,可以实现和持续扩展自身的特殊功能。

当实际业务场景中,出现需要进入编排但是不对应任何组件定义的功能时,Eino 支持将自定义 function 声明为 Lambda 类型。Lambda 有用户声明的输入输出以及 option 类型,可支持全部的流处理范式,具备完整的 Callback 能力,在编排视角等价于官方组件。

高可用易维护

基于 Golang 写 Eino 代码时,开发者可以充分利用 Golang 的强类型特性,为所有的组件、Lambda、编排产物等声明具体类型。这像是为代码绘制了一幅精确的地图,开发者可以沿着清晰的路径进行维护和扩展,即使在项目规模不断扩大、功能持续迭代的情况下,依然能够保有较高的可维护性。

同时,Eino 编排能力也充分利用了强类型系统的编译时校验能力,尽可能将类型匹配问题暴露的时机提前到 graph 的编译时,而不是 graph 的运行时。尽早并明确的暴露类型匹配问题,有助于开发者迅速定位和修复,减少因类型错误在运行时引发的难以排查的故障和性能问题。

另一方面,Eino 遵循模块化设计,核心库以及各组件实现是单独的 go module,每个 go module 做到依赖最小化。同时,API 设计以"精简"、"直观"和"同构性"为原则,辅以由浅入深的全面文档,尽可能让学习曲线更平滑。最重要的是,Eino 采用清晰的分层设计,每层职责明确、功能内聚,在提升维护性的同时能更好的保证稳定性。

工具生态完善

链路追踪、调试、可视化,是编排引擎的三个重要辅助工具。Eino 内置了 tracing callback,并与 APMPlus 和 Langfuse 平台做了集成。同时提供了 IDE 插件,可以在写代码的过程中随时可视化查看编排出的 graph,并进行调试运行,甚至可以通过 UI 拖拽的方式快速构建 graph 并导出为 Eino 代码。

对于Eino个人理解

对于笔者而言,也是由Go语言服务端开发,慢慢去熟悉大模型应用相关的领域;在开始学习Eino这样的大语言模型应用开发框架时也是十分吃力的,有些地方也是比较难去理解;但是笔者一个很好的朋友告诉我,难上手是正常的,多写几遍,就像你高考写解析几何一样,熟练度来自踩坑 踩的坑越多熟练度会越高; 笔者也十分坚定,携风而行,滴水石穿!

相关推荐
程序员爱钓鱼41 分钟前
Go同步原语与数据竞争:原子操作(atomic)
后端·面试·go
天天摸鱼的java工程师42 分钟前
Kafka是如何保证消息队列中的消息不丢失、不重复?
java·后端·kafka
天天摸鱼的java工程师42 分钟前
SpringBoot 自动配置原理?@EnableAutoConfiguration 是如何工作的?
java·后端
郭尘帅6661 小时前
Spring依赖注入的四种方式(面)
java·后端·spring
风象南1 小时前
SpringBoot防重放攻击的5种实现方案
java·spring boot·后端
[email protected]1 小时前
Asp.Net Core SignalR导入数据
前端·后端·asp.net·.netcore
callJJ2 小时前
从 0 开始理解 Spring 的核心思想 —— IoC 和 DI(1)
java·开发语言·spring boot·后端·spring·restful·ioc di
编程乐学(Arfan开发工程师)7 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
Elcker10 小时前
Springboot+idea热更新
spring boot·后端·intellij-idea