- Published on
多轮对话管理中的滑动窗口机制 (Sliding Window Mechanism)
- Authors
- Name
- Shelton Ma
在 AI Companion、聊天机器人等应用中,为了维持连贯的对话体验,必须妥善管理对话上下文.由于大模型存在 Token 限制(如 GPT-4 上限约 128k tokens),长对话会因 Token 超限而丢失早期对话内容.
滑动窗口 + 摘要压缩 + 长期记忆持久化 (如 Redis、MongoDB)
, 不仅能够在 GPT 的 Token 限制内高效保留对话信息,还能最大化上下文的完整度,是在多轮对话管理中值得推荐的解决方案.
1. 滑动窗口机制的核心概念
- 保留最新对话内容,并在
Token
达到上限时丢弃较早的对话. - 类似
窗口
在文本上滑动,仅保留窗口内的数据. - 窗口的大小应根据模型的 Token 限制动态调整.
2. 滑动窗口的 TypeScript 实现
import { encoding_for_model } from 'tiktoken';
const encoder = encoding_for_model('gpt-4'); // GPT-4 编码器
interface Message {
role: 'system' | 'user' | 'assistant';
content: string;
}
// 最大 Token 限制
const MAX_TOKENS = 8192;
// 滑动窗口函数
function slidingWindow(messages: Message[], maxTokens: number): Message[] {
let totalTokens = 0;
const result: Message[] = [];
// 从最新消息往前遍历,尽可能保留最多的消息
for (let i = messages.length - 1; i >= 0; i--) {
const tokens = encoder.encode(messages[i].content).length;
totalTokens += tokens;
if (totalTokens > maxTokens) break; // 超出上限时停止
result.unshift(messages[i]); // 保留该条消息
}
return result;
}
// 示例对话数据
const messages: Message[] = [
{ role: 'user', content: '你好,请推荐一家意大利餐厅.' },
{ role: 'assistant', content: '推荐你去“小意大利之家”,非常正宗.' },
{ role: 'user', content: '这家人均消费多少?' },
{ role: 'assistant', content: '大约150元每人.' },
];
// 模拟不断添加对话
messages.push({ role: 'user', content: '环境如何?' });
messages.push({ role: 'assistant', content: '氛围温馨且安静,适合约会.' });
// 调用滑动窗口
const filteredMessages = slidingWindow(messages, MAX_TOKENS);
console.log(filteredMessages);
3. 最佳实践
1. 优化方案
- 重要消息加权
- 摘要/压缩 (Summarization)
- 角色区分 (Role Splitting)
2. 方案确定
在 AI Companion 或聊天机器人中,滑动窗口
和 摘要压缩
结合使用,可以有效平衡以下两方面:
- 保持最近对话的完整性(关键细节不丢失)
- 提炼早期对话的核心信息(降低 Token 消耗)
3. 滑动窗口 + 摘要压缩 的整体思路
- 滑动窗口优先
- 先保留最近几轮对话,确保最新内容完整.
- 只在 Token 超出阈值时,触发摘要压缩.
- 摘要压缩早期内容:
- 当窗口中消息过多、超出 Token 限制时,将窗口内的早期对话压缩成摘要.
- 压缩内容保留核心信息,降低 Token 占用.
4. 实现
import { encoding_for_model } from 'tiktoken';
import axios from 'axios';
const encoder = encoding_for_model('gpt-4');
interface Message {
role: 'system' | 'user' | 'assistant';
content: string;
}
// Token 限制 (如 GPT-4 限制 8192 tokens)
const MAX_TOKENS = 8192;
const SUMMARY_MAX_TOKENS = 500;
// 模拟 OpenAI API 摘要功能
async function summarizeText(content: string): Promise<string> {
const { data } = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: 'gpt-4-turbo',
messages: [
{ role: 'system', content: '请将以下内容压缩成不超过 500 字的摘要.' },
{ role: 'user', content }
],
max_tokens: SUMMARY_MAX_TOKENS
},
{
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
return data.choices[0].message.content;
}
// 滑动窗口 + 摘要压缩的主逻辑
async function manageConversation(messages: Message[]): Promise<Message[]> {
let totalTokens = 0;
const result: Message[] = [];
// 从最新消息开始保留
for (let i = messages.length - 1; i >= 0; i--) {
const tokens = encoder.encode(messages[i].content).length;
totalTokens += tokens;
if (totalTokens > MAX_TOKENS) {
// Token 超过限制,触发摘要
const oldMessages = messages.slice(0, i + 1); // 提取超出 Token 前的对话
const oldContent = oldMessages.map(msg => msg.content).join('\n');
const summary = await summarizeText(oldContent);
// 将摘要作为新消息插入
result.unshift({ role: 'system', content: `摘要:${summary}` });
break;
}
result.unshift(messages[i]);
}
return result;
}
// 示例对话数据
const messages: Message[] = [
{ role: 'user', content: '你好,推荐一家上海的意大利餐厅.' },
{ role: 'assistant', content: '推荐你去“小意大利之家”,非常正宗.' },
{ role: 'user', content: '我喜欢安静的环境.' },
{ role: 'assistant', content: '这家氛围温馨,适合安静约会.' },
// ... (更多对话)
];
// 调用示例
manageConversation(messages).then(filteredMessages => {
console.log(filteredMessages);
});