eino-ext 生态
Eino 主仓只有"接口 + 引擎",真正的模型/工具/向量库实现在 eino-ext。今天看生态版图、怎么接一个真实模型、以及"接口与实现分离"如何让生态繁荣。
为什么主仓不放实现
回忆 Day 04:Eino 主仓 components/ 里只有接口(ChatModel、Tool、Retriever…),没有 OpenAI、没有 Redis 向量库的具体实现。这些都在独立仓库 eino-ext。
ChatModel 接口),至于插进来的是原装充电器、小米的、还是充电宝(OpenAI / 豆包 / Ollama 各家实现,都在 eino-ext),手机一概不关心、照样充电。想换充电器?拔了换一个,手机不用动。接口统一 → 实现可插拔 → 谁都能来做一款兼容充电器 → 生态繁荣。import 了 components/model,为什么调不出 OpenAI?👨🏫 老师:因为那个包里只有
ChatModel 接口——一张"合同",规定"一个模型得会 Generate/Stream",但没写任何真的调 OpenAI 的代码。👶 小白:那真正能调 OpenAI 的代码在哪?
👨🏫 老师:在另一个仓库 eino-ext,包路径是
eino-ext/components/model/openai。你要 go get 它、单独 import 它,才拿到"履行合同"的实现。👶 小白:那我岂不是要 import 两个地方?
👨🏫 老师:对,而且这正是好处——你只 import 你用的那家(openai),其它家的 SDK 一点不沾。合同在 eino,干活的在 eino-ext。
生态版图
ChatModel 模型
OpenAI、Claude、豆包(ARK)、Gemini、Ollama、通义千问…
Tool 工具
Google 搜索、必应、浏览器、代码执行、MCP 协议接入…
Embedding 向量化
OpenAI、豆包、本地模型…
Retriever/Indexer
Milvus、Redis、Elasticsearch、VikingDB…
Document 文档
PDF/HTML/Markdown 解析、分块 Transformer…
Callbacks 可观测
Langfuse、APMPlus、CozeLoop 链路追踪…
eino-ext 就是这些接口的"官方 + 社区实现集市"。用哪个 import 哪个,各自 go get,互不牵连。接一个 OpenAI 模型(真实用法)
import "github.com/cloudwego/eino-ext/components/model/openai"
cm, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: "gpt-4o",
})
// cm 就是一个 model.ToolCallingChatModel(Day 04 的接口)
// 直接扔进 Day 12 的 Agent 或 Day 07 的 Chain 就能用
agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{Model: cm, ...})
claude.NewChatModel,下游 Agent/Chain 代码一行不用改——这就是面向接口编程的威力。·
import "github.com/cloudwego/eino/adk" — 来自 eino 主仓,拿的是编排/Agent 引擎(吃 ChatModel 接口)。·
import "github.com/cloudwego/eino-ext/components/model/openai" — 来自 eino-ext,拿的是能真调 OpenAI 的实现。两者在
openai.NewChatModel(...) 这一行"合体":eino-ext 造出实现,塞进 eino 引擎的 Model 字段。就像给手机(eino 引擎)插上充电器(eino-ext 实现)——插口标准,插上就能用。grep openai 应该能搜到调用 OpenAI 的代码。其实搜不到——主仓 components/model/ 只有 interface.go 这类接口文件,一行厂商 SDK 都没有。真正的 openai.go 在 eino-ext 仓。搜错地方会以为"框架坏了",其实是找错了仓库。实现方怎么满足接口
eino-ext 里 openai 包做的事:把 OpenAI 的 HTTP API 包装成 Eino 的 ChatModel 接口——实现 Generate(对应 Invoke)和 Stream:
// 概念:eino-ext/components/model/openai 内
func (c *ChatModel) Generate(ctx, msgs []*schema.Message, opts ...) (*schema.Message, error) {
req := 把 schema.Message 转成 OpenAI 请求格式
resp := 调 OpenAI HTTP API
return 把 OpenAI 响应转回 schema.Message, nil // ★ 翻译两端格式
}
func (c *ChatModel) Stream(...) (*schema.StreamReader[*schema.Message], error) {
// 把 OpenAI 的 SSE 流转成 Eino 的 StreamReader(Day 17)
}
工具生态(含 MCP)
工具实现同理——实现 Day 04 的 Tool 接口即可。特别值得一提的是 MCP 接入:eino-ext 能把 MCP(Model Context Protocol)服务器暴露的工具,包装成 Eino 的 Tool。
mcp__* 工具用的协议)。有了 MCP 适配,社区海量的 MCP 工具服务器(文件系统、数据库、各种 SaaS)都能直接变成你 Eino Agent 的工具。接口标准化 → 生态自动繁荣,你几乎不用自己写工具。RAG 生态
Day 04 的 RAG 三件套(Embedder / Indexer / Retriever)在 eino-ext 里都有多家实现:向量库可选 Milvus / Redis / ES / VikingDB,向量化可选 OpenAI / 豆包 embedding。搭一条 RAG 管道就是把它们用 Chain/Graph 串起来。
自己写一个组件
没有你要的实现?自己写一个——只要实现对应接口。比如自定义一个工具:
// 最简单:用 utils 把一个普通函数变成 Tool(Day 04 提过 InferTool)
import "github.com/cloudwego/eino/components/tool/utils"
myTool, _ := utils.InferTool("get_time", "获取当前时间",
func(ctx context.Context, in struct{ Zone string }) (string, error) {
return 查某时区当前时间(in.Zone), nil
})
// myTool 就是合法的 tool.BaseTool,能直接进 Agent
InferTool 用反射从你的函数签名自动推断出工具的参数 schema,把普通 Go 函数变成 Eino 工具。你几乎零样板就能扩展。要接一个新模型/向量库,就实现对应接口的 Generate/Stream 或 Store/Search——照着 eino-ext 里现成的实现抄结构即可。今日小结 + 动手
🧠 今天你应该能回答
- 为什么实现放在 eino-ext 而非主仓?三个好处?
- 接一个 OpenAI 模型几步?换模型为什么零成本?
- 实现方的核心工作是什么?(翻译 schema ↔ 厂商格式)
- MCP 接入带来什么?
- 怎么自己写一个工具/组件?
✋ 动手
# 若本地有 eino-ext 仓库
ls eino-ext/components/model/ 2>/dev/null || echo "去 github.com/cloudwego/eino-ext 看目录结构"
grep -rn 'InferTool' components/tool/utils/ | head
# 回看主仓只有接口
ls components/model/ # 只有 interface.go 等,没有 openai.go