{T}

多轮对话与记忆管理

多轮对话是 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-4o128K tokens~10万字中文
DeepSeek64K tokens~5万字中文
Moonshot128K tokens~10万字中文
Qwen332K-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 持久化

参考资源