/** * ConversationMemory — 基于数据库的会话记忆(话题感知 + 链式摘要) * * 两张表分离存储: * * ai_messages 表(逐条记录): * ┌──────────────────────────────────────────────────────────┐ * │ session_id | role | content | round | time │ * │ s1 | user | 你好 | 1 | ... │ * │ s1 | assistant | 你好呀! | 1 | ... │ * │ ... | ... | ... | ... | ... │ * └──────────────────────────────────────────────────────────┘ * * ai_summaries 表(链式摘要): * ┌────────────────────────────────────────────────────────────────────┐ * │ id | session_id | parent_id | from_round | to_round | summary │ * │ 1 | s1 | null | 1 | 7 | 用户讨论了… │ * │ 2 | s1 | 1 | 8 | 15 | 之前...又… │ * └────────────────────────────────────────────────────────────────────┘ * * 摘要触发规则: * 1. 检测到话题切换(当前消息与最近消息的关键词重合度低) * 2. 且上一个话题持续 ≥ minTopicRounds 轮 * → 异步生成摘要,覆盖上一话题的全部消息 * * 上下文构建规则: * 1. 取最新 summary + 最近 slidingWindowSize 轮消息(滑动窗口) * 2. 检查连续性:summary.to_round === window第一条round - 1 * → 连续:[summary] + [window] * → 不连续:丢弃 summary,仅用 [window] */ import type { AIProvider, ChatMessage } from '../types.js'; /** ai_messages 表结构 */ export declare const AI_MESSAGE_MODEL: { session_id: { type: "text"; nullable: boolean; }; role: { type: "text"; nullable: boolean; }; content: { type: "text"; nullable: boolean; }; round: { type: "integer"; nullable: boolean; }; created_at: { type: "integer"; default: number; }; sender_id: { type: "text"; default: string; }; sender_roles: { type: "text"; default: string; }; }; /** ai_summaries 表结构(链式) */ export declare const AI_SUMMARY_MODEL: { session_id: { type: "text"; nullable: boolean; }; parent_id: { type: "integer"; nullable: boolean; }; from_round: { type: "integer"; nullable: boolean; }; to_round: { type: "integer"; nullable: boolean; }; anchor_message_id: { type: "text"; default: string; }; summary: { type: "text"; nullable: boolean; }; created_at: { type: "integer"; default: number; }; }; export interface SaveRoundMeta { senderId?: string; senderRoles?: readonly string[]; } /** * 数据库模型接口(与 RelatedModel 的链式查询 API 对齐) * * select(...fields) → Selection (thenable, 支持 .where().orderBy().limit()) * create(data) → Promise */ interface DbModel { select(...fields: string[]): any; create(data: Record): Promise; aggregate(): { where(condition: Record): { max(field: string, alias: string): Promise[]>; }; }; delete?(condition: Record): PromiseLike; } export interface ConversationMemoryConfig { /** 一个话题至少持续多少轮才触发摘要(默认 5) */ minTopicRounds?: number; /** 滑动窗口大小:最近 N 轮消息(默认 5) */ slidingWindowSize?: number; /** 话题切换检测阈值(0-1,值越低越敏感,默认 0.15) */ topicChangeThreshold?: number; /** 数据库记录保留天数(默认 30,0 表示不自动清理) */ dbTtlDays?: number; /** 话题检测 LLM 模型(默认可用 provider 的首个模型;建议填轻量模型如 glm-4-flash) */ topicDetectModel?: string; } export declare class ConversationMemory { private store; private provider; private config; private summarizing; /** per-session 话题跟踪 */ private topicStates; /** per-session 轮次缓存(避免每次查数据库) */ private roundCache; /** 各 session 最后活跃时间(用于淘汰) */ private lastAccess; /** DB TTL 自动清理定时器 */ private dbTtlTimer; /** topicStates / roundCache 定时淘汰 */ private sessionCachePruneTimer; /** 内存缓存上限:超过此数量时 LRU 淘汰 */ private static readonly MAX_TRACKED_SESSIONS; /** 缓存过期时间:24 小时未访问即淘汰 */ private static readonly SESSION_CACHE_TTL; /** 内存缓存清扫间隔 */ private static readonly SESSION_CACHE_PRUNE_INTERVAL; /** DB TTL 清理间隔:每 24 小时执行一次 */ private static readonly DB_TTL_INTERVAL; constructor(config?: ConversationMemoryConfig); setProvider(provider: AIProvider): void; upgradeToDatabase(msgModel: DbModel, sumModel: DbModel): Promise; /** 是否已有持久化对话行(ai_messages 或升级前的内存缓存) */ hasStoredMessages(sessionId: string): Promise; /** * 保存一轮对话,并检测话题切换来触发摘要 */ saveRound(sessionId: string, userContent: string, assistantContent: string, meta?: SaveRoundMeta): Promise; /** * 群/频道共享 session:记录未 @ 机器人的用户发言(仅 user 行,不触发话题摘要 LLM)。 */ appendPassiveGroupUserMessage(sessionId: string, userContent: string, meta?: SaveRoundMeta): Promise; /** * 话题检测流程(全程异步,不阻塞对话): * * 1. 短消息(token ≤ 3)→ 跳过检测,视为延续话题 * 2. Jaccard 快检 → 高相似(≥ 0.5) → 肯定同话题,跳过 LLM * 3. Jaccard 不确定(< 0.5) → 调 LLM 裁决 * 4. LLM 判定切换 + 旧话题 ≥ minTopicRounds → 触发摘要 */ private handleTopicAndSummary; /** 更新话题状态(同话题情况) */ private updateTopicState; /** * 调用 LLM 判断话题是否切换 * * 输入: 最近几条用户消息 + 当前用户消息 * 输出: true = 话题切换, false = 同话题 */ private detectTopicChangeByLLM; /** * 异步生成链式摘要(不阻塞对话) */ private generateSummaryAsync; /** * 构建 LLM 上下文消息列表 * * 规则: * 1. 取滑动窗口(最近 slidingWindowSize 轮) * 2. 取最新 summary * 3. 检查连续性:summary.to_round === window第一轮 - 1 * → 连续:[summary] + [window 消息] * → 不连续:仅 [window 消息] */ buildContext(sessionId: string): Promise; private callLLMSummarize; searchMessages(sessionId: string, keyword: string, limit?: number): Promise<{ round: number; role: string; content: string; time: number; }[]>; getMessagesByRound(sessionId: string, fromRound: number, toRound: number): Promise<{ round: number; role: string; content: string; time: number; }[]>; getCurrentRound(sessionId: string): Promise; traceByKeyword(sessionId: string, keyword: string, limit?: number): Promise<{ summary: { id: number; fromRound: number; toRound: number; summary: string; } | null; messages: { round: number; role: string; content: string; time: number; }[]; }>; /** * 淘汰长时间未访问的 session 缓存,防止 topicStates/roundCache 无限增长。 */ private pruneStaleSessionCaches; private evictSessionCacheEntry; private startSessionCachePruneTimer; dispose(): void; private startDbTtlTimer; private runDbTtlCleanup; } export {}; //# sourceMappingURL=conversation-memory.d.ts.map