掌握Golang LangChain的记忆功能实现:聊天机器人场景下的应用与优化

云信安装大师
90
AI 质量分
3 5 月, 2025
3 分钟阅读
0 阅读

掌握Golang LangChain的记忆功能实现:聊天机器人场景下的应用与优化

引言

在构建聊天机器人时,记忆功能是提升用户体验的关键。它能记住对话历史,让机器人显得更加智能和人性化。本文将介绍如何使用Golang和LangChain框架实现聊天机器人的记忆功能,并通过优化技巧提升其性能。

准备工作

环境要求

  • Go 1.18+
  • LangChain Go SDK (v0.0.4+)
  • Redis (可选,用于持久化存储)

安装依赖

代码片段
go get github.com/tmc/langchaingo

基础记忆功能实现

1. 创建基础聊天机器人

首先我们创建一个简单的聊天机器人结构:

代码片段
package main

import (
    "context"
    "fmt"
    "github.com/tmc/langchaingo/llms/openai"
    "github.com/tmc/langchaingo/memory"
    "github.com/tmc/langchaingo/schema"
)

type ChatBot struct {
    llm      *openai.LLM
    memory   schema.Memory
}

func NewChatBot(apiKey string) (*ChatBot, error) {
    llm, err := openai.New(openai.WithToken(apiKey))
    if err != nil {
        return nil, err
    }

    return &ChatBot{
        llm:    llm,
        memory: memory.NewConversationBuffer(),
    }, nil
}

func (c *ChatBot) Chat(ctx context.Context, input string) (string, error) {
    // 将当前输入和记忆一起传递给LLM
    prompt := fmt.Sprintf("当前对话:\n%s\n用户最新输入: %s", 
        c.memory.GetMemoryVariables(ctx), input)

    resp, err := c.llm.Call(ctx, prompt)
    if err != nil {
        return "", err
    }

    // 保存对话到记忆
    c.memory.SaveContext(ctx, map[string]any{"input": input}, 
        map[string]any{"output": resp})

    return resp, nil
}

2. 使用示例

代码片段
func main() {
    bot, err := NewChatBot("your-openai-api-key")
    if err != nil {
        fmt.Println("创建机器人失败:", err)
        return
    }

    ctx := context.Background()

    resp1, _ := bot.Chat(ctx, "你好,我是小明")
    fmt.Println("回复1:", resp1)

    resp2, _ := bot.Chat(ctx, "你还记得我叫什么吗?")
    fmt.Println("回复2:", resp2)
}

记忆功能原理解析

LangChain的记忆系统主要由以下几个组件构成:

  1. Memory Interface: 定义了保存和加载记忆的基本方法
  2. ConversationBuffer: 简单的内存存储,保存完整的对话历史
  3. ConversationBufferWindow: 只保留最近N条对话记录
  4. ConversationSummaryMemory: 使用LLM生成对话摘要而非完整记录

在我们的示例中使用了ConversationBuffer,它会将所有对话历史保存在内存中。

高级优化技巧

1. 使用窗口记忆限制长度

当对话变长时,完整存储所有历史会消耗大量token。我们可以改用窗口记忆:

代码片段
func NewChatBot(apiKey string) (*ChatBot, error) {
    // ...其他代码...
    return &ChatBot{
        llm:    llm,
        memory: memory.NewConversationBufferWindow(5), // 只保留最近5轮对话
    }, nil
}

2. Redis持久化存储

对于生产环境,建议使用Redis持久化存储对话历史:

代码片段
import (
    "github.com/go-redis/redis/v8"
    "github.com/tmc/langchaingo/memory/redismemory"
)

