随着 LLM(大型语言模型)和相关工具如嵌入模型的能力在过去一年中显著增长,越来越多的开发者正在考虑将 LLM 集成到他们的应用程序中。
由于 LLM 通常需要专用硬件和大量计算资源,它们最常被打包为提供 API 访问的网络服务。这就是领先的 LLM 如 OpenAI 或 Google Gemini API 的工作方式;即使是像 Ollama[2] 这样的自行运行 LLM 的工具也将 LLM 包装在 REST API 中以供本地使用。此外,在应用程序中利用 LLM 的开发人员通常需要辅助工具,如向量数据库,这些工具也最常部署为网络服务。
换句话说,基于 LLM 的应用程序很像其他现代云原生应用程序:它们需要出色的 REST 和 RPC 协议支持、并发性和性能。而这恰好是 Go 擅长的领域,使其成为编写基于 LLM 的应用程序的绝佳语言。
这篇博文通过一个使用 Go 构建简单 LLM 应用程序的示例进行讲解。它首先描述了演示应用程序要解决的问题,然后展示了几个变体,这些变体都完成相同的任务,但使用不同的包来实现。本文所有演示的代码 可在线获取[3] 。
用于问答的 RAG 服务器
一种常见的基于 LLM 的应用技术是 RAG - 检索增强生成[4] 。RAG 是为特定领域交互定制 LLM 知识库的最可扩展方式之一。
我们将用 Go 构建一个 RAG 服务器。这是一个为用户提供两种操作的 HTTP 服务器:
- 向知识库添加文档
- 向 LLM 询问有关此知识库的问题
在典型的真实场景中,用户会向服务器添加一组文档,然后继续提问。例如,公司可以用内部文档填充 RAG 服务器的知识库,并使用它为内部用户提供基于 LLM 的问答功能。
下面是一个图表,显示了我们的服务器与外部世界的交互:
除了用户发送 HTTP 请求(上述两个操作)外,服务器还与以下内容交互:
- 嵌入模型,用于计算提交文档和用户问题的 向量嵌入[5] 。
- 向量数据库,用于高效存储和检索嵌入。
- LLM,用于根据从知识库收集的上下文提问。
具体来说,服务器向用户公开两个 HTTP 端点:
/add/: POST {"documents": [{"text": "..."}, {"text": "..."}, ...]}
: 向服务器提交一系列文本文档,以添加到其知识库中。对于此请求,服务器:
- 使用嵌入模型为每个文档计算向量嵌入。
- 将文档及其向量嵌入存储在向量数据库中。
/query/: POST {"content": "..."}
: 向服务器提交问题。对于此请求,服务器:
- 使用嵌入模型计算问题的向量嵌入。
- 使用向量数据库的相似性搜索在知识数据库中找到与问题最相关的文档。
- 使用简单的提示工程,用步骤(2)中找到的最相关文档作为上下文重新表述问题,并将其发送给 LLM,然后将其答案返回给用户。
我们的演示使用的服务有:
- Google Gemini API[6] 用于 LLM 和嵌入模型。
- Weaviate[7] 用于本地托管的向量数据库;Weaviate 是一个 用 Go 实现[8] 的开源向量数据库。
用其他等效服务替换这些应该非常简单。事实上,这正是服务器的第二和第三个变体所要做的!我们将从第一个变体开始,它直接使用这些工具。
直接使用 Gemini API 和 Weaviate
Gemini API 和 Weaviate 都有方便的 Go SDK(客户端库),我们的第一个服务器变体直接使用这些。这个变体的完整代码在 这个目录中[9] 。
我们不会在这篇博文中重现全部代码,但在阅读时请记住以下几点:
结构: 代码结构对任何编写过 Go HTTP 服务器的人来说都很熟悉。Gemini 和 Weaviate 的客户端库被初始化,客户端存储在传递给 HTTP 处理程序的状态值中。
路由注册: 使用 Go 1.22 引入的 路由增强功能[10] ,我们服务器的 HTTP 路由设置非常简单:
http.HandleFunc("POST /add/", rs.handleAdd) http.HandleFunc("POST /query/", rs.handleQuery)
并发: 我们服务器的 HTTP 处理程序通过网络访问其他服务并等待响应。这对 Go 来说不是问题,因为每个 HTTP 处理程序都在自己的 goroutine 中并发运行。这个 RAG 服务器可以处理大量并发请求,每个处理程序的代码都是线性和同步的。
批处理 API: 由于 /add/
请求可能提供大量文档添加到知识库,服务器利用嵌入(embModel.BatchEmbedContents
)和 Weaviate 数据库(rs.wvClient.Batch
)的_批处理 API_ 来提高效率。
使用 LangChain for Go
我们的第二个 RAG 服务器变体使用 LangChainGo 来完成相同的任务。
LangChain[11] 是一个用于构建基于 LLM 的应用程序的流行 Python 框架。 LangChainGo[12] 是它的 Go 等效版本。该框架有一些工具可以从模块化组件构建应用程序,并在通用 API 中支持许多 LLM 提供商和向量数据库。这允许开发人员编写可能适用于任何提供商的代码,并非常容易地更改提供商。
这个变体的完整代码在 这个目录中[13] 。阅读代码时,你会注意到两件事:
首先,它比之前的变体稍短。LangChainGo 负责将向量数据库的完整 API 包装在通用接口中,初始化和处理 Weaviate 所需的代码更少。
其次,LangChainGo API 使切换提供商变得相当容易。假设我们想用另一个向量数据库替换 Weaviate;在我们之前的变体中,我们必须重写所有与向量数据库接口的代码以使用新的 API。使用像 LangChainGo 这样的框架,我们不再需要这样做。只要 LangChainGo 支持我们感兴趣的新向量数据库,我们应该能够只替换服务器中的几行代码,因为所有数据库都实现了 通用接口[14] :
// 使用 Weaviate store, err := weaviate.New( weaviate.WithScheme("http"), weaviate.WithHost("localhost:8080"), ) // 使用 Pinecone store, err := pinecone.New( pinecone.WithAPIKey(os.Getenv("PINECONE_API_KEY")), pinecone.WithEnvironment(os.Getenv("PINECONE_ENVIRONMENT")), pinecone.WithIndexName("my-index"), )
使用 Genkit for Go
今年早些时候,Google 推出了 Genkit for Go[15] - 一个用于构建基于 LLM 的应用程序的新开源框架。Genkit 与 LangChain 有一些共同特征,但在其他方面有所不同。
与 LangChain 一样,它提供了可由不同提供商实现的通用接口(作为插件),从而使从一个切换到另一个变得更简单。然而,它并不试图规定不同 LLM 组件如何交互;相反,它专注于生产功能,如提示管理和工程,以及与集成开发工具的部署。
我们的第三个 RAG 服务器变体使用 Genkit for Go 来完成相同的任务。它的完整代码在 这个目录中[16] 。
这个变体与 LangChainGo 的变体非常相似 - 使用 LLM、嵌入器和向量数据库的通用接口而不是直接的提供商 API,使得从一个切换到另一个变得更容易。此外,使用 Genkit 将基于 LLM 的应用程序部署到生产环境中要容易得多;我们没有在我们的变体中实现这一点,但如果你感兴趣,可以阅读 文档[17] 。
总结 - 使用 Go 构建基于 LLM 的应用程序
本文中的示例仅仅是在 Go 中构建基于 LLM 的应用程序的可能性的一小部分。它展示了用相对较少的代码构建强大的 RAG 服务器是多么简单;最重要的是,由于一些基本的 Go 特性,这些示例具有相当高的生产就绪度。
使用 LLM 服务通常意味着向网络服务发送 REST 或 RPC 请求,等待响应,根据响应向其他服务发送新请求,依此类推。Go 在所有这些方面都表现出色,提供了出色的工具来管理并发性和处理网络服务的复杂性。
此外,Go 作为云原生语言的出色性能和可靠性使其成为实现 LLM 生态系统更基础构建块的自然选择。一些例子包括 Ollama[1] 、 LocalAI[18] 、 Weaviate[6] 或 Milvus[19] 等项目。
参考链接
- Go 博客: https://go.dev/blog/
- Ollama: https://ollama.com/
- 可在线获取: https://github.com/golang/example/tree/master/ragserver
- 检索增强生成: https://en.wikipedia.org/wiki/Retrieval-augmented_generation
- 向量嵌入: https://en.wikipedia.org/wiki/Sentence_embedding
- Google Gemini API: https://ai.google.dev/
- Weaviate: https://weaviate.io/
- 用 Go 实现: https://github.com/weaviate/weaviate
- 这个目录中: https://github.com/golang/example/tree/master/ragserver/ragserver
- 路由增强功能: https://go.dev/blog/routing-enhancements
- LangChain: https://www.langchain.com/
- LangChainGo: https://github.com/tmc/langchaingo
- 这个目录中: https://github.com/golang/example/tree/master/ragserver/ragserver-langchaingo
- 通用接口: https://pkg.go.dev/github.com/tmc/langchaingo@v0.1.12/vectorstores#VectorStore
- Genkit for Go: https://developers.googleblog.com/en/introducing-genkit-for-go-build-scalable-ai-powered-apps-in-go/
- 这个目录中: https://github.com/golang/example/tree/master/ragserver/ragserver-genkit
- 文档: https://firebase.google.com/docs/genkit-go/get-started-go
- LocalAI: https://localai.io/
- Milvus: https://zilliz.com/what-is-milvus