多轮对话与记忆管理
多轮对话是 AI 应用中最具挑战性的实现之一。核心难点在于:如何在有限的上下文窗口内维护连贯的对话记忆,如何协调多个 Agent 的异步协作,以及如何通过时间线和目的表控制对话节奏。
核心概念
基本术语
| 术语 | 定义 | 实现方式 |
|---|---|---|
| 会话(Session) | 标记一轮完整交互,同一会话内 Agent 共享数据 | UUID / session_id |
| 记忆(Memory) | 记录历史内容,供后续推理参考 | Redis / 数据库 / 内存对象 |
| 时间线(Timeline) | 控制对话流程节奏的配置表 | 时间段 + 目标 + 提示词 |
| 目的表(Purpose Table) | 指导 Agent 根据当前状态选择行动方针 | 配置表 + 条件规则 |
| 行动方针(Actions) | 针对用户各种可能回答的预设策略 | 条件分支 + 提示词模板 |
对话系统架构
图表渲染中…
上下文管理
消息历史结构
多轮对话通过 messages 数组维护完整上下文:
typescript
interface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
tool_call_id?: string;
name?: string;
}
// 典型多轮对话消息序列
const messages: Message[] = [
{ role: 'system', content: '你是一位专业的AI面试官...' },
{ role: 'user', content: '你好,我准备好了' },
{ role: 'assistant', content: '好的,请先做个自我介绍吧' },
{ role: 'user', content: '我叫张三,有5年前端经验...' },
{ role: 'assistant', content: '你提到了Vue项目经验,能详细说说...' },
// ... 持续累积
];上下文窗口限制
| 模型 | 上下文窗口 | 约等于 |
|---|---|---|
| GPT-4o | 128K tokens | ~10万字中文 |
| DeepSeek | 64K tokens | ~5万字中文 |
| Moonshot | 128K tokens | ~10万字中文 |
| Qwen3 | 32K-128K | 视版本而定 |
上下文压缩策略
当对话超出窗口限制时,需要压缩策略:
图表渲染中…
typescript
// 滑动窗口 + 摘要压缩
function compressMessages(
messages: Message[],
maxTokens: number = 8000
): Message[] {
const system = messages[0]; // 始终保留 system prompt
const recent = messages.slice(-10); // 最近 5 轮对话
// 对更早的消息生成摘要
const older = messages.slice(1, -10);
if (older.length > 0) {
const summary = generateSummary(older);
return [
system,
{ role: 'system', content: `[历史摘要] ${summary}` },
...recent,
];
}
return [system, ...recent];
}时间线控制
时间线配置
通过时间线配置控制对话节奏,让 AI 在不同阶段聚焦不同目标:
typescript
interface TimelineStep {
startTime: number; // 起始时间(分钟)
endTime: number; // 结束时间(分钟)
focus: string; // 聚焦主题
prompt: string; // 该阶段的指导提示词
}
const timeline: TimelineStep[] = [
{ startTime: 0, endTime: 3, focus: '自我介绍', prompt: '引导候选人进行自我介绍' },
{ startTime: 3, endTime: 10, focus: '项目讨论', prompt: '深入讨论候选人的项目经验' },
{ startTime: 10, endTime: 17, focus: '技术讨论', prompt: '考察核心技术原理理解' },
{ startTime: 17, endTime: 25, focus: '代码算法', prompt: '讨论代码设计和算法问题' },
{ startTime: 25, endTime: 30, focus: '非技术问题', prompt: '讨论团队协作、职业规划' },
{ startTime: 30, endTime: 32, focus: '反问环节', prompt: '邀请候选人提问' },
{ startTime: 32, endTime: 999, focus: '结束', prompt: '礼貌结束面试' },
];
function getCurrentStep(elapsedMinutes: number): TimelineStep {
return timeline.find(
(step) => elapsedMinutes >= step.startTime && elapsedMinutes < step.endTime
) || timeline[timeline.length - 1];
}时间线控制器
AI 应有一定自主权来调整时间线(如某环节讨论深入可适当延长):
typescript
class TimelineController {
private startTime = Date.now();
private currentStepIndex = 0;
getElapsedMinutes(): number {
return (Date.now() - this.startTime) / 60000;
}
getCurrentStep(): TimelineStep {
const elapsed = this.getElapsedMinutes();
const step = getCurrentStep(elapsed);
return step;
}
// AI 可请求延长当前环节
extendCurrentStep(extraMinutes: number) {
timeline[this.currentStepIndex].endTime += extraMinutes;
}
// 强制跳转到下一环节
skipToNext() {
this.currentStepIndex = Math.min(
this.currentStepIndex + 1,
timeline.length - 1
);
}
}多 Agent 协作
MoE(Mixture of Experts)模式
复杂对话系统通常采用多 Agent 协作,各司其职:
图表渲染中…
| Agent | 职责 | 触发时机 |
|---|---|---|
| 对话 Agent | 直接与用户交互,生成回复 | 每次用户输入 |
| 记忆 Agent | 记录、检索、更新对话记忆 | 每轮对话后 |
| 思考 Agent | 分析当前状态,制定下一步策略 | 用户回答等待期间 |
异步并行设计
关键优化:当用户回答时,AI 同时准备下一步策略,而非串行等待:
typescript
async function handleUserInput(input: string, session: Session) {
// 并行执行:对话生成 + 策略思考
const [dialogResult, thinkingResult] = await Promise.all([
// 对话 Agent:基于当前上下文生成回复
generateDialogResponse(session.messages, input),
// 思考 Agent:预判各种回答可能性,制定行动方针
generateActionPlan(session.timeline, session.memory, session.purposeTable),
]);
// 更新记忆
session.memory.add({ role: 'user', content: input });
session.memory.add({ role: 'assistant', content: dialogResult });
// 将行动方针注入下一轮对话的 system prompt
session.currentActions = thinkingResult.actions;
return dialogResult;
}行动方针设计
行动方针不考虑用户实际如何回答,而是将所有可能性都考虑进去:
typescript
const actionPlanPrompt = `
根据当前面试阶段(${currentStep.focus})和历史记忆,生成行动方针:
- 如果用户回答得很完美 → 从目的表中选一个其他方向的问题
- 如果用户回答得还可以 → 选同方向问题继续深入
- 如果用户回答得不好 → 给予提示,降低难度重新提问
- 如果用户表示不了解 → 记录薄弱点,切换到下一个主题
- 如果用户反问 → 简要回答后引导回正题
当前目的表:
${JSON.stringify(purposeTable)}
`;记忆系统设计
存储架构
typescript
interface MemoryStore {
// 短期记忆:当前会话上下文
shortTerm: {
sessionId: string;
messages: Message[];
metadata: Record<string, any>;
};
// 长期记忆:跨会话持久化
longTerm: {
userId: string;
summaries: string[]; // 历史会话摘要
preferences: Record<string, any>; // 用户偏好
keyFacts: string[]; // 关键事实
};
}记忆检索策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 全量加载 | 将所有历史消息放入上下文 | 短对话(<10轮) |
| 滑动窗口 | 只保留最近 N 轮 | 一般对话 |
| 向量检索 | 通过 embedding 相似度检索相关记忆 | 长期记忆、知识库 |
| 摘要注入 | 将历史压缩为摘要放在 system prompt | 超长对话 |
API 读写分离设计
设计原则
多轮对话系统的 API 应遵循读写分离:
图表渲染中…
典型 API 设计
typescript
// 创建会话
POST /api/sessions
Response: { sessionId: string, config: SessionConfig }
// 发送消息(SSE 流式响应)
POST /api/sessions/:id/messages
Body: { content: string }
Response: SSE stream
// 获取会话状态
GET /api/sessions/:id
Response: {
status: 'active' | 'completed',
currentStep: TimelineStep,
elapsedMinutes: number,
messageCount: number
}常见问题与陷阱
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 对话"失忆" | 上下文窗口溢出 | 实现摘要压缩 + 重要信息标记 |
| 角色偏移 | 多轮后 system prompt 影响减弱 | 定期在 user 消息中重申角色 |
| 响应延迟高 | 多 Agent 串行执行 | 改为并行 + 行动方针预生成 |
| 记忆污染 | 错误信息被记入长期记忆 | 记忆写入前增加校验 Agent |
| 会话状态丢失 | 服务重启/内存清空 | 使用 Redis/DB 持久化 |