Files
story/docs/content-memory.md
T
2026-08-29 21:56:51 +08:00

18 KiB

字幕、故事审核与已确认记忆设计

1. 目标与边界

本批次实现“最终转写入库—通话整理—故事候选—人工审核—已确认记忆—索引生效”的完整业务链。系统把模型输出始终视为候选内容;只有具备story.review能力的家庭成员明确确认,并且长者当前存在有效personalized_memory授权时,候选才可转为长期记忆。

当前抽取器支持两类可切换适配器:默认的local-deterministic-extractor用于本地业务验证,ark:<model-id>通过火山方舟OpenAI兼容Chat Completions接口生成结构化候选。向量生成与查询同样支持local-deterministic-hash-v1和ark:<embedding-model-id>两类适配器,并强制使用数据库既定的1536维。生产配置门禁禁止内容生成、索引或查询继续使用本地适配器;真实方舟账号效果仍需外部验收。

2. 功能设计

功能 输入 处理规则 输出
最终话轮接收 供应商签名回调、最终话轮数组 只接受voice_chat.transcript.final/FINAL;校验话轮键、序号、角色、时间和正文;按conversation_id + turn_key去重;同键不同内容拒绝 conversation_turn
通话整理 已结束通话、冻结授权快照、最终话轮 无story_extraction授权时不处理;只使用长者话轮作为故事和摘要来源;同一生成版本幂等 通话摘要、开放话题、故事候选、事实候选
方舟模型调用 本地安全策略通过的长者话轮 使用Bearer API Key调用${ARK_BASE_URL}/chat/completions;要求结构化JSON;网络、超时、408/425/429/5xx可靠重试,永久4xx进入死信 带模型编号、提示词版本和置信度的候选结果
分层内容安全过滤 长者最终话轮及模型结构化输出 验证码、支付账户、提示注入和高敏医疗先在本地拦截且不外发;其余输入在生成前、模型输出在落库前调用托管策略;BLOCK/REVIEW改为受控提示,策略服务异常时可靠重试且不落库 策略版本、非敏感提示和可审计请求引用
故事修订 候选故事、人工正文、原因、强ETag 新增人工修订版本并复制来源关系,不覆盖模型版本;终态故事禁止再改 新的story_revision和IN_REVIEW状态
故事确认 当前故事、审核原因、强ETag 必须有原话来源、具备审核权限且个性化记忆授权当前有效;确认故事及候选事实 CONFIRMED故事和PENDING记忆
故事拒绝 当前故事、审核原因、强ETag 拒绝故事及仍为候选状态的事实,不创建记忆 REJECTED故事
事实候选细粒度审核 单条事实、修订内容、敏感级别、问题类型、原因、强ETag 事实可独立编辑、确认、拒绝或停用;每次变化保存不可覆盖版本,不要求整篇故事一起处理 新的fact_candidate_revision与质量反馈
记忆索引 待索引记忆版本 再次校验记忆为当前版本、状态有效且授权仍有效;不满足条件时清空向量并标记STALE ACTIVE或STALE索引
生产向量化 普通敏感级别的当前记忆正文或新通话检索语句 API与Worker使用同一方舟Embedding模型;请求1536维float向量并校验维度和有限数值;网络、超时、408/425/429/5xx可靠重试 ark:<model-id>索引或固定边界查询向量
记忆修订与停用 当前记忆、正文或状态、原因、强ETag 正文变化创建新版本并立即使旧索引失效;停用后不再召回;重新启用必须重新校验授权并重新索引;PENDING_DELETE/SUPERSEDED/DELETED均禁止修改 新记忆版本或状态
记忆永久删除 当前记忆、删除原因、强ETag 事务内先进入PENDING_DELETE并清空活动向量,使新通话立即停止召回;该状态不可通过修改接口恢复;Outbox后台把索引标记删除、清除所有记忆版本正文并记录完成审计 DELETED记忆和索引
新通话记忆检索 服务端已确定的客户组织、长者和通话目标 SQL同时限制租户、长者、当前授权、活动记忆、当前版本、普通敏感级别、有效期和活动索引;最多5条 冻结到context_snapshot.memory_versions的版本引用和最小背景摘要
通话中记忆工具 供应商持有的单通话短期签名令牌、自然语言查询 令牌只绑定服务端既有通话和任务;租户、家庭和长者边界从数据库反查,模型不能传入或扩大;单条240字、总计800字、最多5条;Embedding失败返回空结果并标记降级,不中断通话 /internal/v1/tools/memory-search、memory_tool_call和实际使用追踪
可选供应商原生记忆 当前人工确认记忆、有效授权、普通敏感级别、受控HTTP适配器 数据库始终是主数据;开启后用Outbox同步,修订先清理旧引用,停用、授权撤回、单条删除和主体删除均可靠删除并对账;关闭功能后通话继续走数据库检索 provider索引的PENDING/ACTIVE/FAILED/DELETED状态
内容质量反馈 故事、事实或记忆的人工确认、修改、拒绝、停用及九类标准问题 不复制私密正文;按生成模型和提示词版本聚合候选数、审核动作和问题类型;跨租户统一拒绝 维护端“内容质量”与content_review_signal
跨通话连续上下文 最近已完成通话的就绪摘要、当前授权快照、交流偏好 只有story_extraction和personalized_memory在新通话冻结快照中同时有效时,才按同租户同长者读取最近3次摘要;读取结果每条最多240字,供应商上下文按每条140字压缩且总计不超过1000字;撤回任一授权后不再注入 冻结到新通话context_snapshot.sanitized_summary的短期连续上下文
避免主动提及 长者档案conversation_preferences.avoided_topics 最多8项、每项50字;从下一次新通话起进入冻结快照;仅限制智能体主动开启话题,长者主动提及时温和转向,紧急安全响应不受限制 新通话的交流边界
授权撤回 personalized_memory授权 在同一事务内停用该长者的活动记忆,清空活动/待处理索引向量并标记失效 DISABLED记忆、STALE索引

