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这样的大语言模型应用开发框架时也是十分吃力的,有些地方也是比较难去理解;但是笔者一个很好的朋友告诉我,难上手是正常的,多写几遍,就像你高考写解析几何一样,熟练度来自踩坑 踩的坑越多熟练度会越高; 笔者也十分坚定,携风而行,滴水石穿!