Files
prop-ai-hr/docs/KNOWLEDGE_PLATFORM_RUNBOOK.md
T

14 KiB

多租户知识空间平台运行手册

1. 适用范围与安全边界

本手册用于银城与美途作为两个独立 SaaS 租户的知识空间初始化、内容迁移、授权、外部应用联调和回滚。租户、调用应用和知识空间是三层独立边界:内部用户的有效查询范围取“当前租户 + 当前调用应用绑定空间 + 用户/角色授权空间”的交集;外部应用只能访问其所属租户内显式绑定的空间。

2026-07-18 平台代码、管理页和 schema 已随 32fa376d 发布,生产知识空间页面已打开复核。该状态不代表银城/美途正式租户、空间、授权矩阵、外部令牌和内容迁移已经业务验收;这些仍须按本手册逐项确认。生产执行知识平台迁移后,必须最后执行 aihr_20260718_release_collation_compat_mysql8.sql,防止历史库排序规则与新表混用。

禁止把集团账号、公司名称、客户端传入的 tenantId/userId/extPartyId 当作授权依据。禁止在浏览器、小程序包、日志、工单或文档中保存 API_TOKEN。回滚优先停用应用或解绑空间,不删除知识数据、附件或 OSS 对象。

2. 初始化前确认

2.1 确认生产租户 ID

先只读查询生产库,不凭公司简称猜租户 ID:

select tenant_id, company_name, status, del_flag
from sys_tenant
where del_flag = '0'
order by tenant_id;

由业务负责人确认银城、美途分别对应的 tenant_id 和公司全称,并确认二者不同。执行脚本还会再次查询并打印 tenant_id:company_name,操作者必须核对后才继续。

2.2 备份与迁移

先备份数据库,依次执行 backend/script/sql/update/aihr_20260716_knowledge_space_platform_mysql8.sql、backend/script/sql/update/aihr_20260722_knowledge_category_mysql8.sql;需要从原 OSS 文件恢复历史零片段图片/视频时,还必须执行 backend/script/sql/update/aihr_20260729_media_reprocess_mysql8.sql。迁移可重复执行,但生产仍应先在结构副本验证。执行后至少核对:

show tables like 'aihr_knowledge_%';
show create table aihr_knowledge_info;
show create table aihr_knowledge_app;
show create table aihr_knowledge_space_grant;
show create table aihr_knowledge_category;
show create table aihr_knowledge_upload_item;

3. 幂等初始化

先 dry-run,不连接数据库、不输出密码或令牌:

./scripts/provision-knowledge-platform.sh \
  --silver-tenant-id '<银城租户ID>' \
  --meitu-tenant-id '<美途租户ID>' \
  --dry-run

确认输出恰好包含银城 3 个空间、美途 1 个空间、3 个应用及预期绑定后,再实际执行:

AIHR_DB_HOST='<数据库地址>' \
AIHR_DB_PORT='3306' \
AIHR_DB_NAME='ry-vue' \
AIHR_DB_USER='<账号>' \
AIHR_DB_PASSWORD='<从安全环境注入>' \
./scripts/provision-knowledge-platform.sh \
  --silver-tenant-id '<银城租户ID>' \
  --meitu-tenant-id '<美途租户ID>' \
  --confirm

不设置 AIHR_DB_HOST 时,脚本使用本地 wygj-mysql 容器。脚本是幂等 upsert,可重复执行;它不会生成 API_TOKEN,也不会把现有细粒度知识库改名、搬迁或删除。

执行后核对:

select tenant_id, code, name, space_type, sensitivity_level, status
from aihr_knowledge_info
where code in ('yc_public_policy','yc_property_sop','yc_management_ops','mt_customer_service')
order by tenant_id, code;

select tenant_id, app_code, auth_type, internal_client_key, status, rate_limit_per_minute
from aihr_knowledge_app
where app_code in ('yc_admin','yc_mobile','mt_card_miniapp')
order by tenant_id, app_code;

mt_card_miniapp 初始化后必须是 DISABLED 且 token_hash 为空;银城与美途之间不得存在应用—空间绑定。

4. 空间、授权与应用管理

超级管理员从“平台设置 → 租户管理”维护租户主体;点击某个租户的“知识维护”会先切换到该租户,再进入知识空间页。管理端“知识空间”页面提供空间、空间内分类与知识、主体授权和调用应用管理。空间和分类编码创建后不可修改;停用空间会让检索立即失去该空间,但不删除资料。分类只组织当前空间内的附件成员关系,不新增授权,也不改变“当前租户 + 调用应用绑定空间 + 用户/角色授权空间”的查询交集。MANAGE 授权可以给用户或角色,READ 只决定主体能否读;SESSION 应用与主体授权取交集。