func NewChatBotWithRedis(apiKey, redisAddr string) (*ChatBot, error) {
    llm, err := openai.New(openai.WithToken(apiKey))
    if err != nil {
        return nil, err
    }

    redisClient := redis.NewClient(&redis.Options{
        Addr:     redisAddr,
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    redisMemory := redismemory.NewRedisMemory(redisClient)

    return &ChatBot{
        llm:    llm,
        memory: redisMemory,
    }, nil
}

3. 摘要式记忆优化

对于超长对话,可以使用摘要式记忆:

代码片段
func NewSummarizedChatBot(apiKey string) (*ChatBot, error) {
    llm, err := openai.New(openai.WithToken(apiKey))
    if err != nil {
        return nil, err
    }

    summaryMemory := memory.NewConversationSummary(llm)

    return &ChatBot{
        llm:    llm,
        memory: summaryMemory,
    }, nil
}

性能优化建议

  1. 控制token消耗:监控每次请求的token使用量,避免因过长历史导致高成本

  2. 异步保存:对于IO操作(如Redis),考虑异步保存以减少延迟:

    代码片段
    go func() { 
        c.memory.SaveContext(ctx, inputs, outputs) 
    }()
    
  3. 定期清理:设置定时任务清理过期的对话记录

  4. 分用户存储:为不同用户维护独立的记忆空间:

    代码片段
    func (c *ChatBot) Chat(ctx context.Context, userID string, input string) (string, error) { 
        // ...使用userID作为key的一部分...
    }
    

完整示例代码

以下是整合了所有优化点的完整示例:

代码片段
package main

import (
    "context"
    "fmt"
    "time"

    "github.com/go-redis/redis/v8"
    "github.com/tmc/langchaingo/llms/openai"
    mem "github.com/tmc/langchaingo/memory"
    schema "github.com/tmc/langchaingo/schema"
)

type EnhancedChatBot struct {
    llm      *openai.LLM

    memories map[string]schema.Memory // userID -> Memory

    maxHistory int

    summarizeInterval time.Duration

}

func NewEnhancedChatBot(
    maxHistory int,
    summarizeInterval time.Duration,
) *EnhancedChatBot {

    return &EnhancedChatBot{

            memories: make(map[string]schema.Memory),

            maxHistory: maxHistory,

            summarizeInterval: summarizeInterval,

         }
}

func (b *EnhancedChatBot) GetOrCreateMemory(userID string) schema.Memory {

    if mem , exists:= b.memories[userID]; exists {

                return mem 

         }

    newMem:= mem .NewConversationBufferWindow(b.maxHistory)

    b.memories[userID] = newMem 

    go b.startSummaryWorker(userID , newMem )

    return newMem 

}

func (b *EnhancedChatBot ) startSummaryWorker(userID string , memory schema . Memory ) {

    ticker:= time . NewTicker(b . summarizeInterval )

    defer ticker . Stop ()

    for range ticker . C {

                b . SummarizeMemory(userID , memory )

         }

}

func (b *EnhancedChatBot ) SummarizeMemory(userID string , memory schema . Memory ) {

    currentHistory:= memory . GetMemoryVariables(context.Background())

    prompt:= fmt.Sprintf("请总结以下对话内容:\n%s", currentHistory )

    resp , _:= b . llm.Call(context.Background(), prompt )

    newMem:= mem .NewConversationBuffer()

    newMem.SaveContext(context.Background(), 

                map[string]any{"summary": resp },

                map[string]any{},

         )

    b.memories[userID] = newMem 

}


func main() {

    const maxHistory =10 

    const summaryInterval=30*time.Minute 

    const testUser="user123"



bot:=NewEnhancedChatBot(maxHistory , summaryInterval )



ctx:=context.Background()



memory:=bot.GetOrCreateMemory(testUser )



for i:=0;i<15;i++{

                input:=fmt.Sprintf("这是第%d条测试消息",i+1)

                 _,err:=memory.SaveContext(ctx ,

                            map[string]any{"input":input },

                            map[string]any{"output":"模拟回复"},

                     )

                 if err!=nil{

                            fmt.Println("保存上下文错误:",err)

                            continue 

                     }

                 fmt.Printf("已保存消息%d\n",i+1)

                 fmt.Println("当前历史:",memory.GetMemoryVariables(ctx))

         }

}


总结

本文详细介绍了如何在Golang中使用LangChain实现聊天机器人的记忆功能关键点包括:

1基础内存实现使用ConversationBuffer简单易用但需要注意token消耗问题

2高级优化技术包括窗口限制Redis持久化和摘要式记忆

3性能优化建议如异步IO分用户存储和定期清理

4完整示例展示了生产级实现的最佳实践

通过合理配置和优化你可以构建出既智能又高效的聊天机器人系统

原创 高质量