🔄 混合双保险上下文方案

--resume + 数据库历史 | 至少 5000 字符 + 3 条用户消息

🎯 方案目标

保留 --resume 机制的速度和官方支持
+ 添加数据库历史作为安全网
= 即使 API 会话过期,也能继续对话

✨ 核心特性

📊 智能提取

从数据库动态提取上下文历史:

  • 规则 1:累积至少 5000 字符
  • 规则 2:至少 3 条用户消息
  • 内容:用户消息 + 机器人回复
  • 排除:工具调用、系统消息

🔗 双保险机制

同时使用两种上下文来源:

  • 主策略:--resume=session-xxx(快速)
  • 备份:数据库历史作为前缀
  • 冗余设计:即使 resume 失效也不丢失记忆
  • 无缝切换:用户无感知

📝 格式化前缀

将历史转换为结构化文本:

  • # 对话历史上下文
  • ## [1] 用户:xxx
  • ## [2] 助手:xxx
  • ---
  • # 当前问题

🔄 工作流程

步骤 1:接收用户消息

用户在飞书发送:"我的名字叫什么?"

步骤 2:构建混合上下文

• 从数据库读取历史消息(按时间正序)
• 反向累积直到满足条件(5000字符 AND 3条用户消息)
• 返回 {useResume, resumeSessionId, databaseHistory, stats}

步骤 3:格式化为前缀

将数据库历史转换为文本前缀:
"# 对话历史上下文\n\n## [1] 用户:我的名字是张三...\n## [2] 助手:好的,记住了...\n\n---\n# 当前问题\n\n"

步骤 4:拼接增强消息

enhancedMessage = 前缀 + 当前消息
= "# 对话历史...\n\n我的名字叫什么?"

步骤 5:调用 Claude CLI

claude -p --resume=session-abc123 --output-format stream-json "[增强消息]"
• --resume 提供 Anthropic API 的远程上下文
• 增强消息提供数据库历史作为补充

步骤 6:返回响应

Claude 基于两份上下文(API + 数据库)回答:"你的名字是张三"
即使 --resume 失效,数据库历史也能提供足够信息

💻 代码实现

1. 上下文管理器

// server/lib/context-manager.js export class ContextManager { constructor() { this.MIN_CHARS = 5000; // 最小字符数 this.MIN_USER_MESSAGES = 3; // 最小用户消息数 } // 提取上下文历史 extractContextFromDatabase(sessionId) { const allMessages = feishuDb.getMessageHistory(sessionId, 1000); let cumulativeChars = 0; let userMessageCount = 0; const selectedMessages = []; // 反向遍历,累积到满足条件 for (let i = allMessages.length - 1; i >= 0; i--) { const msg = allMessages[i]; cumulativeChars += msg.content.length; selectedMessages.unshift({ role: msg.direction === 'incoming' ? 'user' : 'assistant', content: msg.content }); if (msg.direction === 'incoming') userMessageCount++; // 检查是否满足最小条件 if (cumulativeChars >= this.MIN_CHARS && userMessageCount >= this.MIN_USER_MESSAGES) { break; } } return selectedMessages; } // 构建混合上下文 buildHybridContext(sessionId, claudeSessionId, currentMessage) { return { useResume: !!claudeSessionId, resumeSessionId: claudeSessionId, databaseHistory: this.extractContextFromDatabase(sessionId), currentMessage: currentMessage, stats: { /* 统计信息 */ } }; } // 格式化为文本前缀 formatAsContextPrompt(messages) { const lines = ['# 对话历史上下文', '', '...']; messages.forEach((msg, index) => { const label = msg.role === 'user' ? '用户' : '助手'; lines.push(`## [${index + 1}] ${label}:`, msg.content, ''); }); return lines.join('\n'); } }

2. 集成到飞书服务

// server/feishu-ws.js import { contextManager } from './lib/context-manager.js'; // 在 handleMessage 中 const hybridContext = contextManager.buildHybridContext( session.id, session.claude_session_id, userText ); // 如果有历史,添加为前缀 let enhancedMessage = userText; if (hybridContext.databaseHistory.length > 0) { const contextPrefix = contextManager.formatAsContextPrompt( hybridContext.databaseHistory ); enhancedMessage = contextPrefix + userText; } // 调用 Claude await queryClaude(enhancedMessage, { sessionId: session.claude_session_id, cwd: session.project_path, skipPermissions: true }, writer);

📊 效果对比

方案 优点 缺点 适用场景
纯 --resume • 速度快
• 官方支持
• 包含完整历史
• 会话可能过期
• 无法验证
• 黑盒机制
短期对话
(几小时内)
纯数据库 • 完全可控
• 永不过期
• 可追溯
• 不包含工具调用
• 不包含系统提示
• tokens 占用大
长期对话
(跨天)
混合双保险 • 结合两者优点
• 容错性强
• 无缝切换
• 略增 tokens
• 代码复杂度+
推荐方案
适用所有场景

🎯 实施要点

✅ 已实现

  • ContextManager 类完成
  • 智能提取逻辑完成
  • 格式化前缀完成
  • 集成到 feishu-ws.js
  • 统计信息输出

🔧 待测试

  • 多轮对话测试
  • 5000字符阈值验证
  • 3条用户消息验证
  • --resume 失效场景
  • 性能影响评估

💡 未来优化

  • 动态调整阈值
  • 压缩历史消息
  • 智能选择关键对话
  • 上下文相似度匹配
  • 自动降级机制

🚀 启用方法

1. 重启飞书服务:pm2 restart feishu
2. 查看日志确认:pm2 logs feishu --lines 50
3. 测试对话:在飞书中进行多轮对话,观察日志输出
4. 验证效果:查找日志中的"混合上下文统计"信息