六维教程

EdgeOne Makers Agents 对话存储

你的 Agent 要能记住用户之前说了什么,就得有个地方存对话历史。这篇讲三种常用的存储方案,以及怎么管理多轮对话的上下文。

为什么需要存储对话

大模型本身不记得上一轮聊了什么。每次调用都得把完整对话历史传过去,模型才能”记住”上下文。

场景 不存储的后果
多轮问答 用户说”继续”,模型不知道继续什么
客服机器人 客户重复描述问题,体验差
编程助手 忘了之前的代码上下文,给出错误建议

所以对话存储是 Agent 的基础设施。

三种存储方案

方案一 内存存储

最简单的方式,把对话存在内存里。

// 用 Map 存储,key 是 sessionId
const conversations = new Map();

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const sessionId = url.searchParams.get('session_id') || 'default';
    
    if (request.method === 'POST') {
      const { message } = await request.json();
      
      // 获取历史对话
      let history = conversations.get(sessionId) || [];
      
      // 添加用户消息
      history.push({ role: 'user', content: message });
      
      // 调用大模型
      const response = await env.AI.chat('deepseek-v3', {
        messages: history
      });
      
      // 添加助手回复
      history.push({ role: 'assistant', content: response.message.content });
      
      // 更新存储
      conversations.set(sessionId, history);
      
      return Response.json({ reply: response.message.content });
    }
  }
};

优点简单快速,开发调试方便

缺点服务重启就丢失,不适合生产环境

方案二 KV 存储

用 EdgeOne 的 KV 服务持久化存储,按 sessionId 隔离。

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const sessionId = url.searchParams.get('session_id') || 'default';
    
    if (request.method === 'POST') {
      const { message } = await request.json();
      
      // 从 KV 读取历史
      const historyKey = `chat:${sessionId}`;
      let history = [];
      
      try {
        const stored = await env.KV.get(historyKey, 'json');
        if (stored) history = stored;
      } catch (e) {
        // 首次对话,历史为空
      }
      
      // 添加用户消息
      history.push({ role: 'user', content: message });
      
      // 调用大模型
      const response = await env.AI.chat('deepseek-v3', {
        messages: history
      });
      
      // 添加助手回复
      history.push({ 
        role: 'assistant', 
        content: response.message.content 
      });
      
      // 存回 KV,设置过期时间(7天)
      await env.KV.put(historyKey, JSON.stringify(history), {
        expirationTtl: 7 * 24 * 60 * 60
      });
      
      return Response.json({ reply: response.message.content });
    }
  }
};

优点持久化存储,支持过期时间,适合生产环境

缺点每次都要读写 KV,有网络延迟

方案三 数据库存储

用 Supabase 或 EdgeOne 的数据库服务,适合需要复杂查询的场景。

import { createClient } from '@supabase/supabase-js';

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const sessionId = url.searchParams.get('session_id') || 'default';
    
    if (request.method === 'POST') {
      const { message } = await request.json();
      
      // 初始化 Supabase 客户端
      const supabase = createClient(
        env.SUPABASE_URL, 
        env.SUPABASE_KEY
      );
      
      // 查询历史对话
      const { data: history } = await supabase
        .from('messages')
        .select('role, content')
        .eq('session_id', sessionId)
        .order('created_at', { ascending: true });
      
      // 添加用户消息
      const messages = [
        ...(history || []),
        { role: 'user', content: message }
      ];
      
      // 调用大模型
      const response = await env.AI.chat('deepseek-v3', { messages });
      
      // 保存用户消息和助手回复到数据库
      await supabase.from('messages').insert([
        { session_id: sessionId, role: 'user', content: message },
        { 
          session_id: sessionId, 
          role: 'assistant', 
          content: response.message.content 
        }
      ]);
      
      return Response.json({ reply: response.message.content });
    }
  }
};

优点支持复杂查询,可以做数据统计分析

缺点需要建表,开发成本稍高

三种方案对比

方案 持久化 查询能力 性能 适用场景
内存 最快 开发调试、临时测试
KV 简单 大多数生产场景
数据库 较慢 需要分析统计的场景

上下文管理

对话存下来了,但大模型有上下文长度限制。不同模型的 token 限制不一样,得控制历史消息的数量。

截断策略

只保留最近 N 轮对话,丢弃更早的内容。

// 只保留最近 10 轮对话(20条消息)
const MAX_TURNS = 10;

function trimHistory(history) {
  const maxMessages = MAX_TURNS * 2;
  if (history.length <= maxMessages) {
    return history;
  }
  
  // 保留系统提示词(如果有)和最近的消息
  const systemMsg = history[0]?.role === 'system' 
    ? [history[0]] 
    : [];
  const recentMessages = history.slice(-maxMessages);
  
  return [...systemMsg, ...recentMessages];
}

// 使用
const trimmedHistory = trimHistory(history);
const response = await env.AI.chat('deepseek-v3', {
  messages: trimmedHistory
});

摘要策略

把早期对话总结成一段话,压缩上下文。

async function summarizeOldMessages(history, env) {
  if (history.length <= 20) {
    return history; // 不需要摘要
  }
  
  // 分离早期消息和近期消息
  const earlyMessages = history.slice(0, -10);
  const recentMessages = history.slice(-10);
  
  // 把早期消息转成文本
  const earlyText = earlyMessages
    .map(m => `${m.role}: ${m.content}`)
    .join('\n');
  
  // 调用模型生成摘要
  const summaryResponse = await env.AI.chat('deepseek-v3', {
    messages: [
      { 
        role: 'system', 
        content: '请把下面的对话内容总结成一段话,保留关键信息' 
      },
      { role: 'user', content: earlyText }
    ]
  });
  
  const summary = summaryResponse.message.content;
  
  // 用摘要替换早期消息
  return [
    { role: 'system', content: `之前的对话摘要: ${summary}` },
    ...recentMessages
  ];
}

// 使用
const optimizedHistory = await summarizeOldMessages(history, env);
const response = await env.AI.chat('deepseek-v3', {
  messages: optimizedHistory
});

两种策略对比

策略 优点 缺点
截断 简单,不额外消耗 token 丢失早期信息
摘要 保留关键信息 需要额外调用模型,有成本

一般推荐摘要策略,用户体验更好。

速查卡片

要点 说明
为什么要存储 大模型无状态,每次都要传完整历史
内存存储 简单快速,但重启丢失
KV 存储 持久化,支持过期,适合生产环境
数据库存储 支持复杂查询,适合分析统计
sessionId 作用 隔离不同用户的对话历史
截断策略 只保留最近 N 轮,简单但有信息丢失
摘要策略 压缩早期对话,保留关键信息
推荐方案 生产环境用 KV + 摘要策略
上一篇
EdgeOne Makers Agents 快速开始
下一篇
EdgeOne Makers Agents 沙箱工具