3. 业务流程

flowchart TD
    A[供应商最终转写回调] --> B{字幕保存授权允许?}
    B -- 否 --> C[不保存转写正文]
    B -- 是 --> D[校验并去重最终话轮]
    D --> E[通话进入COMPLETED或FAILED]
    E --> F{故事整理授权允许?}
    F -- 否 --> G[结束且不生成候选]
    F -- 是 --> H[仅提取长者话轮]
    H --> I[摘要与开放话题]
    H --> J[故事和事实候选]
    J --> K{家庭成员人工审核}
    K -- 修订 --> L[新增人工版本并保留来源]
    L --> K
    K -- 拒绝 --> M[故事和候选事实拒绝]
    K -- 确认 --> N{来源完整且记忆授权有效?}
    N -- 否 --> O[返回冲突且不建记忆]
    N -- 是 --> P[确认故事与事实]
    P --> Q[创建记忆版本和待处理索引]
    Q --> R{后台再次校验当前状态与授权}
    R -- 合格 --> S[索引ACTIVE]
    R -- 不合格 --> T[清空向量并标记STALE]
    S --> U[创建下一次通话]
    U --> V[数据库固定边界检索]
    V --> W[冻结记忆版本和背景摘要]
    S --> X{供应商记忆功能已验证并开启?}
    X -- 是 --> Y[可靠同步外部引用]
    X -- 否 --> Z[只使用数据库检索]
    Y --> AA[停用/撤回/删除时外部删除对账]

供应商停止事件和最终转写事件可能存在先后竞争。停止事件会发布内容整理待办;如果话轮尚未到达,处理器返回可重试错误,由Outbox退避重试,避免把“暂无话轮”错误当成永久空内容。

内容处理器先在无事务状态读取已结束通话、冻结授权和有序最终话轮,执行本地敏感内容判断;只有普通内容才调用外部模型。模型返回后才开启短事务并锁定通话,重新读取状态、授权和全部话轮并比较SHA-256输入指纹。若模型调用期间授权撤回,则不写入结果;若话轮发生变化,则本次任务进入可靠重试并基于新输入重新生成。该顺序满足“数据库事务不得跨越大模型网络调用”的需求约束。

记忆索引也先在无事务状态读取当前版本和授权,事务外生成Embedding,返回后才锁定记忆与索引行重新校验;修订、停用或撤回授权发生在模型调用期间时,旧结果不会被激活。新通话创建前仅在设备绑定、语音授权和个性化记忆授权均可用时生成查询向量;事务内仍以固定租户、长者、授权、状态和敏感级别条件检索。Embedding临时不可用时,本次通话以pgvector-degraded-v1记录降级且不注入记忆,不阻断基本语音通话。普通顺序幂等重放和同键正文冲突会先读取已完成幂等记录,不重复调用Embedding;并发首请求仍由数据库唯一键保证只创建一个通话。