外部美途应用的启用顺序固定为:

  1. 核对只绑定 mt_customer_service。
  2. 核对空间只含批准对外发布的内容。
  3. 设置合理的每分钟限流和过期时间。
  4. 在管理端轮换令牌;明文只显示一次。
  5. 立即写入小程序云函数、API 网关或可信后端的密钥系统。
  6. 确认后端联调通过,再把应用状态改为 ACTIVE。

轮换令牌后旧令牌立即失效。发生泄露或暂停联调时,先把应用改为 DISABLED;需要恢复时重新轮换,不复用曾暴露的明文。前端、小程序源码和用户设备绝不能直接持有令牌。

5. 内容导入与 legacy 空间迁移

5.1 新内容导入

在 SOP 或资料处理页显式选择一个或多个可管理空间。多空间上传只解析一次、复用同一个 OSS 对象,但为每个空间建立独立附件成员关系和检索片段;上传后可在“分类与知识”页分别为各空间成员归类。解绑或停用一个空间不得影响另一个空间的内容。

导入后核对:

select a.tenant_id, k.code, a.doc_id, a.name, a.oss_id, a.status,
       count(f.id) fragment_count
from aihr_knowledge_attach a
join aihr_knowledge_info k
  on k.tenant_id = a.tenant_id and k.id = a.knowledge_id
left join aihr_knowledge_fragment f
  on f.tenant_id = a.tenant_id and f.knowledge_id = a.knowledge_id and f.doc_id = a.doc_id
group by a.tenant_id, k.code, a.doc_id, a.name, a.oss_id, a.status
order by a.id desc;

空间解绑会先删除当前成员、片段和向量点;只有跨租户附件引用计数为零时才尝试删除 OSS。对象存储删除失败不会恢复已解绑成员,而是留下可审计的孤立对象,按下列查询确认后由独立清理流程处理:

select o.oss_id, o.tenant_id, o.original_name
from sys_oss o
left join aihr_knowledge_attach a
  on a.tenant_id = o.tenant_id and a.oss_id = o.oss_id
where a.id is null and o.ext1 in ('aihr-knowledge','aihr-knowledge-staging');

5.2 零片段图片/视频排障与重试

资料处理页把 aihr_knowledge_attach.status in (0,3) 且片段数为 0 的图片/视频列为“待重试”。先确认视觉模型、视频 ASR/ffmpeg 和原 OSS 对象仍可读取,再从页面点击“重试”;服务端只接受当前租户、状态为待处理/失败且仍有 OSS 原件的图片或视频,通过 POST /api/knowledge/doc/processing-tasks/{attachmentId}/retry 重新暂存并写入异步队列。重复点击由附件状态 CAS 拒绝,不得直接把数据库状态改成完成或手工复制片段。

只读排障可使用:

select a.id, a.tenant_id, a.name, a.status, a.remark, a.doc_id,
       count(f.id) fragment_count
from aihr_knowledge_attach a
left join aihr_knowledge_fragment f
  on f.tenant_id = a.tenant_id
 and f.knowledge_id = a.knowledge_id
 and f.doc_id = a.doc_id
where a.id = <attachmentId>
group by a.id, a.tenant_id, a.name, a.status, a.remark, a.doc_id;

select id, batch_id, file_name, source_attach_id, status, error,
       fragment_count, update_time
from aihr_knowledge_upload_item
where tenant_id = '<当前租户>'
  and source_attach_id = <attachmentId>
order by id desc;

队列状态固定为 0=待处理、1=处理中、2=完成、3=失败。成功条件是最新队列条目为 2、附件为 2、片段数大于 0,并按当前向量配置确认相应片段已向量化;零片段必须保持失败态,不能显示为完成。普通失败条目只有在 72 小时暂存保留期内才可调用 POST /api/knowledge/doc/upload-items/{id}/retry;暂存已清理时应重新上传。历史附件重试不依赖旧暂存文件,而是从 OSS 原件重新入队。

5.3 细粒度 SOP 迁入 yc_property_sop

现有“投诉处理 SOP、催缴沟通 SOP、报修跟进 SOP”等 legacy_* 空间不直接改名。按以下顺序执行:

  1. 业务负责人导出现有空间的文件清单,逐项标记目标空间、密级、版本日期、维护人和是否允许复用。
  2. 通过多空间导入或 OSS 成员复用把批准内容加入 yc_property_sop,保留原 legacy 成员关系。
  3. 对新空间至少运行 10 个标准问题,逐题核对答案、文件标题、引用片段和空间编码。
  4. 核对新应用只绑定新空间,legacy 空间不向新应用授权。
  5. 业务确认后把不再使用的 legacy 空间改为 DISABLED,观察查询日志一个完整业务周期。
  6. 若出现缺失,重新启用 legacy 空间或恢复应用绑定;不要搬回附件、删除片段或清理 OSS。

6. 查询、日志与限流观察

