面向读者:有一定 Go 基础的后端工程师,想上手大模型应用开发但不知从何入手
阅读时间:约 20 分钟
官方仓库:github.com/firebase/genkit/go
我最近在一个 Go 后端项目里接入了大模型,踩了不少坑才摸索出一套顺手的方式,这篇文章把整个过程和思路梳理出来,分享给同样在折腾这件事的朋友。
目录
- 写在前面:为什么 Go 开发者接入 AI 这么难?
- Genkit 是什么,它解决了哪些具体问题?
- 生态全景:不只是 Gemini,它支持哪些模型和存储?
- 四个核心概念,理解了就会用
- 动手写第一个完整示例(含 Tool Calling)
- Dev UI:调试大模型应用从此不再抓瞎
- Dotprompt:把提示词从代码里解放出来
- 集成到现有 Go 项目的建议与注意事项
- 总结
一、写在前面:为什么 Go 开发者接入 AI 这么难?
做过这件事的人都明白,最简单的"调用一个大模型接口"其实并不难,几十行 HTTP 代码就能搞定。难的是下面这些:
第一,Tool Calling(工具调用)非常繁琐。 当你希望大模型能够调用你自己写的函数——比如查数据库、调第三方 API——你需要手动把函数的入参结构描述成 JSON Schema 告诉模型,然后解析模型返回的调用指令,执行本地函数,再把结果拼回消息继续请求模型,整个过程完全是手工活,写起来又长又容易出 bug。
第二,多轮对话的状态管理很乱。 用户和 AI 来回聊几轮,你需要自己维护消息历史,稍不注意就超出模型的上下文窗口,或者上下文顺序乱掉。
第三,完全是黑盒,调试极其困难。 模型回答不对,你不知道是 Prompt 写得不好,还是 Tool 返回了错误数据,还是上下文被截断了。日志里一大坨 JSON 根本看不懂。
第四,今天用 Gemini,明天要换 DeepSeek,代码要大改。 没有统一抽象,换个模型就等于重写。
Genkit 的出现,就是为了把这些问题系统性地解决掉。
二、Genkit 是什么,它解决了哪些具体问题?
Google Genkit 是一个开源的大模型应用开发框架,Go 和 TypeScript 都有官方支持。它的核心思路是:在你的业务代码和大模型之间建立一层结构化的中间层,让你不必每次从零处理那些重复又容易出错的工程问题。
用一句话描述它的定位:Genkit 是 Go 生态里的大模型应用脚手架,而不只是一个 SDK 封装。
具体解决了什么:
| 痛点 | Genkit 的解法 |
|---|---|
| Tool Calling 手工解析 JSON Schema | 直接用 Go struct,框架自动推导 Schema,并完整处理调用闭环 |
| 黑盒调试困难 | 内置本地 Dev UI,调用链、耗时、Token 消耗一览无余 |
| 换模型要改大量代码 | 统一的 Plugin 接口,换模型只改初始化一行 |
| Prompt 写死在代码里,难以维护 | Dotprompt 让提示词单独成文件,支持模板变量,改 Prompt 不用重新编译 |
| 线上缺乏可观测性 | 原生集成 OpenTelemetry,Trace、Span 开箱即用 |
三、生态全景:不只是 Gemini,它支持哪些模型和存储?
很多人看到 "Google Genkit" 就以为只能用 Google 自家的模型。这是个常见误解。Genkit 的插件体系是完全开放的,可以接入主流的各类模型和向量存储。
3.1 模型支持
官方内置插件:
plugins/googlegenai— 接入 Google AI Studio 和 Vertex AI,支持 Gemini 全系列(含 Gemini 3.8 Flash、Gemini 3.1 Pro)及 Imagen 图像生成模型plugins/ollama— 接入本地 Ollama,支持 DeepSeek-R1/V3、Qwen2.5、Llama 3、Gemma 3 等开源模型,数据完全不出本地plugins/compat_oai— OpenAI 协议兼容层,凡是实现了 OpenAI 接口标准的服务都能接入,包括 GPT-4o、o3,以及 vLLM、OneAPI、LiteLLM 搭建的私有代理网关
社区插件(BloomLabsInc/genkit-plugins 等):
- Anthropic Claude(3.5 / 3.7 / Sonnet)
- Groq(极速推理,支持 Llama、Mixtral)
- Mistral
- Azure OpenAI
模型切换对上层代码完全透明,换模型只改初始化:
go
// 用 Gemini 3.8 Flash
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithDefaultModel("googleai/gemini-3.8-flash"),
)
// 换成本地 DeepSeek,其余代码一字不改
g := genkit.Init(ctx,
genkit.WithPlugins(&ollama.Plugin{ServerAddress: "http://localhost:11434"}),
genkit.WithDefaultModel("ollama/deepseek-r1:latest"),
)
// 换成 OpenAI
g := genkit.Init(ctx,
genkit.WithPlugins(&compat_oai.Plugin{
APIKey: os.Getenv("OPENAI_API_KEY"),
BaseURL: "https://api.openai.com/v1",
}),
genkit.WithDefaultModel("openai/gpt-4o"),
)
3.2 向量存储(用于 RAG / 知识库检索)
Genkit 定义了统一的 Indexer(入库)和 Retriever(检索)接口,支持:
- pgvector(PostgreSQL 向量扩展,Go 项目首选)
- Pinecone(云原生向量数据库)
- Milvus / Weaviate / Qdrant / Chroma
- 内存检索器(本地开发和单元测试用,零外部依赖)
3.3 当前主流模型型号速查
| 模型标识符 | 适用场景 |
|---|---|
googleai/gemini-3.8-flash |
日常首选:延迟低、速度快、Tool Calling 决策能力强 |
googleai/gemini-3.1-pro |
高复杂度任务:深度推理、代码分析、长文档处理 |
ollama/deepseek-r1:latest |
私有化部署:数据不出内网,强逻辑推理 |
openai/gpt-4o |
OpenAI 生态:多模态,生态成熟 |
四、四个核心概念,理解了就会用
Genkit 的学习曲线比较平缓,掌握这四个概念就能上手绝大多数场景。
4.1 Plugin(插件)——模型的接入方式
Plugin 负责把具体的大模型或向量存储接进来。你在初始化时注册插件,后面所有地方都通过统一接口使用,不需要知道底层是哪家的模型。
go
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
)
4.2 Tool(工具)——让模型拥有行动能力
这是 Genkit 里最有意思的部分。大模型天生只能生成文本,它不知道"今天几号",也不能查你的数据库。Tool 让你把任意 Go 函数暴露给模型,模型在推理过程中可以自主决定"我需要调用这个函数来回答用户的问题"。
go
// 定义一个 Tool:查询数据库里的订单状态
orderTool := genkit.DefineTool(
g,
"getOrderStatus", // 工具名,模型用这个识别它
"查询指定订单号的当前状态", // 工具描述,模型用这个判断何时调用它
func(ctx *ai.ToolContext, input struct {
OrderID string `json:"order_id"`
}) (string, error) {
// 这里写真实的数据库查询逻辑
status := db.QueryOrderStatus(input.OrderID)
return status, nil
},
)
注意:模型自动完成"决定调用哪个工具 → 框架执行 Go 函数 → 把结果返回给模型 → 模型根据结果生成回复"整个闭环,你不需要手写任何轮询或协调代码。
4.3 Flow(工作流)——可测试、可追踪的执行单元
Flow 是对你的 AI 业务逻辑的封装,相当于一个有类型签名、支持 Trace 追踪、可以在 Dev UI 里单独测试的函数。
go
chatFlow := genkit.DefineFlow(
g,
"chatFlow",
func(ctx context.Context, userMessage string) (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithPrompt(userMessage),
ai.WithTools(orderTool),
)
},
)
// 调用
result, err := chatFlow.Run(ctx, "帮我查一下订单 ORD-12345 的状态")
4.4 Generate(生成)——向模型发起推理
这是实际触发模型推理的地方。你可以在这里组合 Prompt、指定模型、挂载 Tool、设置输出格式。
go
// 生成纯文本
text, err := genkit.GenerateText(ctx, g,
ai.WithPrompt("用一句话介绍 Go 语言"),
)
// 生成结构化数据(模型输出会被严格校验并反序列化)
type ProductInfo struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
resp, err := genkit.GenerateData[ProductInfo](ctx, g,
ai.WithPrompt("描述一款笔记本电脑,返回名称和价格"),
)
fmt.Println(resp.Output.Name, resp.Output.Price)
五、动手写第一个完整示例(含 Tool Calling)
废话不多说,直接上一个可以跑起来的完整例子。这个例子包含两个 Tool:获取当前时间、查询城市天气(模拟),可以清晰地看到 Tool Calling 的完整工作过程。
第一步:初始化项目
bash
mkdir my-genkit-app && cd my-genkit-app
go mod init my-genkit-app
go get github.com/firebase/genkit/go@latest
go get github.com/firebase/genkit/go/plugins/googlegenai@latest
API Key 获取地址(免费):https://aistudio.google.com/
bash
export GEMINI_API_KEY="你申请到的 Key"
第二步:写代码
创建 main.go:
go
package main
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/googlegenai"
)
// 工具的输入类型,Genkit 会自动将 struct 转为 JSON Schema 传给模型
type WeatherInput struct {
City string `json:"city" description:"要查询的城市名,比如北京、深圳"`
}
type WeatherResult struct {
City string `json:"city"`
Temperature string `json:"temperature"`
Description string `json:"description"`
}
func main() {
ctx := context.Background()
// 初始化 Genkit,插件负责对接 Google Gemini
// GEMINI_API_KEY 环境变量会被自动读取
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithDefaultModel("googleai/gemini-3.8-flash"),
)
// Tool 1:获取系统当前时间
// 大模型没有实时时钟,这个 Tool 让它能回答"现在几点"这类问题
timeTool := genkit.DefineTool(g,
"getCurrentTime",
"获取服务器当前的精确时间",
func(ctx *ai.ToolContext, _ struct{}) (string, error) {
return time.Now().Format("2006年01月02日 15:04:05 MST"), nil
},
)
// Tool 2:查询城市天气(这里用模拟数据,实际项目里接真实 API 或数据库)
weatherTool := genkit.DefineTool(g,
"getCityWeather",
"查询指定城市的实时天气情况",
func(ctx *ai.ToolContext, input WeatherInput) (*WeatherResult, error) {
// 模拟天气数据,实际替换成真实 API 调用
data := map[string]WeatherResult{
"北京": {City: "北京", Temperature: "28°C", Description: "晴,微风"},
"上海": {City: "上海", Temperature: "23°C", Description: "多云转阵雨"},
"深圳": {City: "深圳", Temperature: "32°C", Description: "高温,有雷阵雨"},
}
for key, result := range data {
if strings.Contains(input.City, key) {
r := result
return &r, nil
}
}
return &WeatherResult{
City: input.City,
Temperature: "未知",
Description: "暂无该城市数据",
}, nil
},
)
// 定义一个 Flow,把工具挂上去
// Flow 会出现在 Dev UI 里,可以直接在浏览器里测试
assistantFlow := genkit.DefineFlow(g,
"assistantFlow",
func(ctx context.Context, question string) (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithPrompt(question),
ai.WithTools(timeTool, weatherTool),
)
},
)
// 测试几个问题,观察模型如何自主决定调用哪个工具
questions := []string{
"现在是几点?",
"北京今天天气怎么样,出门需要带伞吗?",
"上海和深圳今天哪个城市更热?",
}
for _, q := range questions {
fmt.Printf("\n🙋 问:%s\n", q)
answer, err := assistantFlow.Run(ctx, q)
if err != nil {
log.Printf("出错了: %v", err)
continue
}
fmt.Printf("🤖 答:%s\n", answer)
fmt.Println("────────────────────────────────────────")
}
}
第三步:运行
bash
go run main.go
你会观察到:
- 问"现在几点"→ 模型调用
getCurrentTime,用返回的真实时间回答 - 问"北京天气"→ 模型提取城市参数,调用
getCityWeather,根据天气数据给出出行建议 - 问"上海和深圳哪个更热"→ 模型连续调用两次
getCityWeather,对比数据后给出结论
整个过程你没有写任何协调逻辑,Tool Calling 的闭环完全由框架处理。
六、Dev UI:调试大模型应用从此不再抓瞎
这是 Genkit 最让我惊喜的功能。
开发大模型应用有一个普遍的痛点:你不知道模型到底"看到了什么",也不知道 Tool 有没有被调用,调用时传了什么参数、返回了什么。出问题了只能靠猜。
Genkit 的 Dev UI 直接把这些信息可视化了。
启动方式
首先全局安装 CLI(只需安装一次):
bash
npm install -g genkit-cli
然后用 Genkit 启动你的 Go 服务:
bash
genkit start -- go run main.go
浏览器会自动打开 http://localhost:4000。
你能在 Dev UI 里做什么
直接在浏览器里测试 Flow: 找到 assistantFlow,输入任意问题,点击运行,不需要 Postman 或写测试代码。
查看完整的调用链 Trace: 每次运行都会生成一条 Trace 记录,展示成瀑布图。你能精确看到:
- 第 1 步:模型收到了什么请求(完整的 messages JSON)
- 第 2 步:模型决定调用哪个工具,传了什么参数
- 第 3 步:工具返回了什么数据
- 第 4 步:模型基于工具结果生成了最终回复
- 每一步精确到毫秒的耗时
Token 消耗统计: 每次调用的输入 Token、输出 Token、是否命中缓存,方便你评估成本和优化 Prompt。
七、Dotprompt:把提示词从代码里解放出来
随着项目复杂度提升,你会越来越讨厌把提示词硬编码在 Go 字符串里——改个措辞要重新编译,多人协作时 Prompt 的修改和代码逻辑混在一起,很难管理。
Genkit 提供了 Dotprompt,让提示词独立成文件。
格式示例
创建文件 prompts/support.prompt:
---
model: googleai/gemini-3.8-flash
config:
temperature: 0.5
maxOutputTokens: 1024
input:
schema:
userName: string
issue: string
---
你是一名专业的客服助手,态度耐心、回答简洁。
用户名:{{userName}}
用户反馈的问题:{{issue}}
请分析问题的可能原因,并提供 2-3 个解决方案,每个方案用一句话说明步骤。
在 Go 代码里加载和执行
go
// 在初始化 Genkit 时指定 prompt 文件目录
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithPromptDir("./prompts"),
)
// 加载并执行,不需要重新编译 Go 代码
prompt := genkit.LookupPrompt(g, "support")
resp, err := prompt.Execute(ctx, map[string]any{
"userName": "张三",
"issue": "App 登录时提示网络错误,但我的网络是正常的",
})
fmt.Println(resp.Text())
这带来的好处很实际: 产品经理或算法同学可以直接修改 .prompt 文件,不需要懂 Go,不需要改业务代码,提交 PR 即可上线。
八、集成到现有 Go 项目的建议与注意事项
8.1 初始化放在哪里
genkit.Init 返回的 *genkit.Genkit 实例应该在服务启动时初始化一次,然后在各个 Handler 里复用。可以存在包级变量、全局配置结构或通过依赖注入传入,选哪种取决于你们项目的现有风格。
go
// 示例:在独立的 ai 包里初始化
package genai
import (
"context"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/googlegenai"
)
var G *genkit.Genkit
func Init(ctx context.Context, apiKey, model string) {
G = genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{APIKey: apiKey}),
genkit.WithDefaultModel("googleai/" + model),
)
registerTools() // 在这里统一注册所有 Tool
}
8.2 Tool 的安全边界
给大模型提供的工具要特别注意权限控制:
- 查询操作只读:数据库 Tool 应该只允许 SELECT,永远不要把 DELETE/UPDATE 直接暴露给模型
- 限制返回量:查询结果加 Limit,防止大量数据塞满上下文窗口,浪费 Token
- 高危操作需人工确认:涉及资金、发送消息、修改关键配置等操作,应该让模型返回"操作方案",由用户确认后再执行,而不是直接执行
8.3 处理大模型的响应延迟
大模型推理通常需要 1-5 秒,对用户来说等待感很强。有两种常用方案:
流式输出(Streaming): 边生成边返回,用户看到"打字机效果",首字时间大幅缩短。
go
err = genkit.Generate(ctx, g,
ai.WithPrompt(userMessage),
ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
// 每生成一个 chunk 就推给前端(通过 SSE 或 WebSocket)
fmt.Print(chunk.Text())
return nil
}),
)
异步处理: 对于不需要实时返回的场景(比如对接飞书/钉钉机器人),HTTP 收到消息后立即返回 200,用 goroutine 异步处理后通过机器人 API 主动发消息,避免平台超时重试。
8.4 上下文与多轮对话
Genkit 本身不帮你存对话历史,这是你的业务逻辑要管的事情。通常做法是按用户或会话 ID 在 Redis 里缓存最近 N 轮消息,每次请求时带上:
go
history := sessionStore.Get(sessionID) // 从 Redis 取历史消息
resp, err := genkit.GenerateText(ctx, g,
ai.WithMessages(history...), // 注入历史上下文
ai.WithPrompt(newUserMessage),
)
sessionStore.Append(sessionID, newUserMessage, resp) // 存回去
九、总结
Genkit 不是一个用来展示 Demo 的玩具,它是认真做 AI 应用工程化的框架。从模型 Plugin 的插拔设计,到 Tool Calling 的自动闭环,再到 Dev UI 带来的调试体验,能感受到这是有真实工程痛点积累沉淀下来的产品。
对 Go 开发者来说,最大的价值可能是:你不需要另起炉灶学一套 Python 全家桶,在你熟悉的语言和工程体系里,就能把大模型这块接好、用好、维护好。
如果你想开始试,最简单的路径是:
go get github.com/firebase/genkit/go@latest把依赖装上- 去 aistudio.google.com 申请一个免费的 Gemini API Key
- 把本文第五节的代码跑起来
- 用
genkit start -- go run main.go打开 Dev UI 看看
然后你就会开始想——我现在手头的哪个功能,可以用这个方式加一个 AI 能力进去?
参考资料
- 官方文档:genkit.dev
- 社区插件集合:github.com/BloomLabsInc/genkit-plugins
- 模型申请(免费):Google AI Studio


评论交流