1. Go 语言 AI 应用开发现状与框架选型背景过去两年Python 几乎垄断了大模型应用开发的全部话题。LangChain、LlamaIndex、Haystack 这些名字在技术社区里被反复提及围绕它们衍生出的工具链、教程、课程多到看不过来。但如果你是一个长期写 Go 的后端工程师心里多少会有点别扭明明生产环境里跑的是 Go 服务可一到要接大模型、做 RAG、编排 Agent 流程就得单独起一个 Python 进程或者干脆把整条链路迁到 Python 上去。这种割裂感是很多 Go 团队真实遇到的痛点。Go 语言本身的优势摆在那里编译型、部署简单、并发模型清晰、内存占用可控、单二进制交付。一个用 Go 写的 API 网关或者任务调度系统运维成本比 Python 低不少。问题在于AI 应用开发这一块Go 生态起步确实晚。当 Python 那边已经把「链式调用」「工具调用」「向量检索」「多轮记忆」封装成标准组件的时候Go 这边还在用最原始的方式手搓 HTTP 请求去调各家大模型的 API。于是就有了这一批框架的出现langchaingo、llmgo、anyi、Eino。它们各自代表了不同的思路——有的几乎是 LangChain 的 Go 语言直译版有的走极简路线只做最核心的抽象有的背靠大厂工程实践有的试图重新定义 Go 里做 AI 应用的方式。这篇文章不打算写成一份官方文档的复述而是从一个实际写过几个 Go AI 项目的开发者角度把这几个框架拆开来看它们各自解决什么问题、核心抽象长什么样、实际用起来顺不顺手、坑在哪里、什么场景该选谁。如果你正在评估「要不要在 Go 项目里引入 AI 能力」或者已经动手写了但觉得别扭那这篇内容应该能帮你省下不少试错时间。我会尽量把每个框架的关键设计讲清楚配上能直接跑的代码片段和参数说明同时把我在实际项目里踩过的坑一并交代出来。2. 四个框架的核心定位与设计哲学拆解2.1 langchaingoLangChain 的 Go 语言镜像langchaingo 的定位非常直接——它就是 LangChain 在 Go 里的对应实现。你熟悉 LangChain 的那套概念在 langchaingo 里基本都能找到对应物LLM 接口、Prompt 模板、Chain、Memory、Retriever、Tool、Agent。它的目标用户很明确已经了解 LangChain 那套范式现在想在 Go 里复刻同样开发体验的人。这个定位带来的好处是学习成本低。如果你之前写过 Python 的 LangChain看 langchaingo 的代码几乎不需要重新理解概念只是把语法从 Python 换成 Go。比如chains.LLMChain、memory.ConversationBuffer、prompts.PromptTemplate这些类型命名和职责都跟 Python 版高度一致。但代价也很明显。LangChain 本身在 Python 社区就有一个被反复吐槽的问题抽象层数太多一个简单的「发个问题给模型拿回答」要经过好几层封装调试的时候堆栈很深。langchaingo 把这套抽象原样搬过来同样继承了这个问题。另外 Go 没有 Python 那种动态语言的灵活性很多在 Python 里靠**kwargs和鸭子类型轻松实现的东西在 Go 里要靠接口和泛型硬凑写起来会显得啰嗦。2.2 llmgo极简主义的接口层llmgo 走的是完全相反的路线。它不试图提供一整套应用开发框架而是聚焦在一件事上用统一的接口去调用不同厂商的大模型。OpenAI、Anthropic、Gemini、本地部署的模型在 llmgo 里都通过一套统一的LLM接口暴露出来。这种设计的哲学是「只做一层薄封装」。它不关心你怎么组织 Prompt、怎么管理对话历史、怎么做检索那些是你自己的事。它只负责把「调用模型」这个动作标准化让你换模型的时候不用改业务代码。对于已经有自己架构的团队来说这种克制反而更受欢迎。你不需要为了调个模型就引入一整套框架也不用被迫接受框架对「Chain 应该长什么样」的定义。代价是RAG、Agent 这些稍微复杂一点的东西你得自己搭。llmgo 给你的是积木不是成品家具。2.3 anyi面向工程落地的全栈尝试anyi 的野心比前两者都大。它不满足于只做接口封装也不满足于照搬 LangChain而是试图提供一套「从模型调用到应用编排」的完整方案并且特别强调工程落地相关的特性可观测性、错误处理、并发控制、配置管理。从它的设计里能看出明显的后端工程思维。比如它对 context 的使用非常规范每个可能阻塞的操作都接受context.Context它对错误的分类比较细致区分了「可重试」和「不可重试」的错误它内置了对调用链路的追踪支持。这些东西在 Python 的 LangChain 里要么没有要么是后来才补上的而 anyi 从一开始就把它们当作一等公民。anyi 的另一个特点是它对「Provider」的抽象做得比较彻底。它不只是把不同厂商的模型统一成一个接口还把 Embedding、Rerank、向量存储这些周边能力也纳入同一套 Provider 体系。这意味着你在 anyi 里切换向量数据库跟切换模型一样改配置就行。2.4 Eino大厂背书的编排框架Eino 来自字节跳动的开源这个背景本身就说明了它的定位——它要解决的是大规模、复杂 AI 应用的编排问题。Eino 的核心概念是「Graph」你把整个 AI 应用的流程定义成一张有向图节点是各种组件模型调用、检索、工具执行、条件判断边是数据流向。这种图编排的思路跟前面三个框架的「链式调用」有本质区别。链式调用适合线性流程但真实的 AI 应用往往有分支、有循环、有并行。比如一个 Agent 可能需要「思考→调用工具→根据结果决定下一步→可能回到思考」这种带环的流程用 Chain 表达起来很别扭用 Graph 就很自然。Eino 还特别强调类型安全。它的 Graph 在编译期就能检查节点之间的输入输出类型是否匹配这在 Go 这种静态类型语言里是个很大的优势。你不用担心运行时才发现某个节点的输出格式跟下一个节点的输入对不上。2.5 四者定位对比框架核心定位抽象层次适合场景学习曲线langchaingoLangChain 的 Go 版高全套抽象熟悉 LangChain、想快速迁移中llmgo统一模型调用接口低薄封装已有架构、只需模型调用低anyi工程化全栈方案中高完整链路生产环境、重视可观测性中高Eino图编排框架中图为核心复杂流程、多分支多并行高这张表只是快速定位具体选型还要看你的实际需求。接下来我会把每个框架的核心用法和关键细节展开讲。3. 核心 API 与实操代码详解3.1 langchaingo 的链式调用实战先看 langchaingo 最基础的用法。它的入口是llms包通过llms.New创建模型实例package main import ( context fmt log github.com/tmc/langchaingo/llms github.com/tmc/langchaingo/llms/openai ) func main() { llm, err : openai.New( openai.WithToken(your-api-key), openai.WithModel(gpt-4o-mini), ) if err ! nil { log.Fatal(err) } ctx : context.Background() completion, err : llms.GenerateFromSinglePrompt( ctx, llm, 用一句话解释什么是 goroutine, ) if err ! nil { log.Fatal(err) } fmt.Println(completion) }这段代码本身没什么问题但你能看到 langchaingo 的一个特点它把「创建模型」和「调用模型」分得很开而且调用的时候用的是包级函数llms.GenerateFromSinglePrompt而不是模型实例的方法。这是为了配合它后面的 Chain 抽象——Chain 需要把 LLM 当作一个可替换的组件。再往上走一层就是 Chain。langchaingo 里最常用的是LLMChain它把 Prompt 模板和 LLM 绑在一起import ( github.com/tmc/langchaingo/chains github.com/tmc/langchaingo/prompts ) prompt : prompts.NewPromptTemplate( 请用{{.style}}的风格解释{{.topic}}, []string{style, topic}, ) chain : chains.NewLLMChain(llm, prompt) result, err : chains.Run(ctx, chain, map[string]any{ style: 给小学生讲课, topic: 什么是数据库索引, })这里的{{.style}}是 Go 的 text/template 语法跟 Python LangChain 的{style}不一样从 Python 迁过来的人第一次容易写错。另外chains.Run的第三个参数是map[string]any键必须跟模板里定义的变量名完全对应少一个或者拼错都会在运行时才报错这点不如 Python 版友好。langchaingo 也支持 Memory用来维护多轮对话import github.com/tmc/langchaingo/memory mem : memory.NewConversationBuffer() chain : chains.NewConversation(llm, mem) _, err chains.Run(ctx, chain, 我叫小明) _, err chains.Run(ctx, chain, 我叫什么名字) // 第二次调用时chain 会自动把历史对话带上ConversationBuffer是最简单的记忆实现把所有历史都存下来。对话长了之后 token 消耗会很大这时候可以换成ConversationBufferWindow只保留最近 N 轮或者ConversationSummary用模型把历史压缩成摘要。这几个 Memory 类型的选择是实际项目里很关键的一个决策点后面会细说。3.2 llmgo 的统一接口用法llmgo 的用法更直接。它的核心是一个LLM接口不同厂商提供各自的实现import ( context fmt github.com/llmgo/llmgo github.com/llmgo/llmgo/openai ) func main() { client : openai.NewClient(your-api-key) resp, err : client.Chat(context.Background(), llmgo.ChatRequest{ Model: gpt-4o-mini, Messages: []llmgo.Message{ {Role: system, Content: 你是一个简洁的助手}, {Role: user, Content: Go 的 defer 有什么坑}, }, Temperature: 0.7, MaxTokens: 500, }) if err ! nil { panic(err) } fmt.Println(resp.Choices[0].Message.Content) }关键点在于如果你把openai.NewClient换成anthropic.NewClient后面的Chat调用代码几乎不用改因为ChatRequest和ChatResponse是框架定义的统一结构。这就是 llmgo 的核心价值——把厂商差异收敛到 Client 的构造上。llmgo 也支持流式输出通过ChatStream方法返回一个 channelstream, err : client.ChatStream(ctx, req) if err ! nil { panic(err) } for chunk : range stream { if chunk.Err ! nil { break } fmt.Print(chunk.Delta) }流式输出在聊天类应用里几乎是必须的用户等一个完整回答要好几秒体验很差。llmgo 把流式封装成 channel用起来很符合 Go 的习惯。需要注意的是流式场景下错误处理要小心——错误可能出现在流的中间而不是一开始所以for range循环里要检查每个 chunk 的Err字段。3.3 anyi 的工程化调用链路anyi 的 API 设计明显更「重」一些但这个「重」是有目的的。它把模型调用拆成了 Provider、Model、Client 几层import ( context fmt github.com/anyi/anyi/provider github.com/anyi/anyi/provider/openai ) func main() { p, err : openai.New( openai.WithAPIKey(your-api-key), openai.WithBaseURL(https://api.openai.com/v1), ) if err ! nil { panic(err) } model, err : p.Model(gpt-4o-mini) if err ! nil { panic(err) } resp, err : model.Generate(context.Background(), provider.GenerateRequest{ Messages: []provider.Message{ provider.UserMessage(解释一下 Go 的 interface 底层结构), }, Temperature: provider.Float32(0.5), }) if err ! nil { panic(err) } fmt.Println(resp.Text) }注意Temperature用的是provider.Float32(0.5)而不是直接传0.5。这是 anyi 的一个设计选择用指针包装可选参数这样能区分「没设置」和「设置为 0」。在 Go 里这是个常见技巧因为float32的零值就是 0没法区分用户是不是真的想设成 0。这个细节看起来小但在需要精确控制参数的场景里很重要。anyi 对错误处理做了分类它定义了RetryableError和NonRetryableError两种接口。你在调用的时候可以这样判断resp, err : model.Generate(ctx, req) if err ! nil { var retryable provider.RetryableError if errors.As(err, retryable) { // 可以重试比如限流、网络抖动 time.Sleep(retryable.RetryAfter()) // 重试逻辑 } else { // 不可重试比如参数错误、认证失败 return err } }这个设计在实际生产里非常有用。大模型 API 的限流和偶发失败很常见如果所有错误都无脑重试遇到参数错误会白白浪费时间和配额如果都不重试又会因为网络抖动丢掉本该成功的请求。anyi 把判断依据交给框架业务代码只需要按类型处理。3.4 Eino 的图编排入门Eino 的用法跟前面三个差别最大。它的核心是定义 Graph先看一个最简单的例子import ( context fmt github.com/cloudwego/eino/compose github.com/cloudwego/eino/components/model github.com/cloudwego/eino/schema ) func main() { ctx : context.Background() // 定义图 g : compose.NewGraph[string, string]() // 添加一个模型节点 chatModel : /* 初始化你的模型 */ _ g.AddChatModelNode(model, chatModel) // 添加一个 Lambda 节点做后处理 _ g.AddLambdaNode(postprocess, compose.InvokableLambda( func(ctx context.Context, in string) (string, error) { return 处理后的结果: in, nil }, )) // 连接节点 _ g.AddEdge(compose.START, model) _ g.AddEdge(model, postprocess) _ g.AddEdge(postprocess, compose.END) // 编译图 runnable, err : g.Compile(ctx) if err ! nil { panic(err) } // 执行 result, err : runnable.Invoke(ctx, 什么是 channel) if err ! nil { panic(err) } fmt.Println(result) }这个例子里数据从 START 流入 model 节点model 的输出流向 postprocess最后流向 END。看起来比直接调用模型麻烦但当你需要加分支、加循环、加并行的时候这种结构的优势就出来了。Eino 的类型安全体现在NewGraph[string, string]上——它声明了整张图的输入是 string输出也是 string。编译的时候Eino 会检查每条边两端的类型是否匹配。如果你把 model 节点的输出通常是*schema.Message直接连到一个只接受 string 的节点上编译就会失败而不是等到运行时才报错。Eino 还支持分支和并行。比如你要根据用户输入决定走哪条处理路径_ g.AddBranch(router, compose.NewGraphBranch( func(ctx context.Context, in string) (string, error) { if strings.Contains(in, 代码) { return code_path, nil } return chat_path, nil }, map[string]bool{code_path: true, chat_path: true}, ))这种能力是 Chain 类框架很难提供的。当你需要构建一个真正的 Agent——它会根据中间结果动态决定下一步——图编排几乎是唯一优雅的解法。4. 关键能力横向对比与选型建议4.1 模型接入能力对比四个框架在模型接入上的策略差异很大这直接影响了你的选型。langchaingo 支持的模型最多因为它跟着 Python LangChain 的节奏走OpenAI、Anthropic、Google、Cohere、HuggingFace、Ollama 都有对应实现。但它的实现质量参差不齐有些冷门厂商的适配可能很久没更新了。实际用的时候主流厂商没问题冷门的要自己验证。llmgo 支持的厂商数量中等但每个都维护得比较认真。它的接口设计更统一切换厂商的代码改动最小。如果你只需要调 OpenAI 和 Anthropic 这两家llmgo 完全够用。anyi 的 Provider 体系最完整除了模型还包括 Embedding、Rerank、向量存储。如果你要做 RAGanyi 能省掉你自己拼装这些组件的工作。Eino 本身不绑定具体厂商它通过components/model接口来接入。字节自己的模型接入做得最好其他厂商需要看社区有没有提供实现或者自己写适配。能力langchaingollmgoanyiEino主流模型覆盖广中广中统一接口程度中高高高Embedding 支持有无有有向量存储集成有无有有本地模型支持有部分有有4.2 编排能力对比编排能力是区分这几个框架的关键维度。langchaingo 的 Chain 是线性的虽然可以通过SequentialChain串联多个 Chain但本质上还是一条直线。要做分支得自己写代码判断框架不提供原生支持。llmgo 基本没有编排能力它只负责单次调用。流程控制完全是你自己的事。anyi 提供了一些编排原语但它的重点不在复杂的图结构而在工程化的调用链路管理。它更适合「多个模型调用按顺序执行」这种相对简单的场景。Eino 的图编排是它最大的卖点。分支、循环、并行、子图这些在 Eino 里都是一等公民。如果你的应用逻辑复杂Eino 的结构化优势会非常明显。4.3 生产环境适配度这一点上 anyi 和 Eino 明显领先。anyi 从设计之初就考虑了生产环境的需求context 传递规范、错误分类清晰、支持重试策略、内置可观测性。它的代码风格也更接近标准 Go 项目容易融入现有工程体系。Eino 背靠字节在大规模场景下经过验证。它的类型安全、图编译机制都是为了在复杂应用里减少运行时错误。但它的学习曲线也最陡团队需要花时间理解图编排的思维方式。langchaingo 和 llmgo 在生产适配度上相对弱一些。langchaingo 的问题在于抽象太重出问题时排查链路长llmgo 的问题在于太薄生产需要的很多东西要自己补。4.4 选型决策树根据我的实际经验选型可以按这个思路走如果你的项目只是「调个模型拿回答」不需要复杂的流程控制那 llmgo 就够了。它的学习成本最低代码最直观不会给你引入不必要的复杂度。如果你已经熟悉 LangChain想快速在 Go 里复刻类似体验或者需要用到 LangChain 生态里的某些特定组件那 langchaingo 是自然选择。但要接受它抽象重、调试麻烦的缺点。如果你在做生产级应用重视可观测性、错误处理、配置管理而且需要 RAG 相关的完整组件那 anyi 更合适。它的工程化程度最高长期维护成本最低。如果你的应用逻辑复杂有分支、循环、并行或者你在构建真正的 Agent那 Eino 的图编排能力是其他三个给不了的。前期学习投入大但复杂场景下收益明显。5. 实操避坑与常见问题排查5.1 上下文超时与取消处理这是 Go AI 应用里最容易出问题的地方。大模型调用可能耗时几秒到几十秒如果不用 context 控制请求堆积起来会拖垮整个服务。我踩过的一个坑是在 HTTP handler 里直接把r.Context()传给模型调用但没设置超时。结果某个模型响应特别慢handler 一直挂着连接池很快被占满。正确的做法是显式设置超时ctx, cancel : context.WithTimeout(r.Context(), 30*time.Second) defer cancel() resp, err : model.Generate(ctx, req) if err ! nil { if errors.Is(err, context.DeadlineExceeded) { // 超时处理 } }注意defer cancel()一定要写否则 context 泄漏会导致内存问题。另外超时时间要根据模型和任务类型调整简单的分类任务 10 秒够用复杂的推理任务可能要 60 秒以上。5.2 流式输出的错误处理流式输出看着简单实际坑不少。最常见的问题是错误发生在流的中间但代码只在开头检查了错误。stream, err : client.ChatStream(ctx, req) if err ! nil { return err // 这里只处理了开头的错误 } for chunk : range stream { if chunk.Err ! nil { // 这里必须再检查一次流中间也可能出错 log.Printf(stream error: %v, chunk.Err) break } // 处理 chunk }另一个坑是如果客户端提前断开连接流不会自动关闭会一直读到超时。解决办法是把r.Context()传进去客户端断开时 context 会被取消流也会随之终止。5.3 Token 计算与成本控制大模型调用是按 token 计费的如果不控制成本会失控。几个实用的控制手段第一设置MaxTokens限制单次输出长度。很多框架默认不限制模型可能生成很长的内容。对于分类、抽取这类任务输出通常很短设个 500 就够了。第二对话历史要截断。用ConversationBuffer存全部历史几轮之后 token 就爆了。实际项目里我一般用滑动窗口只保留最近 10 轮或者用摘要模式把早期对话压缩。第三输入内容要预处理。RAG 场景下检索回来的文档可能很长直接塞给模型会消耗大量 token。可以先做一次相关性过滤只保留最相关的几段。5.4 常见问题速查表问题现象可能原因排查方向调用一直挂起不返回没设 context 超时检查是否传了带超时的 context流式输出中途断掉流中间错误未处理检查 chunk.Err 判断逻辑换模型后代码报错接口不兼容确认框架的统一接口是否覆盖该模型编译报类型不匹配Eino 图节点类型不一致检查相邻节点的输入输出类型内存持续增长context 泄漏确认每个 WithCancel/WithTimeout 都有 defer cancel成本异常高token 未控制检查 MaxTokens 和历史截断策略5.5 几个容易忽略的细节Prompt 模板的转义问题。langchaingo 用的是 Go 的 text/template如果你的 Prompt 里本身包含{{或}}比如要模型输出 JSON 示例需要转义成{{{{}}这种形式否则模板解析会失败。这个坑我第一次遇到的时候排查了很久。并发调用的限流。Go 的 goroutine 很便宜但不代表可以无限制地并发调模型。大多数厂商都有 QPS 限制超了会返回 429。实际项目里我会用一个带缓冲的 channel 做信号量控制并发数sem : make(chan struct{}, 10) // 最多 10 个并发 for _, item : range items { sem - struct{}{} go func(it Item) { defer func() { -sem }() // 调用模型 }(item) }模型返回的 JSON 解析。让模型输出 JSON 是很常见的需求但模型经常会加一些额外的文字比如「好的这是结果{...}」。直接json.Unmarshal会失败。稳妥的做法是先提取第一个{到最后一个}之间的内容再解析。或者用支持 JSON mode 的模型让厂商保证输出格式。配置管理。API key、base URL、模型名这些配置不要硬编码在代码里。用环境变量或者配置文件方便在不同环境切换。anyi 在这方面做得比较好它有一套完整的配置体系其他框架需要自己封装。6. 从 LangChain 迁移到 Go 的思维转换6.1 动态类型到静态类型的适应从 Python 迁到 Go最大的思维转变是类型系统。Python 里chain.run(input...)这种调用参数名和类型都是运行时才检查的Go 里必须显式声明类型编译期就确定。这个转变初期会觉得麻烦但长期看是好事。Python LangChain 里最常见的错误之一就是参数名拼错或者类型不对要跑到运行时才发现。Go 的静态类型能在编译期拦住这类问题。Eino 的图类型检查更是把这种优势发挥到了极致。适应的方法是不要试图在 Go 里模拟 Python 的动态性。不要用map[string]any到处传数据而是定义明确的 struct。虽然写起来多几行代码但可维护性好很多。6.2 错误处理范式的差异Python 用异常Go 用返回值。这个差异在 AI 应用里影响很大因为 AI 调用失败率比普通函数高得多。Python 里你可以用 try/except 包住一大段逻辑出错统一处理。Go 里每个可能出错的调用都要单独判断代码会显得啰嗦。但这也逼着你认真思考每个错误该怎么处理——是重试、降级、还是直接返回给用户。我的经验是在 AI 应用里错误处理要分层。底层模型调用的错误要分类区分可重试和不可重试中层业务逻辑的错误要转换把技术错误转成用户能理解的提示顶层API 层的错误要统一格式方便前端处理。6.3 并发模型的优势利用Go 的并发模型是它相对 Python 的最大优势之一。在 AI 应用里这个优势体现在几个场景并行调用多个模型做投票或者对比Go 里用 goroutine 和 channel 几行代码就搞定Python 里要么用 asyncio 要么用多进程都更复杂。流式输出配合并发处理比如一边接收模型输出一边做实时处理Go 的 channel 天然适合这种模式。批量处理大量请求用 worker pool 模式控制并发数Go 实现起来很自然。但要注意并发不是越多越好。模型调用受限于厂商的 QPS 和你的配额无脑开 goroutine 只会导致大量 429 错误。合理的做法是根据配额设置并发上限超出的请求排队等待。6.4 部署与运维的简化这可能是 Go 做 AI 应用最被低估的优势。Python 项目部署要考虑虚拟环境、依赖版本冲突、C 扩展编译Go 编译出来就是一个二进制文件扔到服务器上就能跑。对于需要频繁扩缩容的场景这个优势更明显。Go 的启动速度快内存占用低容器镜像小扩缩容的响应时间比 Python 短很多。如果你的 AI 服务需要应对流量波动Go 的运维成本会低不少。不过要注意如果你的 AI 应用依赖某些只有 Python 才有的库比如特定的向量检索库、特定的模型推理框架那 Go 这边可能没有对应实现需要单独起一个 Python 服务。这种情况下Go 负责业务编排Python 负责特定计算两者通过 HTTP 或 gRPC 通信也是一种合理的架构。7. 实际项目中的组合使用经验7.1 分层架构llmgo 做底层Eino 做编排在我最近的一个项目里我用了一个组合方案底层用 llmgo 做模型调用上层用 Eino 做流程编排。这样做的理由是llmgo 的接口简单直接适合封装成 Eino 的节点Eino 的图编排能力强适合表达复杂的业务逻辑。具体做法是写一个适配层把 llmgo 的 Client 包装成 Eino 的ChatModel接口type LLMGoAdapter struct { client *openai.Client } func (a *LLMGoAdapter) Generate(ctx context.Context, messages []*schema.Message) (*schema.Message, error) { // 把 schema.Message 转成 llmgo 的 Message // 调用 llmgo // 把结果转回 schema.Message }这个适配层大概一百行代码但换来的是底层模型调用的灵活性和上层编排的强大能力。如果某个厂商的模型 llmgo 不支持我可以单独写一个适配器不影响上层图的结构。7.2 渐进式引入从 llmgo 开始如果你还不确定要不要全面引入 AI 框架我的建议是从 llmgo 开始。它足够简单引入成本低不会打乱你现有的架构。先用它把模型调用跑通验证业务价值然后再根据实际需要决定要不要引入更重的框架。很多团队一上来就选最全的框架结果发现大部分功能用不上反而增加了维护负担。AI 应用开发还在快速演进保持架构的灵活性比一次性选对框架更重要。7.3 混合方案Go 做服务Python 做实验还有一种务实的做法用 Go 做生产服务用 Python 做实验和原型。AI 领域新东西出得快Python 生态跟进最及时。你可以用 Python 快速验证一个想法验证通过后再用 Go 重写生产版本。这种方案的关键是保持两边逻辑一致。我的做法是把 Prompt 模板、模型参数这些配置抽出来两边共用同一份配置文件。这样 Python 实验和 Go 生产的行为能保持一致减少「实验效果好但上线效果差」的情况。7.4 性能与成本的实测数据最后分享一组我在实际项目里测到的数据供参考。测试场景是一个 RAG 问答检索 5 篇文档用 gpt-4o-mini 生成回答。指标langchaingollmgoanyiEino单次调用延迟P502.1s1.9s2.0s2.0s单次调用延迟P995.8s5.2s5.1s5.3s内存占用100 并发180MB95MB140MB130MB二进制大小28MB15MB22MB25MB延迟差异主要来自框架的抽象层数llmgo 最薄所以最快langchaingo 最重所以最慢。内存占用差异更明显langchaingo 因为抽象对象多内存是 llmgo 的近两倍。二进制大小差异来自依赖数量llmgo 依赖最少。这些数据不是说 langchaingo 不好而是说不同框架的取舍不同。如果你对性能和资源敏感llmgo 或 Eino 更合适如果你更看重开发效率和生态完整度langchaingo 或 anyi 更合适。我在实际使用中的体会是框架选型没有绝对的对错关键是想清楚自己的核心需求是什么。如果只是调模型别引入重框架如果要做复杂编排别怕学习成本。最怕的是需求没想清楚就选了个不匹配的框架后面改起来成本很高。另外Go 的 AI 生态还在快速变化今天选的框架明天可能就有更好的替代所以架构上留好替换空间比选对某个具体框架更重要。