知识底层内部入口为 POST /api/knowledge/query,移动端“问”的规范入口是 /api/aihr/agent/**,仅在 Agent 识别为知识/资源意图后调用该层。知识查询可带 conversationId/contextVersion 启用最新 6 轮、30 分钟不活跃过期的短期会话;外部入口 POST /api/open/knowledge/query 始终无状态。内部原文件/视频通过 GET /api/knowledge/resources/{attachmentId}/content 在下载时重新鉴权。外部请求使用 Authorization: Bearer <API_TOKEN>。查询日志只保存问题哈希、有效空间、来源类型、状态、耗时和提示版本,不保存完整问题或令牌。

aihr_knowledge_conversation 只保存脱敏截断后的短期上下文,过期记录由查询流量每 5 分钟惰性清理最多 500 条。可只读观察积压:

select count(*) total, sum(expires_time <= now()) expired, min(expires_time) oldest_expiry
from aihr_knowledge_conversation;
select tenant_id, app_id, status, source_types, count(*) calls,
       round(avg(latency_ms)) avg_latency_ms, max(create_time) last_time
from aihr_knowledge_query_log
group by tenant_id, app_id, status, source_types
order by last_time desc;

重点观察 REJECTED、FAILED、NO_EVIDENCE、连续 429 和异常高耗时。外部应用的限流依赖 Redis;Redis 不可用时外部查询应失败关闭并返回服务不可用,不能绕过限流继续调用。

6.1 总结卡证据绑定与排障

总结卡必须复用当前知识回答中的 DOCUMENT 引用,不接受客户端自行填写或历史回答残留的片段:

{
  "queryText": "厕所打扫的 SOP 是什么?",
  "category": "sop",
  "fragmentIds": [120535]
}

fragmentIds 最多 5 条。POST /api/knowledge/summary-card 会按当前租户、应用和主体授权重新校验,只基于仍有权限的片段生成固定「问题/建议/标准」结构,不再执行第二次检索。引用为空、越权、模型不可用或输出结构不合格都应返回明确错误;前端保留当前答案并允许重试,不展示原文伪造的步骤卡。

若知识回答正常但总结卡失败,先核对请求片段确实来自同一轮响应,再检查启用的 category='chat' 模型。DeepSeek 旧模型名或 SOP Prompt 未兼容部分证据时,按生产迁移 Runbook执行 aihr_20260725_deepseek_v4_model_mysql8.sql 与 aihr_20260725_sop_answer_partial_evidence_prompt_mysql8.sql,随后通过管理端连接测试和认证态查询—总结卡链路复核;不要在日志或工单中记录密钥、手机号或完整问题正文。

7. 验证与发布闸门

自动合同测试:

bash scripts/tests/provision-knowledge-platform.test.sh
node --test scripts/tests/knowledge-platform-security.test.mjs
npm --prefix mobile-uni run test:unit

本地手机号登录、多轮改写、原文件下载、视频空结果和旧版本冲突可执行:

AIHR_BASE_URL='http://127.0.0.1:8080' node scripts/verify-ask-memory-local.mjs

真实环境验证器从环境变量读取令牌,绝不写入命令脚本或仓库:

AIHR_BASE_URL='https://wygj-api.localhost' \
AIHR_SILVER_ADMIN_TOKEN='<管理端登录令牌>' \
AIHR_SILVER_EMPLOYEE_TOKEN='<员工登录令牌>' \
AIHR_SILVER_SUPERVISOR_TOKEN='<主管登录令牌>' \
AIHR_MEITU_APP_TOKEN='<美途应用令牌>' \
AIHR_MEITU_QUERY='<已确认能命中公开内容的美途标准题>' \
node scripts/verify-knowledge-platform.mjs

在预先导入了同一测试文件到 yc_public_policy 与 yc_property_sop 的非生产环境,可额外执行破坏性解绑验证;它会删除公共制度空间中的测试成员,因此必须使用专用测试文件:

AIHR_VERIFY_UNBIND=true \
AIHR_SHARED_DOC_NAME='<专用测试文件名>' \
AIHR_SHARED_DOC_QUERY='<测试文件唯一标记>' \
node scripts/verify-knowledge-platform.mjs

只有工程回归、真实 HTTP 正反例、数据库租户归属、Qdrant tenant+knowledge 过滤和每空间标准题都通过,才可标记“联调可用”。当前生产包已发布,但在真实内容负责人/授权矩阵签字、正式空间初始化、令牌联调和标准题完成前,不得标记“知识平台业务上线”或“正式试点验收”。

8. 回滚

按影响最小顺序处理:

  1. 停用 mt_card_miniapp 或对应 SESSION 应用。
  2. 解绑错误空间或把有问题的新空间置为 DISABLED。
  3. 客户端临时回到兼容 /api/knowledge/search,该入口仍经过统一授权服务。
  4. 恢复旧应用绑定或重新启用 legacy 空间。

不要删除新表,不删除查询审计,不把附件移动回旧空间,不清理 OSS,不回滚已经写入的稳定空间编码。数据库和静态资源发布仍遵循项目既有备份与发布口径。