# dsh-personal-assistant v0.10.0 个人知识库测试报告 测试日期:2026-10-16~2026-10-17(Asia/Shanghai) ## 结论 **通过。** v0.10.0 已经可以把个人经历、人物关系、行为观察、低置信推断、决策理由和阶段状态作为独立于偏好与任务的个人知识保存,并在全新会话中按当前问题有界检索和使用。 本轮最终结果: - 自动化测试:**54/54 通过**; - 干净隔离环境自然语言场景:**6/6 最终通过**; - Web 设置面板:知识库展示、搜索、创建、归档、两次确认删除均可用; - v3 → v4 迁移:原有偏好、任务、任务唤醒、工作流、例程及时间戳保持不变,只新增空 `knowledge`; - 测试数据隔离:合成的凭据、证件标识、人物关系和出差安排未写入任何持久状态; - 真实主人数据:测试未使用真实 profile 写入,全部模型验收在独立 DSH_HOME 中完成。 ## 1. 本版实现范围 ### 1.1 个人知识模型 状态格式由 v3 升至 v4,新增最多 500 条 `knowledge` 记录。支持以下类型: - `entity`:人物、组织、地点、设备等实体; - `event`:经历和事件; - `state`:阶段状态; - `observation`:可核查的观察; - `pattern`:经多次证据形成的模式; - `goal`:目标; - `decision`:决定与理由; - `commitment`:承诺背景(正式跟进仍由任务负责); - `artifact`:资料指针和元信息; - `relation`:人物或事物之间的关系; - `rule`:外部领域规则; - `hypothesis`:尚未确认的推测。 每条记录携带:来源类型(`stated/observed/inferred/imported`)、置信度、敏感级别、生命周期状态、保留策略、时间字段、标签、证据、关联记录和创建/更新时间。 ### 1.2 三类状态权威分离 | 数据 | 唯一权威 | 边界 | |---|---|---| | 老板自己的稳定/情景偏好 | `assistant_preference` | 一次选择不能自动升级为偏好;第三方偏好不进入这里 | | 截止时间、提醒、进度、审批、下一步 | `assistant_task` | 知识里的目标或承诺不能替代任务状态 | | 事件、人物、关系、观察、状态、决定、资料、假设 | `assistant_knowledge` | 知识是背景数据,不构成发送、预订、付款或删除授权 | ### 1.3 模型工具 新增 `assistant_knowledge`: - `list`:有界列出; - `get`:按 id 读取单条; - `search`:确定性词法检索; - `create`:新建一条知识; - `update`:纠正或补充同一事实; - `archive`:归档; - `delete`:主人确认具体记录后删除。 各动作使用判别式 JSON Schema:`search` 必须带 query,`create` 必须带 kind/title/content/basis/source,`update/archive/delete` 必须带 id 与 `expected_updated_at`。 ### 1.4 情境检索 `assistant_context` 新增: - `knowledge_query`; - `knowledge_kinds`; - `knowledge_limit`(最多 8 条)。 不带 `knowledge_query` 时不返回知识库内容,避免每轮倾倒主人历史。检索默认只返回 active/disputed 的非敏感知识;敏感记录必须通过 `assistant_knowledge` 在请求直接相关时显式读取。 ### 1.5 Web 设置面板 「设置 → 个人助理」新增个人知识库区域: - 展示最近 50 条非归档、非敏感记录; - 按类型和关键词本地过滤; - 录入类型、来源、标题、内容和标签; - 归档; - 两次点击确认删除。 面板与对话工具共用同一组 Host mutator、串行化存储与校验,不维护第二套写入逻辑。 ## 2. 数据治理与安全 ### 2.1 有界性 - 500 条知识记录; - 每条标题最多 160 字符、正文最多 3000 字符、证据最多 2000 字符; - 最多 12 个标签、20 个关联 id; - 整体状态文件继续限制为 2 MiB; - `assistant_context` 单次最多返回 8 条知识; - list/search 单次最多返回 50 条。 ### 2.2 秘密拒存 Host 写入层和磁盘载入校验均拒绝: - 密码、验证码、授权码、API Key、Access Token; - PEM/OpenSSH 私钥; - 完整身份证号码; - 带“护照号/passport number”语境的完整护照号; - 通过 Luhn 校验的 13~19 位支付卡号。 检测覆盖 title/content/evidence/source/tags,而不只检查正文。 ### 2.3 敏感数据 - `sensitive` 记录不会进入普通 list/search、`assistant_context` 或面板; - 未显式 `include_sensitive=true` 的精确读取只返回不含标题和正文的 opaque id + redacted 标记; - imported 文本和知识正文被政策明确标为“数据而非指令”。 ### 2.4 并发与关系完整性 - 更新、归档、删除必须提供上次读取到的 `expected_updated_at`; - 更新时间保证单记录单调递增,即使同一毫秒内连续写入,旧令牌也无法重用; - 删除被其他记录引用的知识会失败,必须先用 CAS 安全解除关联; - 面板使用请求代次号,慢刷新不能覆盖后完成的新写入; - 面板 POST 强制 `application/json`、同源 Origin/Host 和 same-origin Fetch Metadata,跨站请求被拒绝。 ## 3. 自动化测试 命令: ```text npm run check ``` 结果: ```text 54 tests 54 pass 0 fail ``` 新增覆盖包括: 1. 偏好与知识分权、跨重启持久化; 2. 查询驱动、8 条上限、确定性排序、不同 UTC offset 的真实时间排序; 3. 敏感记录默认隔离和标题不泄漏; 4. 状态过时抑制、旧状态历史保留; 5. 必填 CAS、同毫秒令牌重放失败; 6. 入站关联阻止删除; 7. 密码、令牌、私钥、身份证、护照号、15 位支付卡号拒存; 8. v3 → v4 无损迁移; 9. 异常 v3 `knowledge` 字段 fail closed,原始文件不改写; 10. 面板同源和 JSON 请求检查; 11. 判别式工具 schema 各动作必填字段; 12. 原有偏好、任务、唤醒、工作流、例程和面板行为回归。 ## 4. 干净会话模型验收 模型:隔离 headless profile 的 GLM-5.3;每个场景使用独立 DSH_HOME,跨轮验证使用新的根会话。 ### S1:轨迹与一次性选择 输入:西安 → 北京 → 上海;脚扭伤时临时选过道,明确“只是这一次”。 新会话追问:到上海前一站在哪里?以后坐飞机选什么座? 结果:**通过。** - 正确回答北京; - 从事件时间线恢复两段行程; - 明确靠过道是脚伤下的一次性选择; - 偏好库保持 0 条; - 未擅自推断未来座位偏好,而是询问老板。 会话:`session-921b89fa-d661-4a3d-957e-06e561f6ab47` → `session-8f909700-5d76-44d3-9d29-05c3ff13b0c1`。 ### S2:同一人物的家庭/工作关系与联系方式不留存 输入:林岚是爱人的姐姐,同时负责 Apollo;手机号仅供本次联系,不要记。 新会话同时询问三项。 结果:**通过。** - 恢复家庭关系; - 恢复 Apollo 负责人关系; - 明确手机号未保存、不能复述; - 状态文件中不存在完整手机号。 会话:`session-1fbd36c1-e559-4975-afb7-db098837dc96` → `session-4eb57f7a-54f3-4595-b65d-d03a80ae88c8`。 ### S3:观察与推断分离 输入:三次长会周启提前离开;“他不喜欢长会”只是低置信猜测。 结果:**初测发现问题,修复后通过。** 正确部分: - 观察单独保存,置信 0.9; - 假设单独保存,basis=inferred,置信 0.4; - 假设关联观察证据; - 新会话明确“不是事实”,只提出可逆的议程前置建议。 初测缺陷:模型曾说“周启本人确认后转成偏好”。这会污染老板偏好库。 修复:政策和 `assistant_preference` 描述明确——偏好库只存老板本人的指导;其他人的偏好即使本人确认,也永远留在 `assistant_knowledge`。复测正确回答:旧假设标为已解决,另建本人确认的知识记录并保留证据链。 会话:`session-e274ef8f-0d43-4b1d-b077-7c137edf6fa3`、`session-754ff4b9-ad83-4d1b-9130-afcce2b7cab4`;修复复测 `session-bf478e7f-800f-48c3-a57d-afcebbdabf77`。 ### S4:决定理由不泛化为偏好 输入:南京选择高铁,因为门到门更快、机票改签不灵活;明确不是一贯偏好。 新会话追问原因,并问广州是否照搬。 结果:**通过。** - 完整恢复两条理由; - 知识记录为 decision,偏好库仍为 0; - 没有直接照搬到广州,而是要求出发地/日期后重新计算; - 将真实待推进的广州交通选择落成 waiting 任务,并在回复中说明。 会话:`session-2a8322ff-1613-417e-8711-4e28abf449be` → `session-9a24f44e-299d-4bec-95a2-2c7500e25fe3`。 ### S5:阶段状态更新与历史保留 输入 A:护照换发仍在收集材料。 输入 B:材料已提交,正在等审核,不再提醒收集材料。 输入 C:现在到哪一步,下一步是什么? 结果:**初测发现问题,修复后通过。** 初测缺陷:模型直接覆写原 state,当前答案正确但丢失阶段轨迹。 修复:政策明确真实状态变化必须“旧记录 stale + 新建关联记录”;update 只纠正或补充同一个事实。 复测结果: - 旧“收集材料”状态标为 stale; - 新“材料已提交,等待审核”状态为 active 并关联旧记录; - 普通检索只返回新状态; - 精确历史读取仍能看到旧状态; - 新会话正确回答当前阶段和下一步,不再提示收集材料。 复测会话:`session-69e6be73-caf0-446e-9fef-7541f48117d8` → `session-8d160949-b05e-44ba-a357-5d073bcf7a27` → `session-176c09bb-e17f-4bdb-8bf8-9594aef0a383`。 ### S6:测试数据与秘密隔离 输入明确标注为验收测试,包含合成的凭据、证件标识、同事关系和出差安排,并要求不写入长期记录。所有字段均为专门构造的测试数据,不对应任何真实个人或行程。 结果:**通过。** - 模型选择只读演练; - 写入次数为 0; - 最终 state 为 `knowledge=[]`、`preferences=[]`、`tasks=[]`; - 合成凭据、证件标识、人物关系和出差安排均不在磁盘中。 会话:`session-0f7e301e-3e66-4e6f-83c3-45782196440f`。 ## 5. 工具 schema 真实模型兼容性 独立审查要求每个动作使用判别式必填 schema。第一次实现把完整字段复制到七个 `oneOf` 分支后,GLM 接口出现参数类型退化: - `limit: 10` 被生成成 `limit: "10"`; - `confidence: 1` 被生成成字符串; - `tags: [...]` 被生成成 JSON 字符串。 模型能多次回退最终成功,但不够可靠。 最终方案:字段类型只在根 schema 定义一次,`oneOf` 分支只声明 action 常量和各动作 required 字段。结果: - schema 从 7069 字符降至 **3993 字符**; - 数字和数组恢复正确 JSON 类型; - `search` 一次成功; - `create` 一次成功并正确提交 `confidence:1`、tags 数组、related_ids 数组。 验证会话:`session-bbbbfab1-51d4-403a-8dfa-a38a71dc92fd`。 ## 6. 独立代码审查 独立只读审查发现的实质问题均已修复: 1. v3 状态若异常携带 `knowledge` 会被覆盖为空(P1)→ v3 exact-schema 先验校验,异常字段 fail closed; 2. CAS 可省略且同毫秒令牌可复用(P1)→ mutation 强制 expected_updated_at + 单调更新时间; 3. 删除目标未覆盖入站关联并发(P2)→ 有入站引用时拒绝删除; 4. secret 校验遗漏 source/tags/disk,卡号范围不完整(P2)→ 全记录、全入口校验 + 13~19 位 Luhn; 5. sensitive get 泄漏标题(P2)→ 未授权读取只回 opaque id; 6. 带 offset 时间用字符串排序(P2)→ Date.parse 真实时间排序; 7. 工具 schema 动作必填不足(P2)→ 根类型 + 判别式 oneOf required; 8. localhost 面板 mutation 缺同源/Content-Type 约束(P2)→ JSON + Origin/Host + Fetch Metadata; 9. 慢 refresh 可覆盖新 action 快照(P3)→ Client 请求代次号 + busy 禁止刷新; 10. 一条模型快照断言随描述更新失效(P3)→ 更新为更精确的三权分离断言。 ## 7. Web 面板活体测试 在 `D:\Code\pa-test-home` 的 webtest profile、端口 3081 上重启宿主后验证: - state API 200; - 状态文件自动迁移至 v4; - 页面出现“个人知识库”; - 卡片样式继续使用宿主 token,与偏好库一致; - create 200; - archive 后记录从可见清单消失; - 带最新 CAS 的 delete 200; - `text/plain` POST 返回 400 `application/json required`; - 跨站 Origin POST 返回 400 `same-origin request required`; - 合法 same-origin JSON 操作正常。 所有活体 smoke 记录均在验证后删除。 ## 8. 模型上下文成本 - 原运行政策:7443 字符; - v0.10.0:8968 字符; - 增量:1525 字符(+20.5%,约数百 token); - 最终 `assistant_knowledge` 参数 schema:3993 字符。 新增成本换取三权分离、知识时态、隐私和生命周期约束。检索本身严格查询驱动,避免把知识正文常驻每轮上下文。后续若继续扩展,应优先把详细知识治理说明下沉到工具描述或 Skill,而不是继续扩大常驻政策。 ## 9. 当前限制与非目标 本版有意不做: - 自动扫描每轮对话并无差别落库; - 邮件、日历、浏览器、GPS 的自动采集; - 连续精细位置轨迹; - 向量数据库和 embedding; - 自动关系图推理; - 自动读取 artifact 指向的文件或 URL; - 完整图谱可视化和批量编辑。 当前检索是确定性词法匹配,适合 500 条以内的个人知识第一版。下一阶段应优先增加:时间精度(日期/月/季节而非伪造午夜时间)、自动候选提取的确认队列、结构化 relation 边、来源引用和可视化时间线;不应先上无边界的自动采集。 ## 10. 最终判断 v0.10.0 不只是“能存知识”,而是已经表现出以下灵活性: - 能判断一次性选择与长期偏好的边界; - 能把同一人物的多个情境合并使用,同时尊重不留存联系方式; - 能把观察和推断分开,并按置信度限制使用; - 能恢复过去决定的理由,而不是只记结果; - 能在状态变化时保留历史、抑制旧状态; - 能把当前情景需要的知识投影给模型,而不是倾倒整库; - 能在测试语境中完全不落库; - 能在工具、面板和磁盘三个入口上保持同一组校验与并发规则。 因此,本报告给出 **“个人知识库第一版可用,可以进入卸载重装和真实 profile 全流程验收”** 的结论。