Hello,大家好久不见呀。想不到 2025 年刚开始,CloudWeGo
团队就做出了大动作,开源了 Go 生态下第一个由官方维护的 AI 应用开发框架。
如果你对我们感兴趣,欢迎加入到 豆包 MarsCode|校园 Campus 与我们一同玩耍呀~
作为一名 Go 开发者,曾经和 LLM 应用开发斗智斗勇的日子
在 24 年中,接手了校内知识库平台的建设,基于校内伙伴们的情况,选择了 Java 生态中 Spring Cloud Alibaba
和 Go 生态中的 Hertz
与 Kitex
做开发,其中 Java 负责主要业务流程部分的开发,而 Go 更多参与到基础设施与中间件的开发中。
在 AI 的冲击与对信息检索效率的考验下,我们团队开始尝试构建 RAG 流程。而在团队中有 AI 开发经验的也就只有我一人,比较尴尬的一点是,我又不想写 Python,同时对 Java 也没有那么娴熟,还是想使用 Go 来构建整套 RAG 流程。但是在选来选去中,发现 Python 有应用广泛的 LangChain
和 LangGraph
作为坚实的后盾,在 Java 生态中,Spring AI Alibab
a 也有着业内顶级大佬阿里的背书,但是作为近几年云原生时代大火的 Go 语言,却缺失了能够站得住跟脚的 AI 应用开发框架,当时在 LangChain-go
的社区徘徊了好久,发现社区中持续向前走的意见并不统一。不过好在在 2025 年,字节跳动开源团队 CloudWeGo
开源出了 Go 生态中第一份由大厂背书的 AI 应用开发框架,真可谓是千呼万唤始出来啊~
第一次见到 Eino 是 24 年底在 GitHub 中看到了 Eino
,便在尝试从源码入手,不断尝试这个崭新的框架啦。不过万万没想到,就在短短一段期末考试的时光,Eino
便已经登陆了 CloudWeGo 官网,并且已经有一份相当详细的文档了。那么今天我们就跟随着官方的步伐,去从官方文档中,把这个框架玩起来。
从 Eino 中去看到 Go 生态在 LLM 时代的野心
正可谓是后发先至,由于 Eino 的后发优势,使其在设计时,充分吸纳了 LangChain
、LangGraph
等 AI 应用开发框架的优势,在更契合 Golang 编程习惯的前提下,提供了更丰富的生态与更优秀的体验。
Eino[‘aino] (近似音: i know,希望应用程序达到 “i know” 的愿景) 旨在提供基于 Golang 语言的终极大模型应用开发框架。 它从开源社区中的诸多优秀 LLM 应用开发框架,如 LangChain 和 LlamaIndex 等获取灵感,同时借鉴前沿研究成果与实际应用,提供了一个强调简洁性、可扩展性、可靠性与有效性,且更符合 Go 语言编程惯例的 LLM 应用开发框架。
—— 这是 CloudWeGo 团队对其定位作出的描述
Eino 提供的价值如下:
- 精心整理的一系列 组件(component) 抽象与实现,可轻松复用与组合,用于构建 LLM 应用。
- 强大的 编排(orchestration) 框架,为用户承担繁重的类型检查、流式处理、并发管理、切面注入、选项赋值等工作。
- 一套精心设计、注重简洁明了的 API。
- 以集成 流程(flow) 和 示例(example) 形式不断扩充的最佳实践集合。
- 一套实用 工具(DevOps tools) ,涵盖从可视化开发与调试到在线追踪与评估的整个开发生命周期。
Eino 可在 AI 应用开发周期中的不同阶段,规范、简化和提效:
- Development: 开箱即用的 AI 相关组件;常见的 Flow 范式;对并发、异步、流式友好的图编排;完善的流处理能力等。这些均可对 AI 应用的开发提供很大助力。
- Debugging: 可对图编排的应用,进行可视化的开发调试
- Deployment: 提供丰富的对 AI 应用的评测能力
- Maintenance: 提供丰富的切面对 AI 应用进行观测、监控
从 Eino 的宏观事业下来看,CloudWeGo 的野心不可谓不大
从宏观角度来看,Eino 从框架学习、开发、测试、部署与观测这六个常见的 AI 应用开发阶段考虑,针对每个阶段的特点,提出了对应的解决方案。
从样例开始,让每一个 Go LLM 开发者零成本上手
在学习过程中,Eino
在 Eino Examples 中提供了大量易于理解的示例,并且针对常见的 AI 应用场景做了对应的 Demo 供大家学习与参考。
其中针对 Eino 的每一项核心能力都有针对性的示例供大家上手体验与学习,并在 quickstart 很贴心的为想要快速上手的同学提供了完善的示例。
我们可以看到,
CloudWeGo
团队对 Eino
的用户不可谓是不上心了,这可真的是把偏心写在了脸上🙈
从开发角度来讲,Eino 不仅为 AI 应用开发提供了高效的解决方案,更提供了强大的扩展能力
Eino
整体分为三个部分,分别提供了核心能力的抽象、具体实现与开发、调试、评测的管理能力:
-
Eino
: 其实我感觉这部分更贴切的叫法叫做 Eino Core,因为在这一层面,提供了 Eino 核心能力的抽象,其中主要包含以下几个部分:- 丰富的 Component 组件
- Graph、Chain 等图编排能力
- 丰富的 Stream 流处理机制
- Callback 高性能切面机制
-
Eino Ext
: 这一部分主要由组件实现、通用切面实现、组件使用示例等,与各种各样的 Eino 扩展能力 -
Eino Devops
: 在这一部分中对应的开发能力、调试能力与评测能力,不过目前还是以期待为主,也希望早日与其见面~
使用 Eino 动手跑通一个最简易的 LLM 应用
不知不觉说了这么多内容了,那下面让我们来看一下 Eino
到底是一个仅仅理论满分的花架子,还是一个文武双全的全能手呢?
下面让我们实际来编写一个最简易的 Prompt 提示词工程来看一下 Eino
的表现吧~
在这个 Demo 中,我们将使用 Eino 来实现一个通过 openAI 的方式调用 DeepSeek 的 prompt 工程项目。
那么写 Demo 的第一步是什么呢~ 那必然是MarsCode启动!!!
如果你也想随时随地拥有一台 2c4g 的服务器和一个贴心的代码助手,帮你解答在编程中的任何困惑与编写大量重复代码的话,那不妨也来试一下豆包 MarsCode | 云IDE,真的是爱不释手✌️
如果你也是在校大学生,也希望结识到一群志同道合的朋友,亦或是想通过自己的努力帮助到更多对 AI 与编程感兴趣的朋友,那也欢迎你加入到我们豆包 MarsCode | 校园 Campus
环境准备
-
项目创建 现在让我们一同在 MarsCode 云 IDE 中创建一个 Go 语言项目
-
引入相关依赖 我们可以在终端通过
go get
命令引入 Eino 相关依赖项
go get github.com/cloudwego/eino
go get github.com/cloudwego/eino-ext
这样就可以在终端与 go.mod
文件中看到我们成功引入了 Eino 相关依赖
- 申请 LLM 模型 API Key 我们在这篇文章中使用当前爆火的国产新兴模型 DeepSeek | 深度求索 我们在其开放平台 Api Key页面,创建一个全新的 Api Key 作为本次 Demo 的 Api Key,关于其调用方式可以参考其接口文档
代码编写
- 创建 prompt
package main
import (
"github.com/cloudwego/eino/components/prompt"
"github.com/cloudwego/eino/schema"
)
var (
SystemMessageDefaultTemplate = `你是一个{role}。你需要用{style}的语气回答问题。你的目标是帮助程序员保持积极乐观的心态,提供技术建议的同时也要关注他们的心理健康。`
UserMessageDefaultTemplate = `问题: {question}`
)
func creatPrompt() *prompt.DefaultChatTemplate {
// 创建模板,使用 FString 格式
return prompt.FromMessages(schema.FString,
// 系统消息模板
schema.SystemMessage(SystemMessageDefaultTemplate),
// 插入示例对话,可选
schema.MessagesPlaceholder("examples", true),
// 插入对话历史,必须
schema.MessagesPlaceholder("chat_history", false),
// 用户消息模板
schema.UserMessage(UserMessageDefaultTemplate),
)
}
- 使用模版生成消息
func getMessage(template *prompt.DefaultChatTemplate, chatHistory, example []*schema.Message, userQuestion string) (result []*schema.Message, err error) {
// 使用模板生成消息
messages, err := template.Format(context.Background(), map[string]any{
"role": "程序员鼓励师",
"style": "积极、温暖且专业",
"question": userQuestion,
// 对话历史(必需的)
"chat_history": chatHistory,
// 示例对话(可选的)
"examples": example,
})
if err != nil {
return nil, err
}
return messages, err
}
- 创建大模型客户端
func createChatModel(ctx context.Context, apiKey string) (*openai.ChatModel, error) {
chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
Model: "deepseek-chat",
APIKey: apiKey,
BaseURL: "https://api.deepseek.com",
})
if err != nil {
return nil, err
}
return chatModel, nil
}
- 完成调用
- 普通调用
chatHistory := []*schema.Message{}
example := []*schema.Message{
schema.UserMessage("我觉得自己写的代码太烂了"),
schema.AssistantMessage("每个程序员都经历过这个阶段!重要的是你在不断学习和进步。让我们一起看看代码,我相信通过重构和优化,它会变得更好。记住,Rome wasn't built in a day,代码质量是通过持续改进来提升的。", nil),
}
for {
fmt.Print("请输入你的问题:")
// 读取用户输入
var userQuestion string
count, err := fmt.Scanln(&userQuestion)
if err != nil {
fmt.Println("读取输入时发生错误:", err)
return
}
if count <= 0 {
fmt.Println("没有输入任何内容")
return
}
message, err := getMessage(ctx, template, chatHistory, example, userQuestion)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
outMsg, err := chatModel.Generate(ctx, message)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
chatHistory = append(chatHistory, outMsg)
fmt.Println("AI回复:", outMsg.Content)
}
- 流式调用
// 实现流式输出
stream, err := chatModel.Stream(ctx, message)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
defer stream.Close()
for {
var context string
response, err := stream.Recv()
if errors.Is(err, io.EOF) {
// 将用户的问题和模型的回答添加到聊天历史中
resp := schema.AssistantMessage(context, nil)
chatHistory = append(chatHistory, resp)
fmt.Println()
break
}
if err != nil {
fmt.Println("接收流式响应时发生错误:", err)
return
}
fmt.Print(response.Content)
context += response.Content
}
}
完整代码
- 普通调用
package main
import (
"context"
"fmt"
"github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/components/prompt"
"github.com/cloudwego/eino/schema"
)
var (
ModelType = "deepseek-chat"
OwnerAPIKey = "YOUR_API_KEY"
DeepSeekBaseURL = "https://api.deepseek.com"
SystemMessageDefaultTemplate = `你是一个{role}。你需要用{style}的语气回答问题。你的目标是帮助程序员保持积极乐观的心态,提供技术建议的同时也要关注他们的心理健康。`
UserMessageDefaultTemplate = `问题: {question}`
)
func main() {
ctx := context.Background()
// 创建提示词模版
template := createPrompt()
// 创建模型配置
chatModelConfig := &openai.ChatModelConfig{
Model: ModelType,
APIKey: OwnerAPIKey,
BaseURL: DeepSeekBaseURL,
}
// 创建模型客户端
chatModel, err := createChatModel(ctx, chatModelConfig)
if err != nil {
fmt.Println("创建聊天模型时发生错误:", err)
return
}
// 构造聊天历史(必须)与示例(可选)
chatHistory := []*schema.Message{}
example := []*schema.Message{
schema.UserMessage("我觉得自己写的代码太烂了"),
schema.AssistantMessage("每个程序员都经历过这个阶段!重要的是你在不断学习和进步。让我们一起看看代码,我相信通过重构和优化,它会变得更好。记住,Rome wasn't built in a day,代码质量是通过持续改进来提升的。", nil),
}
for {
fmt.Print("请输入你的问题:")
// 读取用户输入
var userQuestion string
count, err := fmt.Scanln(&userQuestion)
if err != nil {
fmt.Println("读取输入时发生错误:", err)
return
}
if count <= 0 {
fmt.Println("没有输入任何内容")
return
}
// 构造消息
message, err := getMessage(ctx, template, chatHistory, example, userQuestion)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
// 调用模型生成答案
outMsg, err := chatModel.Generate(ctx, message)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
// 将答案追加到聊天历史中
chatHistory = append(chatHistory, outMsg)
fmt.Println("AI回复:", outMsg.Content)
}
}
func createPrompt() *prompt.DefaultChatTemplate {
// 创建模板,使用 FString 格式
return prompt.FromMessages(schema.FString,
// 系统消息模板
schema.SystemMessage(SystemMessageDefaultTemplate),
// 插入可选的示例对话
schema.MessagesPlaceholder("examples", true),
// 插入必需的对话历史
schema.MessagesPlaceholder("chat_history", false),
// 用户消息模板
schema.UserMessage(UserMessageDefaultTemplate),
)
}
func getMessage(ctx context.Context, template *prompt.DefaultChatTemplate, chatHistory, example []*schema.Message, userQuestion string) (result []*schema.Message, err error) {
// 使用模板生成消息
messages, err := template.Format(ctx, map[string]any{
"role": "程序员鼓励师",
"style": "积极、温暖且专业",
"question": userQuestion,
// 对话历史(必需的)
"chat_history": chatHistory,
// 示例对话(可选的)
"examples": example,
})
if err != nil {
return nil, err
}
return messages, err
}
func createChatModel(ctx context.Context, config *openai.ChatModelConfig) (*openai.ChatModel, error) {
chatModel, err := openai.NewChatModel(ctx, config)
if err != nil {
return nil, err
}
return chatModel, nil
}
- 流式调用
package main
import (
"context"
"errors"
"fmt"
"io"
"github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino/components/prompt"
"github.com/cloudwego/eino/schema"
)
var (
ModelType = "deepseek-chat"
OwnerAPIKey = "YOUR_API_KEY"
DeepSeekBaseURL = "https://api.deepseek.com"
SystemMessageDefaultTemplate = `你是一个{role}。你需要用{style}的语气回答问题。你的目标是帮助程序员保持积极乐观的心态,提供技术建议的同时也要关注他们的心理健康。`
UserMessageDefaultTemplate = `问题: {question}`
)
func main() {
ctx := context.Background()
template := createPrompt()
chatModelConfig := &openai.ChatModelConfig{
Model: ModelType,
APIKey: OwnerAPIKey,
BaseURL: DeepSeekBaseURL,
}
chatModel, err := createChatModel(ctx, chatModelConfig)
if err != nil {
fmt.Println("创建聊天模型时发生错误:", err)
return
}
chatHistory := []*schema.Message{}
example := []*schema.Message{
schema.UserMessage("我觉得自己写的代码太烂了"),
schema.AssistantMessage("每个程序员都经历过这个阶段!重要的是你在不断学习和进步。让我们一起看看代码,我相信通过重构和优化,它会变得更好。记住,Rome wasn't built in a day,代码质量是通过持续改进来提升的。", nil),
}
for {
fmt.Print("请输入你的问题:")
// 读取用户输入
var userQuestion string
count, err := fmt.Scanln(&userQuestion)
if err != nil {
fmt.Println("读取输入时发生错误:", err)
return
}
if count <= 0 {
fmt.Println("没有输入任何内容")
return
}
message, err := getMessage(ctx, template, chatHistory, example, userQuestion)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
// 实现流式输出
stream, err := chatModel.Stream(ctx, message)
if err != nil {
fmt.Println("生成消息时发生错误:", err)
return
}
defer stream.Close()
for {
var context string
response, err := stream.Recv()
if errors.Is(err, io.EOF) {
// 将用户的问题和模型的回答添加到聊天历史中
resp := schema.AssistantMessage(context, nil)
chatHistory = append(chatHistory, resp)
fmt.Println()
break
}
if err != nil {
fmt.Println("接收流式响应时发生错误:", err)
return
}
fmt.Print(response.Content)
context += response.Content
}
}
}
func createPrompt() *prompt.DefaultChatTemplate {
// 创建模板,使用 FString 格式
return prompt.FromMessages(schema.FString,
// 系统消息模板
schema.SystemMessage(SystemMessageDefaultTemplate),
// 插入可选的示例对话
schema.MessagesPlaceholder("examples", true),
// 插入必需的对话历史
schema.MessagesPlaceholder("chat_history", false),
// 用户消息模板
schema.UserMessage(UserMessageDefaultTemplate),
)
}
func getMessage(ctx context.Context, template *prompt.DefaultChatTemplate, chatHistory, example []*schema.Message, userQuestion string) (result []*schema.Message, err error) {
// 使用模板生成消息
messages, err := template.Format(ctx, map[string]any{
"role": "程序员鼓励师",
"style": "积极、温暖且专业",
"question": userQuestion,
// 对话历史(必需的)
"chat_history": chatHistory,
// 示例对话(可选的)
"examples": example,
})
if err != nil {
return nil, err
}
return messages, err
}
func createChatModel(ctx context.Context, config *openai.ChatModelConfig) (*openai.ChatModel, error) {
chatModel, err := openai.NewChatModel(ctx, config)
if err != nil {
return nil, err
}
return chatModel, nil
}
Eino 到底是不是 Go LLM 开发者所期待的 AI 应用开发框架呢?
从个人角度出发,Eino 作为一个刚刚诞生之初的 AI 应用开发框架,贴合 Go 生态且相对丰富的组件与编排能力,以及本篇文章中暂且没有聊到的 Tools 等工具,现在对 Eino 的了解可谓是冰山一角,而其展现的活力已经是十分惊人了,如果后续能够形成一套统一且完善的生态,那也将带领着 Go 在 LLM 领域闯出一番相当不错的天地。同时在当前境遇下,Eino 所独有的可持续性,使我也愿意为 Eino 投入一番经历。
在日后我也将会继续从 Eino 官方示例、架构设计与源码解读等多个方面持续解读 Eino 这一个 Go 生态下崭新的 LLM 应用开发框架
p.s. CloudWeGo 团队是不是该打钱了🤓🤓🤓,还有 DeepSeek🤫🤫🤫
谢谢大家,我是 KingYen.,一名不入流大学的大三计科生,欢迎你的喜欢~