在AI大模型时代,LangChain已成为Python开发者构建LLM应用的首选框架。但对于Go开发者来说,有没有类似的工具呢?答案是肯定的——langchaingo,它是LangChain的Go语言实现,让Go开发者也能轻松构建强大的AI应用。
随着ChatGPT等大语言模型的普及,越来越多的应用开始集成AI能力。Python生态中的LangChain提供了丰富的组件来简化LLM应用开发,包括链式调用、工具调用、记忆管理等。然而,对于需要高性能、并发处理能力的企业级应用,Go语言往往是更优选择。
langchaingo正是为了填补这一空白而生,它将LangChain的核心概念引入Go生态,让开发者可以用熟悉的Go语言构建LLM应用。
langchaingo支持多种大语言模型,不仅限于OpenAI:
// 示例1:初始化OpenAI LLM
llm, err := openai.New(
openai.WithModel("gpt-4"),
openai.WithToken("your-api-key"),
)
// 示例2:初始化Ollama本地模型
ollamaLLM, err := ollama.New(
ollama.WithModel("llama2"),
)
链式调用是LangChain的核心概念,langchaingo同样支持。通过组合多个组件,可以构建复杂的应用流程。
// 示例:简单链式调用
package main
import (
"context"
"fmt"
"log"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/prompts"
)
func main() {
ctx := context.Background()
// 初始化LLM
llm, err := openai.New()
if err != nil {
log.Fatal(err)
}
// 创建提示模板
prompt := prompts.NewPromptTemplate(
"作为一位{name}专家,请回答以下问题:{question}",
[]string{"name", "question"},
)
// 填充模板
promptValues := map[string]any{
"name": "Go语言",
"question": "Go的协程是如何实现的?",
}
finalPrompt, err := prompt.Format(promptValues)
if err != nil {
log.Fatal(err)
}
// 生成回答
completion, err := llms.GenerateFromSinglePrompt(ctx, llm, finalPrompt)
if err != nil {
log.Fatal(err)
}
fmt.Println(completion)
}
Tools允许LLM调用外部功能,这是构建AI Agent的关键。
// 示例:定义自定义工具
package main
import (
"context"
"fmt"
"time"
"github.com/tmc/langchaingo/agents"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/tools"
)
// GetCurrentTime 获取当前时间工具
type GetCurrentTime struct{}
func (t *GetCurrentTime) Name() string {
return "get_current_time"
}
func (t *GetCurrentTime) Description() string {
return "获取当前系统时间,格式为YYYY-MM-DD HH:MM:SS"
}
func (t *GetCurrentTime) Call(_ context.Context, _ string) (string, error) {
return time.Now().Format("2006-01-02 15:04:05"), nil
}
func main() {
ctx := context.Background()
llm, err := openai.New()
if err != nil {
panic(err)
}
// 创建工具列表
timeTool := tools.Tool(&GetCurrentTime{})
// 创建Agent
agent := agents.New(
llm,
[]tools.Tool{timeTool},
)
// 运行Agent
answer, err := agents.Run(ctx, agent, "现在几点了?")
if err != nil {
panic(err)
}
fmt.Println(answer)
}
Memory组件让AI能够记住对话历史,维持上下文连贯性。
// 示例:使用对话记忆
package main
import (
"context"
"fmt"
"log"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/memory"
"github.com/tmc/langchaingo/prompts"
)
func main() {
ctx := context.Background()
llm, err := openai.New()
if err != nil {
log.Fatal(err)
}
// 创建对话记忆
mem := memory.NewChatMessageHistory()
// 添加对话历史
mem.AddAIMessage(ctx, "你好!我是Go语言助手。")
mem.AddUserMessage(ctx, "你能帮我写个HTTP服务器吗?")
// 获取历史消息
messages, err := mem.Messages(ctx)
if err != nil {
log.Fatal(err)
}
// 构建提示词
var promptText string
for _, msg := range messages {
switch msg.GetType() {
case llms.ChatMessageTypeHuman:
promptText += "User: " + msg.GetContent() + "\n"
case llms.ChatMessageTypeAI:
promptText += "Assistant: " + msg.GetContent() + "\n"
}
}
completion, err := llms.GenerateFromSinglePrompt(ctx, llm, promptText)
if err != nil {
log.Fatal(err)
}
fmt.Println(completion)
}
下面我们用langchaingo构建一个智能代码助手,它可以:
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/memory"
"github.com/tmc/langchaingo/schema"
)
// CodeAssistant 智能代码助手
type CodeAssistant struct {
llm llms.Model
memory *memory.SimpleMemory
}
func NewCodeAssistant(apiKey string) (*CodeAssistant, error) {
llm, err := openai.New(openai.WithToken(apiKey))
if err != nil {
return nil, err
}
return &CodeAssistant{
llm: llm,
memory: memory.NewSimpleMemory(),
}, nil
}
// Ask 询问问题并获取回答
func (ca *CodeAssistant) Ask(ctx context.Context, question string) (string, error) {
// 获取历史对话
history := ca.memory.GetMemory()
// 构建完整提示
prompt := fmt.Sprintf(`你是一个专业的Go语言开发助手。请根据用户的问题提供准确、简洁的回答。
历史对话:
%s
当前问题:%s
请用Go语言代码示例和清晰的解释来回答问题。`, history, question)
// 生成回答
completion, err := llms.GenerateFromSinglePrompt(ctx, ca.llm, prompt)
if err != nil {
return "", err
}
// 更新记忆
ca.memory.AddMemory(question + "\n" + completion)
return completion, nil
}
// ExplainCode 解释代码
func (ca *CodeAssistant) ExplainCode(ctx context.Context, code string) (string, error) {
prompt := fmt.Sprintf(`请解释以下Go代码的功能和关键点:
%s
请按以下格式回答:
1. 代码功能概述
2. 关键代码片段解释
3. 可能的改进建议`, code)
return llms.GenerateFromSinglePrompt(ctx, ca.llm, prompt)
}
// GenerateCode 生成代码
func (ca *CodeAssistant) GenerateCode(ctx context.Context, description string) (string, error) {
prompt := fmt.Sprintf(`请根据以下描述生成Go代码:
%s
要求:
1. 代码要完整可运行
2. 添加必要的错误处理
3. 添加注释说明关键逻辑
4. 代码要简洁高效`, description)
return llms.GenerateFromSinglePrompt(ctx, ca.llm, prompt)
}
func main() {
ctx := context.Background()
// 初始化代码助手
assistant, err := NewCodeAssistant("your-openai-api-key")
if err != nil {
log.Fatal(err)
}
// 示例1:询问问题
answer, err := assistant.Ask(ctx, "Go的context包有什么作用?")
if err != nil {
log.Fatal(err)
}
fmt.Println("回答:")
fmt.Println(answer)
// 示例2:生成代码
code, err := assistant.GenerateCode(ctx, "实现一个简单的HTTP服务器,支持GET和POST请求")
if err != nil {
log.Fatal(err)
}
fmt.Println("\n生成的代码:")
fmt.Println(code)
// 示例3:解释代码
codeToExplain := `
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-time.After(10 * time.Second):
fmt.Println("完成")
case <-ctx.Done():
fmt.Println("超时:", ctx.Err())
}
}`
explanation, err := assistant.ExplainCode(ctx, codeToExplain)
if err != nil {
log.Fatal(err)
}
fmt.Println("\n代码解释:")
fmt.Println(explanation)
}
特性 | langchaingo | Python LangChain | Java LangChain4j |
|---|---|---|---|
性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
并发能力 | 原生支持 | GIL限制 | 线程安全 |
学习曲线 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
生态系统 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
社区活跃度 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
部署便利性 | 单二进制 | 需要Python环境 | JVM依赖 |
合理使用并发
// 利用Go的并发能力处理多个请求
func ProcessConcurrent(ctx context.Context, questions []string) []string {
results := make([]string, len(questions))
var wg sync.WaitGroup
for i, q := range questions {
wg.Add(1)
go func(idx int, question string) {
defer wg.Done()
result, _ := assistant.Ask(ctx, question)
results[idx] = result
}(i, q)
}
wg.Wait()
return results
}
使用Context控制超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
answer, err := assistant.Ask(ctx, question)
if err != nil && errors.Is(err, context.DeadlineExceeded) {
log.Println("请求超时")
}
缓存常用结果
type CachedAssistant struct {
*CodeAssistant
cache *lru.Cache
}
func (ca *CachedAssistant) Ask(ctx context.Context, question string) (string, error) {
// 检查缓存
if cached, ok := ca.cache.Get(question); ok {
return cached.(string), nil
}
// 调用原方法
answer, err := ca.CodeAssistant.Ask(ctx, question)
if err != nil {
return "", err
}
// 存入缓存
ca.cache.Add(question, answer)
return answer, nil
}
错误处理与重试
func RetryAsk(ctx context.Context, maxRetries int, question string) (string, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
answer, err := assistant.Ask(ctx, question)
if err == nil {
return answer, nil
}
lastErr = err
time.Sleep(time.Duration(i+1) * time.Second)
}
return "", fmt.Errorf("重试%d次后失败: %w", maxRetries, lastErr)
}
API费用控制
敏感信息保护
// 不要在代码中硬编码API密钥
// 使用环境变量或配置管理工具
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("OPENAI_API_KEY环境变量未设置")
}
llm, err := openai.New(openai.WithToken(apiKey))
记忆大小管理
错误响应处理
不要在前端直接调用API
不要忽略错误处理
// 错误做法
answer, _ := assistant.Ask(ctx, question)
// 正确做法
answer, err := assistant.Ask(ctx, question)
if err != nil {
log.Printf("调用失败: %v", err)
return
}
不要过度依赖记忆
不要在生产环境使用硬编码模型
// 错误做法
llm, err := openai.New(openai.WithModel("gpt-4"))
// 正确做法
modelName := os.Getenv("LLM_MODEL")
if modelName == "" {
modelName = "gpt-3.5-turbo"
}
llm, err := openai.New(openai.WithModel(modelName))
langchaingo为Go开发者提供了构建LLM应用的标准框架,它的价值主要体现在:
优势方面:
不足之处:
适用场景:
未来展望: 随着AI应用的普及,langchaingo有望在以下几个方向持续发展:
对于Go开发者来说,如果要在项目中集成LLM能力,langchaingo无疑是当前最佳的选择之一。虽然生态还不够成熟,但它的性能和部署优势使其在特定场景下具有不可替代的价值。