# 结构化会话存储 - 需求分析 ## 1. 文档信息 | 项目 | 说明 | |------|------| | 功能名称 | 结构化会话存储(Structured Conversation Store) | | 版本 | 3.0 | | 适用范围 | 所有会话(不区分 Ask/Workflow) | | 依赖 | ChatSession、MainWindowViewModel、特殊消息类型(Form/ParameterSet/Table/ColumnMatch)、LLM 调用链 | --- ## 2. 背景与目标 ### 2.1 背景 - **现状问题**:当前以「LLM 用的 ChatHistory」为唯一事实来源。引入特殊消息(表单、参数集、表格、列匹配等)后,若把特殊消息编码进同一条 History(如 Content 前缀+YAML),会导致: - 真相源混合了「该发给模型的对话」与「仅供 UI 展示的卡片」; - 每次调用 LLM 前必须解析、过滤;加载会话时要从字符串反推卡片; - 扩展新类型或改格式时,解析约定与两端逻辑都要一起改,维护成本高。 - **共识**:应有一份**独立的、全结构化的会话存储**,作为 **UI 与 Prompt 构建的共同真相源**;LLM History 仅作为从该存储**派生出的视图**,不再承担「存储真相」的职责。 ### 2.2 目标 - **结构化存储为唯一事实来源**:引入一套**会话级的结构化存储**(以下简称「会话存储」或「Store」),其中每条记录均为**结构化条目**(文本消息 = 角色 + 文本;特殊消息 = 类型 + YAML 载荷)。该存储按时间顺序记录会话中发生的所有消息与卡片,**不依赖** ChatHistory 的形态。 - **UI 与 Prompt 均为视图**: - **UI**:从会话存储按序生成界面所需的列表(如 `ChatMessages`),文本即文本气泡,特殊消息即卡片(还原 `SpecialContent`)。 - **Prompt 构建**:从同一份会话存储生成「发给 LLM 的历史」——仅包含需参与对话的文本(及可选的简短占位),再转为后端所需的 `ChatHistory`。 二者均不直接依赖「混入特殊消息的 History」作为真相源。 - **可持久化、可恢复**:会话存储可序列化(如 YAML/JSON)并持久化;加载会话时先恢复该存储,再据此构建 UI 与 Prompt,保证顺序与内容一致。 - **与 System/摘要解耦**:不向 System 消息写入或追加摘要;不在 System 后固定插入观察摘要;不因 Ask 模式做上述注入。 --- ## 3. 用户与利益相关方 | 角色 | 诉求 | |------|------| | 用户 | 切换会话或重新打开后,能看到完整对话流及曾出现的表单、参数卡、表格、列匹配等卡片,顺序与当时一致。 | | 会话列表/详情 | 进入某会话时,从持久化/内存中的会话存储恢复,保证 UI 与行为一致。 | | 开发/维护 | 真相源单一、结构清晰;扩展新消息类型仅改存储条目类型与两种视图逻辑,无需解析字符串约定。 | --- ## 4. 功能需求 ### 4.1 会话存储的条目类型 - **文本消息**:角色(User / Assistant / System)+ 文本内容。用于用户输入、助手回复及可选的 System 引导词。 - **特殊消息**:类型(Form / ParameterSet / Table / ColumnMatch / WorkflowStatus 等)+ **YAML 载荷**(对 AI 更友好、可读性好)。载荷含 type、id 及类型专用字段,可序列化/反序列化,用于恢复 UI 卡片。 ### 4.2 特殊消息类型与载荷内容(YAML) | 类型 | 载荷需包含内容 | 恢复时说明 | |------|----------------|------------| | Form | formId、title、status、fields、submitLabel 等 | 恢复时可根据 formId 从 FormRegistry 取 FormDefinition,还原表单卡片及已填/已提交状态。 | | ParameterSet | title、items(name、valueText、description、fieldType、options 等) | 直接反序列化还原 ParameterSetMessage。 | | Table | title、columnNames、rows/totalRowCount/maxPreviewRows | 直接反序列化还原 TableDataMessage。 | | ColumnMatch | title、requiredColumns、previewColumns、mappings | 直接反序列化还原 ColumnMatchMessage。 | | WorkflowStatus | title、steps 等 | 可选,恢复为只读展示卡片。 | ### 4.3 顺序与唯一性 - 所有条目在存储中**严格按发生顺序**排列,形成一条时间线。 - 新增用户输入、助手回复、出现的卡片等,均**追加**到存储末尾,不修改已有条目(除业务上允许的「更新同一卡片」策略外,若需支持再单独约定)。 ### 4.4 视图行为 - **UI 视图**:遍历会话存储,对每条条目——文本消息 → 生成对应 `ChatMessageModel`(User/AI + 文本);特殊消息 → 反序列化载荷为 `ISpecialMessage`,生成带 `MessageType` 与 `SpecialContent` 的 `ChatMessageModel`。结果用于填充 `ChatMessages` 等界面列表。 - **Prompt 视图**:遍历同一份会话存储,仅取「需要参与 LLM 对话」的条目(如 User、Assistant 文本);对特殊消息可**跳过**或**替换为一句简短描述**(如「[已展示表单:加载散点]」)。将结果转为 `ChatHistory`(或当前后端所需格式)供 `AskAsync` / `AskStreamAsync` 使用。**不**把原始 YAML 载荷发给模型。 ### 4.5 持久化与加载 - **持久化**:会话存储可序列化为 YAML 或 JSON(建议 YAML,对人与 AI 更友好),随会话一起持久化;若当前仅内存会话,则内存中的存储即为真相源。 - **加载**:加载会话时先反序列化得到会话存储,再基于该存储构建 UI 视图与(在调用 LLM 时)Prompt 视图;不依赖「从 ChatHistory 反推卡片」。 --- ## 5. 非功能需求 | 类型 | 要求 | |------|------| | 单一事实来源 | UI 与 Prompt 仅从会话存储派生,不另设混合了编码约定的 History 作为真相源。 | | 可序列化 | 存储及每条特殊消息载荷均有稳定、可逆的序列化格式(YAML),便于持久化与跨版本兼容。 | | 顺序一致 | 存储中的顺序与用户看到的时间线一致;恢复后顺序不变。 | | 可维护性 | 新增消息类型时,仅扩展存储条目类型与两种视图逻辑,无需改字符串解析约定。 | --- ## 6. 约束与假设 - **不注入 System**:不在 System 消息内容中追加或合并摘要;System 后不再固定插入观察摘要 Assistant 消息。 - **LLM History 非真相源**:`ChatHistory` 仅作为「调用 LLM 时由会话存储生成的视图」,不作为持久化或 UI 恢复的真相源。 - **Form 恢复**:特殊消息 Form 恢复时依赖 FormRegistry 提供 FormDefinition;若 formId 不存在可降级为占位或只读文本。 - **会话列表**:「恢复卡片」指用户**进入某会话**时,从该会话的存储恢复 UI;若列表仅展示标题/摘要,不展开历史,则恢复发生在打开该会话时。 --- ## 7. 验收标准(高层) - 存在独立的**会话存储**,其中所有条目均为结构化(文本 = 角色+内容,特殊消息 = 类型+YAML 载荷);存储为 UI 与 Prompt 构建的**唯一事实来源**。 - 加载会话后,UI 仅从该会话的存储构建,能按序还原所有文本与卡片;调用 LLM 时使用的历史仅从同一份存储投影得到(仅文本或文本+短占位),且不包含原始 YAML 载荷。 - 持久化格式为会话存储的序列化形式(如 YAML 文档);不依赖「混入特殊消息的 ChatHistory」作为持久化真相源。