通话中工具令牌由平台在启动智能体时签发,令牌只包含通话编号、固定供应商任务编号和过期时间。工具接口先重新读取权威通话、任务、租户和长者关系,再执行与通话前召回相同的数据库边界;请求正文没有租户、家庭或长者字段。查询摘要、结果数量、延迟和降级状态进入最小审计,原始查询和记忆正文不进入普通日志。供应商原生记忆是可选增强,不取代该工具或通话前数据库召回;外部服务失败时,本地主数据和本地召回仍可继续工作。

最近通话摘要是短期连续上下文,不会自动升级为长期记忆,也不会绕过故事人工审核。平台在每次开房时重新检查并冻结当前授权,只读取同一客户组织、同一长者、已完成通话的最新就绪摘要;隐私删除或授权撤回后的摘要不会继续进入后续通话。纯端到端豆包模式把这份冻结上下文写入S2SConfig.ProviderParams.dialog.system_role,级联模式继续写入LLMConfig.SystemMessages,两种链路使用同一上下文快照。

4. 接口设计

方法与路径 权限 并发/校验 说明
GET /api/v1/conversations/{id}/turns conversation.read HMAC游标、1—100条 最终话轮分页
GET /api/v1/conversations/{id}/summaries conversation.read 资源隔离 摘要和开放话题
GET /api/v1/subjects/{id}/stories subject.read 可按状态过滤 故事列表,不展开子项
GET /api/v1/stories/{id} subject.read 返回强ETag 当前故事、来源和事实
PATCH /api/v1/stories/{id} story.review 必须If-Match;正文和原因必填 创建人工修订版本
POST /api/v1/stories/{id}/confirm story.review 必须If-Match;来源与授权门禁 确认故事并创建记忆
POST /api/v1/stories/{id}/reject story.review 必须If-Match;原因必填 拒绝候选
PATCH /api/v1/stories/{story_id}/facts/{fact_id} story.review 必须If-Match;单条事实状态机、原因及可选标准问题类型 独立编辑、确认、拒绝或停用事实
GET /api/v1/subjects/{id}/memories subject.read 过滤已删除记录;可用q在固定长者边界内搜索 已确认记忆列表
GET /api/v1/memories/{id} subject.read 返回强ETag 当前记忆版本和索引状态
PATCH /api/v1/memories/{id} story.review 必须If-Match;正文/状态至少一项且原因必填;删除中及历史终态拒绝修改 修订、停用或重新启用记忆
DELETE /api/v1/memories/{id} story.review 必须If-Match;删除原因必填;返回202 可靠受理永久删除任务
POST /internal/v1/tools/memory-search 单通话短期Bearer令牌 固定通话/任务/租户/长者边界;限流、长度和结果上限 通话中受控记忆检索,失败安全降级
GET /api/v1/ops/content-quality ops_viewer及以上 必须选择有权客户组织;不返回正文 按模型和提示词版本聚合审核反馈

统一冲突码包括STORY_SOURCE_REQUIRED、PERSONALIZED_MEMORY_CONSENT_REQUIRED和STORY_REVIEW_STATE_CONFLICT。跨租户或非家庭成员访问统一返回403,不借404差异泄露资源是否存在。

5. 数据库设计

表 作用 关键约束
conversation_turn 最终话轮 通话内turn_key和sequence_no唯一;角色、时间顺序和正文长度约束
conversation_summary 摘要与开放话题 通话、类型、生成版本唯一;必须有源话轮
story_draft 故事聚合根和审核状态 通话与生成版本唯一;强版本号;指向当前修订
story_revision 不可覆盖的故事版本 故事内修订号唯一;区分模型/人工作者;保存基础版本和原因
story_source 故事版本到原话的追溯关系 版本、话轮和文本偏移组合唯一
fact_candidate 可审核事实候选 区分长者陈述与模型推断;保存源话轮、敏感级别和状态
fact_candidate_revision 事实不可覆盖版本 每条事实版本号唯一;保留内容、状态、原因和标准问题类型
curated_memory 长期记忆聚合根 当前版本、状态、有效期、敏感级别和强版本号
memory_version 不可覆盖的记忆版本 记忆内版本号唯一;确认人、确认时间、原因、来源和内容摘要
memory_index_entry 记忆检索索引状态 记忆版本和索引类型唯一;向量维度1536;状态可失效和删除
conversation_memory_use 实际记忆使用追踪 按通话、版本和使用方式唯一,不复制正文
memory_tool_call 通话中工具可观测性 只保存查询摘要、数量、状态、延迟和引擎模式
content_review_signal 内容质量反馈 按租户和长者隔离;保存资源、动作、问题类型、模型和提示词版本,不复制正文

