# 问师傅多轮会话与原始资料交付设计 > 状态:实施基线,尚未代表代码完成、生产发布或业务验收。 > > 目标:让“问·数字师傅”能理解本次连续对话中的指代,并在当前授权范围内交付命中的原文件或操作视频。 ## 1. 当前事实与问题 `mobile-uni/src/pages/user/sop/index.vue` 会在页面内展示消息列表,但每次文字查询只向 `POST /api/knowledge/query` 发送当前 `queryText`;附件查询同样只向 `POST /api/knowledge/query-media` 发送当前问题和本次附件。页面刷新、重新进入或服务端生成答案时都没有可用的对话上下文。 因此当前能力是“微信式界面 + 单轮无状态 RAG”,不是多轮智能会话。典型失败包括: - 用户先问“自动扶梯困人怎么办”,再说“把这个 SOP 原文件给我”,第二句无法确定“这个”指什么; - 用户说“有没有操作视频”,系统仍按普通知识问答检索,可能返回另一份语义相近但无关的 SOP; - 命中文档后只有引用片段,缺少经过再次授权校验的原文件或视频入口。 ## 2. 第一版目标与非目标 第一版完成: 1. 内部登录用户拥有 30 分钟不活跃过期的短期会话;页面可显式“新对话”。 2. 使用现有 chat 模型把追问改写为可独立检索的问题,并输出 `QA`、`FILE`、`VIDEO`、`DATA_TOOL` 四类意图。 3. 改写后的问题继续走现有租户、调用应用、主体授权交集和 RAG 链路。 4. 文件/视频只从当前命中且仍有权限的知识附件中返回;下载时再次校验权限。 5. 模型、会话表或附件不可用时明确降级,不编造上下文、链接或文件。 第一版不做: - 跨天长期记忆、用户画像记忆或对话向量库; - 对外 API_TOKEN 调用端的有状态会话; - 自动执行任意数据库查询、自然语言转 SQL; - 把用户上传的本次图片/视频自动写入共享知识库; - 仅凭语义相似度把未命中的文件伪装成“原文件”。 ## 3. 会话数据 新增单表 `aihr_knowledge_conversation`,只保存一个会话的压缩 JSON,不拆消息表: | 字段 | 作用 | |---|---| | `tenant_id`、`app_id`、`user_id` | 会话隔离键,全部由服务端身份解析 | | `conversation_id` | 客户端持有的随机会话 ID | | `version` | 乐观并发版本 | | `context_json` | 最近最多 6 轮的脱敏问题、截断答案、引用标题/文档 ID | | `expires_time` | 30 分钟不活跃过期时间 | | `create_time`、`update_time` | 审计时间 | 约束:单个用户问题最多 600 字、单个答案最多 800 字、送入模型的会话上下文最多 6000 字;过期记录惰性清理。唯一键为 `tenant_id + app_id + user_id + conversation_id`。客户端不能传租户、应用或用户身份。 ## 4. 查询契约 内部 `POST /api/knowledge/query` 和 `POST /api/knowledge/query-media` 增加可选字段: ```json { "conversationId": "01J...", "contextVersion": 3 } ``` 响应在现有字段上增加: ```json { "conversationId": "01J...", "contextVersion": 4, "intent": "FILE", "rewrittenQuery": "自动扶梯困人应急操作 SOP 原文件", "resources": [ { "attachmentId": 123, "title": "自动扶梯困人应急操作 SOP.pdf", "type": "FILE", "contentUrl": "/api/knowledge/resources/123/content" } ] } ``` 旧客户端不传会话字段时继续按无状态查询处理。外部 `POST /api/open/knowledge/query` 保持无状态,并忽略或拒绝会话字段。 ## 5. 处理流程 ```text 内部登录与应用解析 → 计算租户 + 应用 + 主体的有效知识空间 → 读取本人未过期会话并校验 contextVersion → 现有 chat 模型 temperature=0 输出 rewrittenQuery + intent → QA/FILE/VIDEO:在有效空间内执行现有混合检索 → DATA_TOOL:只执行既有白名单工具及角色/项目范围校验 → FILE/VIDEO:把命中 docId 映射为当前空间的附件资源 → 返回答案、引用、资源和新 contextVersion → 脱敏截断后更新最近 6 轮会话 ``` 模型输出必须使用固定 JSON 契约。JSON 解析失败或模型不可用时,以原问题检索,并用关键词规则只做保守意图判断;不能从历史上下文猜测缺失主体。 ## 6. 原文件和视频安全 新增认证接口 `GET /api/knowledge/resources/{attachmentId}/content`。每次下载必须重新计算当前租户、调用应用、用户/角色与知识空间授权,并确认附件仍属于有效空间,然后才通过现有 OSS 服务流式返回内容。 响应只暴露业务附件 ID、标题、类型和受控接口地址,不暴露 `ossId`、对象存储 key、原始 OSS URL 或服务器路径。权限被撤销、文件被解绑或附件不存在时立即拒绝;对话中曾经命中过不能覆盖下载时的实时授权。 视频意图只返回视频类型附件;没有视频时明确回答“当前授权资料中未找到相关视频”,不把 PDF、图片或用户本次上传的视频替代成知识库操作视频。 ## 7. 并发与降级 - 客户端提交旧 `contextVersion` 时返回冲突,避免两个请求互相覆盖上下文。 - 会话不存在或过期时,从空上下文继续当前问题,并返回新的版本。 - 会话持久化失败时保留当前单轮 RAG 能力,同时不声称已理解前文。 - RAG 无证据时沿用 `noEvidence=true`;文件/视频意图无附件时返回空 `resources` 和明确说明。 - 原始资料接口失败不回退到公开 URL、跨空间附件或模型生成链接。 ## 8. 移动端交互 问师傅页维护当前 `conversationId/contextVersion`,消息仍只作当前页面展示。顶部提供“新对话”,清空页面消息和会话标识。答案卡增加原文件/视频资源卡,点击时复用现有鉴权文件下载能力;页面卸载或资源替换时释放本地 blob URL。 需要验证文字和语音追问、模型失败、会话过期、并发冲突、无文件、无视频、授权撤销、重新登录和超大字号。微信式外观不再作为“有记忆”的完成证据。 ## 9. 验收用例 最小连续对话: 1. “自动扶梯困人怎么办?”返回有依据的 SOP 答案和引用。 2. “把这个 SOP 原文件给我。”`rewrittenQuery` 指向同一主题,只返回当前授权附件。 3. “有操作视频吗?”只返回同一主题的视频;不存在时明确无视频。 4. 点击资料可下载;撤销空间授权后,同一链接立即拒绝。 安全与回归还需覆盖:跨用户/跨租户会话不可读、旧版本冲突、30 分钟过期、模型关闭、OSS 缺失、视频类型过滤、外部 API 保持无状态、旧客户端单轮查询不回退。 完成结论分为:代码实现、自动化测试、本地 HTTP、移动端视觉与交互、生产部署、生产真实多轮、正式业务验收;这些层级不得互相替代。