# Hermes、本体解析与财务报告多租户安全 概念文档 更新时间:2026-07-17 ## 功能一句话 让本体解析、员工行为画像、Hermes 扫描、数字员工看板和定时财务报告从请求到存储全程绑定可信企业,并在任何租户上下文缺失或冲突时停止运行。 ## 背景与问题 本体解析曾在建立 `AgentRun` 前调用模型,并从全局员工、组织、客户、供应商、项目和单据字典构造提示词;员工画像详情可按任意员工 ID 查询;Hermes 扫描、看板和财务报告存在全表读取、全局收件人以及跨企业复用存储路径的风险。内部 Orchestrator 调用还可在没有认证用户时继续运行,无法证明 `AgentRun` 的企业归属。 ## 目标与非目标 ### 目标 - HTTP 入口只信任 `CurrentUserContext.tenant_id`,内部任务只接受显式、已注册且启用的 `trusted_tenant_id`。 - 本体解析在任何模型调用前创建租户化 `AgentRun`,商业运行上下文和失败证据可追溯。 - 员工、组织、费用、应收、应付、画像、风险、提醒和看板查询在首个 SQL 中限定租户。 - 员工画像仅允许本人、直属领导、财务/高管或管理员读取;越权和跨租户统一返回 404。 - 财务报告按租户配置收件人、生成内容、存储文件和幂等运行账本,不使用全局邮件回退。 - 所有 Hermes 定时任务逐个枚举 `status=active` 的企业,运行记录同时在 route/ontology 保存租户快照。 ### 非目标 - 不提供客户端选择或覆盖租户的兼容参数。 - 不实现跨企业合并分析、集团穿透报表或平台运营后台。 - 不在本切片中重构预算分配等仍属旧模型的全部业务域。 ## 信任边界 ```text HTTP 请求 ── CurrentUserContext.tenant_id ─┐ ├─→ tenant-scoped AgentRun 内部任务 ── active trusted_tenant_id ──────┘ ├─ route_json.tenant_id └─ ontology_json.tenant_id ``` - 客户端 `context_json.tenant_id` 会被可信租户覆盖,不能参与授权。 - 同时传入登录用户和内部租户时,两者必须一致。 - 内部任务租户必须存在于租户注册表且状态为 active;缺失、停用或不存在均在创建 Run 前拒绝。 - 后台消费者从数据库 Run 恢复租户,并校验 route/ontology 两份快照一致。 ## 功能设计 ### 本体解析 - 接口覆盖请求中的 user id,并把认证企业传给 `SemanticOntologyService`。 - `parse()` 先建立 `running/pending_model_analysis` Run,再加载租户字典、商业运行上下文并调用模型。 - 模型、规则降级和失败路径都更新同一 Run;失败不会留下无企业、无状态的调用。 - 员工、部门、费用申请、应收、应付和项目参考目录均以租户作为第一层 SQL 条件。 ### 员工画像 - 快照保存 `tenant_id`,并用 `(tenant_id, subject_id)` 复合外键绑定员工。 - Profile Service 的快照、费用单和 Agent Run 查询均先限定租户。 - 详情接口先在当前企业解析目标员工,再执行本人/直属领导/财务/高管/管理员访问矩阵。 - 附带 `claim_id` 时必须同时属于目标员工和当前企业。 ### Hermes 与数字员工 - 风险扫描、画像扫描、风险线索、提醒扫描和看板均接受显式租户。 - 调度器只枚举 active 企业,并为每个企业独立创建任务与结果。 - 看板只统计 route/ontology 租户快照均匹配的 Run;缺少或冲突快照的历史记录不会被猜测归属。 - 提醒任务的费用单、员工和关联报销查询在首个 SQL 限制当前企业。 ### 财务分析报告 - `TenantFinanceReportConfig` 保存企业启停状态和经校验的收件人。 - `TenantFinanceReportRun` 以 `(tenant_id, idempotency_key)` 防止同企业同周期重复发送。 - 报告上下文只读取当前企业的费用、风险、画像和 Agent Run。 - 邮件发送只使用当前企业配置;未配置、停用或无有效收件人时 fail-closed。 - 文件存储目录使用企业标识哈希,避免路径注入和跨企业文件覆盖。 ## 数据与迁移 `20260717_0028_hermes_ontology_tenant_security.py` 完成: - 员工行为画像、Hermes 任务配置/日志和风险报告新增租户列、索引、唯一约束和复合外键。 - 新增租户财务报告配置和周期运行账本。 - 旧记录回填为 `default`,再移除 server default,后续写入必须显式确定企业。 - 仅支持 PostgreSQL;upgrade/downgrade 在任何 DDL 前检查方言,其他数据库直接拒绝。 - downgrade 在存在非默认企业事实或报告账本时拒绝有损回滚。 ## 降级与回滚 - 租户缺失、停用、不存在或上下文冲突:不创建 Run、不查询业务数据。 - 跨企业员工、画像、费用单:统一按不存在处理。 - 企业未配置报告收件人:保留失败/跳过证据,不回退环境变量中的全局地址。 - 历史 Run 没有双租户快照:看板不纳入企业统计。 - 生产升级前必须在数据库备份副本演练 `0027 → 0028 → 0027 → 0028`。 ## 测试与验收 - 两企业本体目录、画像 IDOR、Hermes 扫描、风险线索、看板和报告内容/收件人/路径/幂等隔离。 - Orchestrator 缺租户、停用租户、不存在租户和认证/内部租户冲突均在 Run 前拒绝。 - 本体模型调用前存在 running Run,失败后仍保存同企业失败证据。 - PostgreSQL 完成 fresh `base → 0028`、`0028 → 0027 → 0028` 及旧表回填/外键验证。 - 所有后端测试和 Ruff 均在 `local-x-financial-linux` 容器内执行,单命令不超过 60 秒。 ## 本轮实现记录 - 2026-07-17:完成本体解析前置 Run、可信商业上下文和租户参考目录。 - 2026-07-17:完成员工画像模型、服务和 API 的租户隔离与访问矩阵。 - 2026-07-17:完成 Hermes 扫描、提醒、看板、调度和财务报告的逐租户运行。 - 2026-07-17:完成 `20260717_0028` PostgreSQL 迁移、反向回滚和安全回归。