内容与记忆数据库结构目前覆盖到V41。迁移在独立临时数据库执行up → down 1 → up并核对最终版本与dirty=false,测试数据库随后删除,不对开发或生产数据库做回滚验证。

6. 安全、隐私与可观测性

  • 供应商事件记录只保存事件类型、状态、时间和话轮数量,不把转写正文复制进事件审计正文。
  • 普通故事生成前执行本地敏感规则基线;验证码、支付账户和高敏感医疗信息默认不进入普通故事。该规则集只是生产内容安全服务接入前的最低门禁。
  • 内容生成API Key由Worker环境注入,Embedding API Key由API和Worker环境注入;密钥不写入数据库、候选记录、HTTP响应或错误正文,业务记录只保留ark:<model-id>和提示词版本。
  • Embedding API Key由API和Worker环境注入;记忆只记录模型引用,不记录密钥。只有普通敏感级别、当前有效且已授权的记忆进入外部向量化和语音召回。
  • 只有冻结快照允许保存字幕时才落conversation_turn;故事整理和个性化记忆分别使用独立授权门禁。
  • 故事、事实和记忆均保留租户、长者和来源边界;所有网页接口从OIDC主体重新解析家庭成员关系,不信任客户端传入租户。
  • 人工修订、确认、拒绝和授权撤回记录审计事件;写操作使用强ETag防止并发覆盖。
  • 通话创建和通话中工具均使用服务端固定的租户、长者、授权和版本边界;供应商或模型不能传入自由租户、家庭或长者过滤条件。
  • 供应商原生记忆默认关闭,只有完成真实账号的数据条款、隔离、同步、删除和容量验证后才配置PROVIDER_MEMORY_MODE=managed_http;数据库仍是唯一主数据。

7. 验证结果

  • 新增迁移已在隔离数据库完成升库与数据库组合测试;OpenAPI 0.70.0可生成TypeScript类型,React类型检查和生产构建通过。
  • 本地内容策略单元测试覆盖验证码、银行卡号、高敏感医疗词和普通生活经历。
  • 方舟兼容适配器轻量验证覆盖Bearer鉴权、请求路径、结构化响应校验、HTTP 429重试分类;真实PostgreSQL组合验证模型调用期间不持有通话行锁,模型返回后正常生成候选,敏感话轮不会调用外部模型。
  • 方舟Embedding适配器轻量验证覆盖Bearer鉴权、/embeddings请求、模型/维度参数、1536维结果和错误维度拒绝;真实PostgreSQL组合验证向量生成期间不持有索引行锁,返回后可激活索引。
  • 端到端通话冒烟验证最终话轮去重、两类整理结果、故事来源、人工确认前零记忆、确认后记忆生成及索引生效;下一次通话冻结1个记忆版本。
  • 记忆修订生成第2版并重新索引;停用后索引立即失效且新通话召回0条;重新启用再次进入索引流程。
  • 永久删除验证覆盖PENDING_DELETE → DELETED、删除中重新激活稳定拒绝、向量清理、数据库正文替换为删除标记和删除后新通话召回0条。
  • 通话中记忆工具验证覆盖短期令牌签名、固定通话/任务边界、跨长者不可选、结果长度上限、实际使用追踪和Embedding异常降级。
  • 供应商记忆适配器验证覆盖Bearer鉴权、幂等同步、旧版本删除、单条删除、主体隐私删除前对账和失败状态保留;当前只使用模拟HTTP/内存适配器,真实供应商账号仍待外部验收。
  • 内容质量反馈验证覆盖九类问题、事实不可覆盖版本、按模型/提示词聚合和跨租户拒绝;维护端只展示汇总,不显示故事、事实或记忆正文。
  • React故事与记忆页以页面内管理区提供正文修订、启停和永久删除;未修改正文、空原因、未确认删除均不能提交,删除中和历史终态只读,不再依赖浏览器弹窗。
  • OIDC、SaaS、设备、成员和通话五组冒烟全部通过;测试结束后内容表残留为0,Outbox无待处理、处理中或死信事件。
  • 当前方舟内容与Embedding协议均使用本地模拟端点验证,尚未使用业务方真实API Key核验模型效果、召回质量、额度和数据条款;真实语音供应商与真实设备也仍待验收。