docs(readme): 重写平台定位与能力边界并归档原说明
build / build (push) Successful in 49s

This commit is contained in:
姚章浩
2026-10-13 17:53:23 +08:00
parent 6de5d52363
commit 3a1d32da8f
2 changed files with 370 additions and 207 deletions
+115 -207
View File
@@ -1,251 +1,159 @@
# 知识同步治理平台
# 知识治理平台
独立的知识治理控制平面。当前包含P1持久任务队列、P2统一来源与PP历史接管、P3本地/测试WeKnora编排验收、P4来源只读扫描、P5知识工程师审核与封批门禁、默认关闭的P6三通道生产发布与回滚Worker,以及P7持久快照ChangeSet、真实运行监控和每50项一批的发布进度。
面向企业多数据源的知识同步、生命周期治理与持续运营平台。
## 安全边界
企业知识分散在钉钉、文件目录与业务资料门户中,内容会不断新增、更新和失效。平台连接这些来源与 WeKnora 知识底座,组织资料发现、取件、处理、验收、发布和下架,让知识不只是被导入一次,而是能够持续维护、追踪和治理。
- 本工程不修改 WeKnora、PP、AList、钉钉或 DobotAI 源码。
- PP接管与核验只读取指定的本地账本、manifest和测试WeKnora;不请求PP写接口,不修改来源文件。
- 普通Worker领取演示任务、`test.upload/test.poll/test.validate`和显式启用的`source.scan.pp`;只有普通Worker可挂载测试WeKnora Key和只读PP快照。
- Release Worker默认`RELEASE_WORKER_ENABLED=false`,禁用时不读取release config/生产secret,不连接生产数据面或治理数据库。
- P5封批只生成治理侧`sealed`记录,不创建生产发布Job;5条真实候选仍为待人工审核。
- P6生产请求另受`PRODUCTION_RELEASE_REQUESTS_ENABLED=false`保护;开启时还必须有服务端固定actor和0600 Bearer token文件。当前未授权、未配置、未发布生产。
- 凭据文件位于`secrets/`且被Git忽略;仓库只保留示例文件。
**同步是入口,知识库治理是核心,持续运营是目标。**
## 目录
## 平台解决什么问题
- **资料分散**:统一查看来源与目录,按范围选择、标注和获取内容。
- **更新难追踪**:通过来源身份、内容摘要、版本、快照与变更集记录变化。
- **处理不透明**:查看任务和解析状态,定位失败、停滞和需要人工处理的项目。
- **知识边界不清**:区分对内、对外和禁止导入,绑定相应测试与生产知识库。
- **上线不可控**:先处理与验收,再按审核策略和生产门禁组织发布。
- **失效难治理**:识别需下架内容,提供移除、失败重排与发布回滚等处理机制。
自动同步不等于无条件自动发布。不同来源的扫描、取件、处理和调度能力存在差异;生产变更仍受配置、权限与授权约束。
## 在 DobotAI 体系中的位置
```text
cmd/api HTTP API
cmd/worker 普通任务Worker
cmd/release-worker 默认关闭的发布Worker
cmd/pp-ledger-import PP历史账本一次性导入工具
internal/jobs 任务模型、租约和Worker循环
internal/governance 统一来源治理领域模型
internal/platform PostgreSQL仓库与Migration
internal/releasebundle v0.7.1发布bundle契约与摘要
internal/releaseadapter v0.7.1 PostgreSQL/ES/S3 Sidecar
internal/httpapi HTTP路由
web React TypeScript状态页
deploy Nginx配置
docs 验收计划
业务资料来源:钉钉 / AList / PP(资料门户)
↓
知识治理平台:发现与同步 → 状态跟踪 → 验收与治理 → 发布与维护
↓
WeKnora:文档处理、知识组织、索引与检索
↓
Knowledge Adapter / DobotAI 网关
↓
钉钉问答 / 软件内 AI 助理 / Wiki 问答等业务入口
```
## P2.1来源数据底座
本仓库负责知识治理流程与控制,不重复建设问答应用或检索引擎,也不修改 WeKnora 官方源码。Knowledge Adapter、DobotAI 网关及业务入口属于关联系统,不由本项目部署。
Migration 2创建五张空表:
## 核心能力与当前边界
```text
source_connections 来源连接配置,只保存secret引用名
source_items 来源内稳定对象身份与变化状态
content_objects 按完整SHA-256唯一的内容对象
source_item_versions 来源对象版本与内容对象关联
test_publications 测试知识库导入、处理、检索状态
```
下表描述仓库已有实现及文档记录的能力,**不代表任一部署环境已启用所有功能**。现场接线、运行开关与验收结果以对应环境为准。
`source_change`、`ingest_state`、`processing_state`和`retrieval_state`相互独立。只有同一来源连接内的`stable_external_id`唯一;只有完整小写SHA-256相同才能共享内容对象。
| 能力 | 已有支持 | 边界 |
| --- | --- | --- |
| 来源与目录管理 | 来源连接、目录浏览、范围选择、对内/对外/禁止导入策略 | 标注是治理意图,不代表已取件、入库或发布 |
| 多源资料获取 | 钉钉取件、AList 来源接入、PP 清单与历史账本接管 | 格式、凭据、目录及调度限制不同,不承诺所有来源全自动同步 |
| 变化与资产管理 | 稳定来源身份、内容摘要去重、来源版本、快照与变更集 | 来源变化与目标知识库处理状态分别记录 |
| 测试处理与验收 | 后台上传、轮询、知识与切片验收、失败恢复 | 解析和索引由对接的知识底座完成;部分检索判断仍需人工 |
| 解析与任务监控 | 处理中、失败/死信、需下架、知识库差异及处理进度视图 | 指标、动作与实际覆盖程度见验收清单,不等于完整告警平台 |
| 审核与发布 | 可配置自动审核策略、人工审核、批次锁定、生产授权与结果追踪 | 对内自动审核默认策略与对外人工控制详见操作流程;锁定不等于发布 |
| 回滚与备份 | 发布回滚、目标库快照门禁、备份配置及恢复工具 | 依赖专用接线、权限和恢复验证,不能仅凭页面存在判定可用 |
| 权限与审计 | 管理员、文档工程师、只读角色与操作记录 | 不等于已实现所有来源到问答端的文档级授权 |
P2.1不会自动创建来源连接、读取PP账本或上传文档。完整验收步骤见`docs/P2_SOURCE_FOUNDATION_ACCEPTANCE.md`。
### 数据源支持
## P2.2 PP历史账本接管
| 来源 | 当前接入方式 | 参考文档 |
| --- | --- | --- |
| 钉钉 | 知识库目录发现与取件;优先 dws,按错误类型决定是否回落兼容路径 | [钉钉取件](docs/DINGTALK_DWS_FETCHER.md) |
| AList | 来源目录与资料获取,依赖指定连接和凭据 | [用户操作流程](docs/OPERATIONS_RUNBOOK.md) |
| PP(资料门户) | 本地清单、历史账本接管与只读扫描 | [历史接管](docs/P2_PP_LEDGER_TAKEOVER_ACCEPTANCE.md)、[来源扫描](docs/P42_SOURCE_SCAN_ACCEPTANCE.md) |
一次性工具将PP append-only账本折叠为当前终态:
IT、行政等是待扩展的业务资料范围,不是已经完成的新连接器。接入敏感资料前,需要先确认 Owner、有效版本、可见范围与知识库路由。
```text
1093 条历史事件
→ 559 个来源对象和版本
→ 530 个SHA内容对象
→ 530 条历史测试发布
→ 29 个content_alias共享canonical内容和Knowledge ID
```
## 工作流程
75条旧`completed`记录缺少SHA时,只允许从locator hash匹配的本地manifest恢复,并重新校验文件大小和实际SHA-256。单文件继续遵守旧连接器50MiB上限。连接器默认关闭,历史发布的`retrieval_state`保持`unpublished`,等待未来对目标WeKnora逐条只读核验。
1. **配置来源与知识库绑定**:确认凭据、可见范围和目标库。
2. **发现、标注与取件**:按目录或文件选择范围,记录资料身份和变化。
3. **处理与验收**:进入测试知识库,跟踪上传、解析、索引及验收结果。
4. **审核与组批**:应用审核策略;建立并锁定发布清单,保留可追踪快照。
5. **授权发布**:核验备份与发布门禁,再执行目标知识库变更并读回验证。
6. **持续维护**:跟踪后续更新、失败、失效与需下架内容,按流程重排、移除或回滚。
工具只执行幂等INSERT,不执行UPDATE或DELETE。完整验收步骤见`docs/P2_PP_LEDGER_TAKEOVER_ACCEPTANCE.md`。
具体操作、确认语句和异常处理见 [操作 Runbook](docs/OPERATIONS_RUNBOOK.md);已实测、待验收和已知限制见 [用户流程验收清单](docs/USER_FLOWS_ACCEPTANCE.md)。
## P2.3 PP完整清单与知识资产页
## 访问与部署
P2.3将manifest中未进入旧账本的54个超限来源纳入治理视图:
### 已部署环境
```text
559 个历史终态 + 54 个over_limit阻断项 = 613 个来源对象
530 个内容对象
613 个来源版本
530 条历史测试发布
历史已同步内容计划重新上传数 = 0
```
Compose 中网页入口固定映射为 **宿主机 `8088` → Web 容器 `8080`**。网页和 `/api/` 使用同源入口。
超限版本没有虚假ContentObject或TestPublication,保持`content_state=over_limit`和阻断状态。`GET /api/v1/source-items`提供只读分页清单,支持`state=completed|content_alias|over_limit`、`limit`和`offset`;响应不包含来源URL、URI哈希、raw记录或Knowledge ID。Web“知识资产”模块读取该真实API。完整验收见`docs/P2_PP_INVENTORY_ACCEPTANCE.md`。
- 本机访问:`http://127.0.0.1:8088/`
- 局域网访问:`http://<服务器内网IP>:8088/`
- 健康入口:`http://127.0.0.1:8088/health`
## P3 WeKnora测试编排
可以由 NAS 反向代理提供 HTTPS 域名入口,目标为平台服务器的 `8088`,保持路径原样转发。公网开放前必须验证鉴权、访问控制及转发头配置,不直接暴露数据库或内部管理接口。
普通Worker从只读Spool流式上传到测试WeKnora,持久保存远端身份,按单调`poll_sequence`轮询,并在完成后执行Knowledge/Chunk验收。API和Web只返回安全投影;人工重试只允许失败或停滞且仍有已验证制品的Publication。530条PP历史Knowledge已在测试服务器完成在线只读核验,没有重新上传。
**平台入口展示当前部署版本,不是每次 CI 的构建预览。** CI 静态预览由独立链路提供;CI 通过不会自动更新运行服务。
本地、测试服务器、失败恢复、Worker重启和浏览器验收见`docs/P3_LOCAL_AND_CROSS_ENV_ACCEPTANCE.md`。
### 部署前提
## P4.1 来源连接只读工作台
当前 [compose.yaml](compose.yaml) 面向已有接线环境,包含宿主目录、凭据挂载和外部 Docker 网络,**不是仅克隆仓库就能直接启动的通用安装包**。
`GET /api/v1/source-connections`返回来源ID、类型、名称、启用状态、是否配置调度、健康枚举、对象数和更新时间。查询与响应均不包含`base_url`、`secret_ref`或原始健康错误正文。Web“来源管理”模块展示真实PostgreSQL数据,保留左侧导航和顶部多标签。
部署者需先准备:
P4.1完整验收见`docs/P41_SOURCE_CONNECTIONS_ACCEPTANCE.md`。
- Docker Compose,以及 `compose.yaml` 声明的外部网络。
- PostgreSQL、测试知识库及所启用来源的凭据文件与权限。
- 来源目录、spool 与运行状态挂载;按部署主机调整路径和 UID/GID。
- 测试 WeKnora 连接与对内/对外路由;生产发布按独立配置和授权接线。
## P4.2 PP只读扫描与变化入账
普通Worker在`PP_SCAN_ENABLED=true`且三个安全路径完整时注册`source.scan.pp` Handler。Compose仅给普通Worker只读挂载PP数据目录;API、Web和Release Worker均不挂载。Scanner拒绝越界、软链接、非普通文件和不完整manifest;54个历史`over_limit`项允许其制品未下载,但仍保持阻断。
Migration 5新增`source_scans`和`source_scan_changes`。触发Scan与创建Job原子执行,每个连接最多一个活动Scan;完成时聚合、变化与来源健康原子写入;失败只保存固定错误码。安全API支持触发、扫描历史和连接作用域详情,Web展示真实聚合与变化记录,不返回来源ID/revision/SHA/路径/URL或凭据。
真实PP快照只读扫描结果:
```text
observed=613
unchanged=559
over_limit=54
added=0
updated=0
missing=0
planned_uploads=0
```
过期`running`租约已由常驻Worker真实重领并以`attempt=2`完成;并发第二次触发返回`409`。完整迁移、备份、恢复、API和Firefox验收见`docs/P42_SOURCE_SCAN_ACCEPTANCE.md`。
## P5 知识工程师审核与封批
Migration 6新增追加式`publication_reviews`、`release_batches`和`release_batch_items`。审核决定绑定最新Acceptance run与Publication时间戳;批准仅允许完整结构门禁通过的候选,`retrieval_hit=manual_required`保持人工判断。创建draft和sealed封批均原子验证整个项目集合,任一项目失效则整批拒绝。
“发布管理”模块展示真实审核候选、固定门禁、决定可见性、草稿批次和封批记录,并明确显示“生产门禁保持关闭”“Release Worker保持关闭”“尚未发布”。当前535条Publication中,530条无逐条Acceptance的PP legacy不自动进入人工队列;5条manual合成验收项显示为ready/pending,持久库没有代替知识工程师创建Review或Batch。
P5 Store与HTTP写链路已在disposable PostgreSQL和临时API中验证`pending→approved→draft→sealed`,完成后临时数据库与临时密钥文件均清理;持久环境只应用空Migration 6。完整验收见`docs/P5_REVIEW_AND_SEAL_ACCEPTANCE.md`。
## P6 生产发布Worker与回滚
Migration 7新增`release_executions`与`release_item_executions`。只有受保护的生产授权请求才能把P5 sealed batch原子转换为`release.publish` Job;rollback只允许针对完整成功的publish execution。终态失败可在同一Bearer门禁下重排,仅重置failed items并重新验证sealed snapshot。API和Release Worker使用两个独立默认关闭的门禁,UI只展示执行审计,不提供一键生产授权按钮。
Release Worker按Knowledge Base精确路由,固定WeKnora `v0.7.1 / migration 74 / elasticsearch_v8 / 1024维`契约;同一release package协调复制以下内容且不触发生产解析/Embedding:
```text
PostgreSQL: knowledges/chunks/knowledge_tags/relations/resources/bindings
对象存储: referenced assets,staging→SHA校验→相同relative key
Elasticsearch: chunk text+vectors,先upsert新文档再清理同Knowledge旧_id
```
每个release revision写入0700目录中的0600 bundle并重验整包SHA。失败恢复旧bundle;首次发布无旧revision时禁用Knowledge/Chunk/ES,不删除行、文档或对象。生产WeKnora官方API最终验证Knowledge、Chunk数和knowledge-scoped检索。
本地真实验收使用完整v0.7.1 75个migration的source/target PostgreSQL、真实MinIO、状态化ES协议服务,已通过`publish v1 → publish v2 → rollback v1`以及PG disable、S3 Open/Put/Copy、Worker受限配置装配。模板见`config/release.v071.example.json`,完整边界与结果见`docs/P6_RELEASE_WORKER_ACCEPTANCE.md`。
当前生产状态保持:
```text
PRODUCTION_RELEASE_REQUESTS_ENABLED=false
RELEASE_WORKER_ENABLED=false
release executions/items/jobs=0/0/0
```
## 服务配置在哪配(四项)
「系统设置 → 服务配置」只保留四项,其他都不在这页:
| 项 | 含义 | 校验 | 谁来写 |
| --- | --- | --- | --- |
| 测试地址 | 控制台探活的测试 WeKnora 地址 | 裸 http(s) 源地址 | 仅管理员 |
| 生产地址 | 控制台探活的生产 WeKnora 地址 | 裸 http(s) 源地址 | 仅管理员 |
| 生产 ID | 发布契约里的租户 ID | 十进制整数(空 = 未配置);保存时真查它是否就是该知识库的租户 | 仅管理员 |
| 知识库 ID | 生产知识库标识 | 不含空格、`/`、`?`、`#`、`\`(空 = 未配置);保存时必须在该地址上真实存在 | 仅管理员 |
一次点「保存并启用服务配置」四项一起提交:服务端先格式校验,再写版本化的活动设置(候选 → 激活 →
旧版本保留审计),网页只录裸地址、绝不夹带凭据。四项里任何一项相对活动版本发生变化都要管理员角色,
普通工程师提交会被 403(只改刷新参数不受影响)。留空表示未配置,界面如实显示「未配置」。
- 地址探活:后端按地址真打 `/health`,显示「端点可达 / 不可达 · 未做接单验证」;测试地址留空时回落到
容器 env `TEST_WEKNORA_BASE_URL`,生产地址回落到 `PRODUCTION_WEKNORA_BASE_URL`(可选
`PRODUCTION_WEKNORA_API_KEY_FILE`、`PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP=false`)。探活只打
`/health`,从不读写知识库、从不参与发布,所以生产端点抖动不会阻断测试流水线。
- **两个 ID 的真校验(保存时执行)**:保存/激活服务配置时,后端拿「生产地址 + 生产 API Key 文件」
调 WeKnora 列库接口(`GET /api/v1/knowledge-bases/{id}`,回落到 `GET /api/v1/knowledge-bases`
列租户)真查一次:
- 查到 → 通过,界面显示知识库名与观测到的租户(`verification.knowledge_base_name` /
`observed_tenant_id`,服务端只读,从不写回配置);
- 端点明确回答「没有这个知识库」或租户与该库不符 → **拒绝保存**(HTTP 400 + 原话),一行都不写;
- 端点抖动、超时、`401/403`、未配置生产凭据 → **只警告不拦**(响应里带 `verification.status`
= `unreachable` / `unauthorized` / `cannot_verify`),保存照常落地——运维不能因为端点故障就改不了地址。
真校验需要 api 容器能读到生产凭据:`PRODUCTION_WEKNORA_API_KEY_FILE` 指向 0600 文件(值永不进网页、
永不进响应);留空时该项显示「未核验:没有可用的生产 API Key 文件」,其余行为不变。
激活前会再查一次(候选可以躺几天,库可能已被删);字段与线上一致的回放不重复核验,幂等重试不受端点影响。
覆盖率要点:租户校验只在「该库的 tenant_id」这一条路径上成立;只填生产 ID 时按列库结果的租户去比对。
- 排列组合之外的东西(依赖探活矩阵、对内/对外知识库绑定下拉)已从这页摘掉;依赖状态在总览页,
绑定数据与 API 仍在(`internal/httpapi/routing_bindings.go`,迁移 000034)。
发布目标与凭据仍然是另一件事,刻意不进网页:
1. 发布通道的目标(唯一真源,Release Worker 读它):`RELEASE_CONFIG_FILE` 指向的 0600 JSON 里
`routes[].target.weknora.base_url`(同级 `api_key_file`),模板见 `config/release.v071.example.json`。
硬约束:绝对路径、0600 普通文件、严格 JSON(多字段即报错)、source/target 不能是同一库或同一桶。
凭据与发布目标刻意不进网页、不回读——它只在管理员受限的运行时配置里。
所以:网页上配的地址管「控制台看到的真实状态」,真正发单时的目标仍由发布运行时配置决定。改了一处却
忘了另一处时,控制台会显示旧地址的真实状态——这是提示,不是故障。
## 本地验证
要求Go 1.26、Node.js 22和npm。
准备完成后,由获授权的操作者执行:
```bash
make test
make vet
make build
make web-test
make web-typecheck
make web-build
```
## Docker Compose(由操作者执行)
先创建仅限本机的密钥文件:
```bash
cp secrets/postgres_password.example secrets/postgres_password
cp secrets/pgpass.example secrets/pgpass
# 将两个文件中的CHANGE_ME_LOCAL_ONLY替换为同一随机本地密码
chmod 600 secrets/postgres_password secrets/pgpass
docker compose up --build -d
```
Compose默认让三个Go进程以`1000:1000`运行,以读取宿主机Owner为1000且权限为600的密钥文件。若操作者UID/GID不同,启动前设置`APP_UID`和`APP_GID`为密钥文件Owner。
主机需要提权时,由操作者按本机权限规则执行,不在自动化中默认使用 sudo。停止服务可用 `docker compose down`;**不要执行 `docker compose down -v`,除非明确批准删除数据库卷。**
访问:
## 配置与安全边界
- 状态页:`http://127.0.0.1:8088/`
- 同源健康检查:`http://127.0.0.1:8088/health`
- 网页服务地址用于控制台探活与配置核验;真正的发布目标由受限发布运行时配置决定,两者不能混为一谈。
- 密钥通过文件或 secret 引用提供,不写入 Git、网页响应或日志。示例配置不是真实凭据。
- 生产请求与 Release Worker 默认关闭;启用状态必须现场确认。测试处理成功、自动审核或批次锁定都不能绕过生产门禁。
- 来源扫描与取件不应改写原始业务资料;目标知识库的发布、移除和回滚属于独立受控动作。
- 发布与恢复前必须按目标环境准备备份,并核验可恢复性。
停止时保留数据库:
参考:[服务与操作配置](docs/OPERATIONS_RUNBOOK.md)、[生产备份与恢复](docs/PRODUCTION_BACKUP_RECOVERY.md)、[来源范围规则](docs/SOURCE_SCOPE_POLICY.md)。
## 开发与 CI
基础环境:Go 1.26、Node.js 22、npm,以及检查脚本所需的 Python 3 和工具链。依赖以仓库锁文件和实际 CI 配置为准。
```bash
docker compose down
make verify # 完整检查入口:本地与 CI 使用同一套规则
make list # 查看检查清单与适用范围
```
不要执行`docker compose down -v`,除非明确决定删除本地数据库数据。
单项测试或构建只用于定位问题,不能代替完整验证。远端 CI 校验事件提交身份、汇总必需检查,再按条件生成静态预览与通知;不默认执行部署。
## P1 API
版本与变更见 [CHANGELOG](CHANGELOG.md),正式版本流程见 [发布 Runbook](docs/RELEASE_RUNBOOK.md)。`make version` 查看构建身份;日常开发构建与正式构建分开标识,软件版本 tag 不等同于知识发布批次。
创建普通演示任务:
## 后续方向
```bash
curl -fsS -X POST http://127.0.0.1:8088/api/v1/jobs \
-H 'Content-Type: application/json' \
-d '{"type":"demo","payload":{},"idempotency_key":"demo:001"}'
- 扩展多数据源调度与增量同步的覆盖范围。
- 完善处理监控、积压分析、告警及来源与目标知识库的一致性对账。
- 建设知识资产、新鲜度、更新超期与知识缺口报表。
- 对接评测和业务反馈,形成可归因的持续优化闭环。
- 细化敏感资料权限及生命周期规则。
这些是演进方向,不作为当前已上线能力承诺。待办见 [BACKLOG](docs/BACKLOG.md)。
## 文档导航
| 文档 | 用途 |
| --- | --- |
| [操作 Runbook](docs/OPERATIONS_RUNBOOK.md) | 使用步骤、配置、凭据续期与异常处理 |
| [用户流程验收](docs/USER_FLOWS_ACCEPTANCE.md) | 实测证据、待验收项及限制 |
| [领域模型](docs/DOMAIN_MODEL.md) / [状态机](docs/STATE_MACHINE.md) | 模块边界、数据与状态语义 |
| [发布与版本](docs/RELEASE_RUNBOOK.md) | 软件发布准备与只读预检 |
| [备份与恢复](docs/PRODUCTION_BACKUP_RECOVERY.md) | 知识库数据的备份、恢复与演练 |
| [旧 README 存档](docs/archive/README_BEFORE_PRODUCT_REWRITE.md) | 原阶段说明与历史验收记录;不是实时部署状态 |
## 仓库结构
```text
cmd/ API、任务 Worker、发布 Worker 与运维工具
internal/ 治理模型、任务队列、持久化、HTTP 与发布适配
web/ React / TypeScript 管理界面
deploy/ Nginx 配置
config/ 发布等受限配置的示例
scripts/ 检查、资料处理与运维脚本
docs/ 操作、设计、验收与历史档案
```
创建可用于租约恢复验收的30秒任务:
```bash
curl -fsS -X POST http://127.0.0.1:8088/api/v1/jobs \
-H 'Content-Type: application/json' \
-d '{"type":"demo.sleep","payload":{"sleep_ms":30000},"idempotency_key":"demo:sleep:001"}'
```
查看最近任务:
```bash
curl -fsS 'http://127.0.0.1:8088/api/v1/jobs?limit=50'
```
完整人工步骤见`docs/P1_ACCEPTANCE_TEST_PLAN.md`。
@@ -0,0 +1,255 @@
# 原 README 存档
> 来源:提交 `950c1b429bc814d9fa7c2c1449f999a05537af21` 的 README。保留历史阶段记录;其中数量、开关和部署结论不是当前环境的实时状态。原文相对链接以仓库根目录为基准。
# 知识同步治理平台
独立的知识治理控制平面。当前包含P1持久任务队列、P2统一来源与PP历史接管、P3本地/测试WeKnora编排验收、P4来源只读扫描、P5知识工程师审核与封批门禁、默认关闭的P6三通道生产发布与回滚Worker,以及P7持久快照ChangeSet、真实运行监控和每50项一批的发布进度。
## 安全边界
- 本工程不修改 WeKnora、PP、AList、钉钉或 DobotAI 源码。
- PP接管与核验只读取指定的本地账本、manifest和测试WeKnora;不请求PP写接口,不修改来源文件。
- 普通Worker领取演示任务、`test.upload/test.poll/test.validate`和显式启用的`source.scan.pp`;只有普通Worker可挂载测试WeKnora Key和只读PP快照。
- Release Worker默认`RELEASE_WORKER_ENABLED=false`,禁用时不读取release config/生产secret,不连接生产数据面或治理数据库。
- P5封批只生成治理侧`sealed`记录,不创建生产发布Job;5条真实候选仍为待人工审核。
- P6生产请求另受`PRODUCTION_RELEASE_REQUESTS_ENABLED=false`保护;开启时还必须有服务端固定actor和0600 Bearer token文件。当前未授权、未配置、未发布生产。
- 凭据文件位于`secrets/`且被Git忽略;仓库只保留示例文件。
## 目录
```text
cmd/api HTTP API
cmd/worker 普通任务Worker
cmd/release-worker 默认关闭的发布Worker
cmd/pp-ledger-import PP历史账本一次性导入工具
internal/jobs 任务模型、租约和Worker循环
internal/governance 统一来源治理领域模型
internal/platform PostgreSQL仓库与Migration
internal/releasebundle v0.7.1发布bundle契约与摘要
internal/releaseadapter v0.7.1 PostgreSQL/ES/S3 Sidecar
internal/httpapi HTTP路由
web React TypeScript状态页
deploy Nginx配置
docs 验收计划
```
## P2.1来源数据底座
Migration 2创建五张空表:
```text
source_connections 来源连接配置,只保存secret引用名
source_items 来源内稳定对象身份与变化状态
content_objects 按完整SHA-256唯一的内容对象
source_item_versions 来源对象版本与内容对象关联
test_publications 测试知识库导入、处理、检索状态
```
`source_change`、`ingest_state`、`processing_state`和`retrieval_state`相互独立。只有同一来源连接内的`stable_external_id`唯一;只有完整小写SHA-256相同才能共享内容对象。
P2.1不会自动创建来源连接、读取PP账本或上传文档。完整验收步骤见`docs/P2_SOURCE_FOUNDATION_ACCEPTANCE.md`。
## P2.2 PP历史账本接管
一次性工具将PP append-only账本折叠为当前终态:
```text
1093 条历史事件
→ 559 个来源对象和版本
→ 530 个SHA内容对象
→ 530 条历史测试发布
→ 29 个content_alias共享canonical内容和Knowledge ID
```
75条旧`completed`记录缺少SHA时,只允许从locator hash匹配的本地manifest恢复,并重新校验文件大小和实际SHA-256。单文件继续遵守旧连接器50MiB上限。连接器默认关闭,历史发布的`retrieval_state`保持`unpublished`,等待未来对目标WeKnora逐条只读核验。
工具只执行幂等INSERT,不执行UPDATE或DELETE。完整验收步骤见`docs/P2_PP_LEDGER_TAKEOVER_ACCEPTANCE.md`。
## P2.3 PP完整清单与知识资产页
P2.3将manifest中未进入旧账本的54个超限来源纳入治理视图:
```text
559 个历史终态 + 54 个over_limit阻断项 = 613 个来源对象
530 个内容对象
613 个来源版本
530 条历史测试发布
历史已同步内容计划重新上传数 = 0
```
超限版本没有虚假ContentObject或TestPublication,保持`content_state=over_limit`和阻断状态。`GET /api/v1/source-items`提供只读分页清单,支持`state=completed|content_alias|over_limit`、`limit`和`offset`;响应不包含来源URL、URI哈希、raw记录或Knowledge ID。Web“知识资产”模块读取该真实API。完整验收见`docs/P2_PP_INVENTORY_ACCEPTANCE.md`。
## P3 WeKnora测试编排
普通Worker从只读Spool流式上传到测试WeKnora,持久保存远端身份,按单调`poll_sequence`轮询,并在完成后执行Knowledge/Chunk验收。API和Web只返回安全投影;人工重试只允许失败或停滞且仍有已验证制品的Publication。530条PP历史Knowledge已在测试服务器完成在线只读核验,没有重新上传。
本地、测试服务器、失败恢复、Worker重启和浏览器验收见`docs/P3_LOCAL_AND_CROSS_ENV_ACCEPTANCE.md`。
## P4.1 来源连接只读工作台
`GET /api/v1/source-connections`返回来源ID、类型、名称、启用状态、是否配置调度、健康枚举、对象数和更新时间。查询与响应均不包含`base_url`、`secret_ref`或原始健康错误正文。Web“来源管理”模块展示真实PostgreSQL数据,保留左侧导航和顶部多标签。
P4.1完整验收见`docs/P41_SOURCE_CONNECTIONS_ACCEPTANCE.md`。
## P4.2 PP只读扫描与变化入账
普通Worker在`PP_SCAN_ENABLED=true`且三个安全路径完整时注册`source.scan.pp` Handler。Compose仅给普通Worker只读挂载PP数据目录;API、Web和Release Worker均不挂载。Scanner拒绝越界、软链接、非普通文件和不完整manifest;54个历史`over_limit`项允许其制品未下载,但仍保持阻断。
Migration 5新增`source_scans`和`source_scan_changes`。触发Scan与创建Job原子执行,每个连接最多一个活动Scan;完成时聚合、变化与来源健康原子写入;失败只保存固定错误码。安全API支持触发、扫描历史和连接作用域详情,Web展示真实聚合与变化记录,不返回来源ID/revision/SHA/路径/URL或凭据。
真实PP快照只读扫描结果:
```text
observed=613
unchanged=559
over_limit=54
added=0
updated=0
missing=0
planned_uploads=0
```
过期`running`租约已由常驻Worker真实重领并以`attempt=2`完成;并发第二次触发返回`409`。完整迁移、备份、恢复、API和Firefox验收见`docs/P42_SOURCE_SCAN_ACCEPTANCE.md`。
## P5 知识工程师审核与封批
Migration 6新增追加式`publication_reviews`、`release_batches`和`release_batch_items`。审核决定绑定最新Acceptance run与Publication时间戳;批准仅允许完整结构门禁通过的候选,`retrieval_hit=manual_required`保持人工判断。创建draft和sealed封批均原子验证整个项目集合,任一项目失效则整批拒绝。
“发布管理”模块展示真实审核候选、固定门禁、决定可见性、草稿批次和封批记录,并明确显示“生产门禁保持关闭”“Release Worker保持关闭”“尚未发布”。当前535条Publication中,530条无逐条Acceptance的PP legacy不自动进入人工队列;5条manual合成验收项显示为ready/pending,持久库没有代替知识工程师创建Review或Batch。
P5 Store与HTTP写链路已在disposable PostgreSQL和临时API中验证`pending→approved→draft→sealed`,完成后临时数据库与临时密钥文件均清理;持久环境只应用空Migration 6。完整验收见`docs/P5_REVIEW_AND_SEAL_ACCEPTANCE.md`。
## P6 生产发布Worker与回滚
Migration 7新增`release_executions`与`release_item_executions`。只有受保护的生产授权请求才能把P5 sealed batch原子转换为`release.publish` Job;rollback只允许针对完整成功的publish execution。终态失败可在同一Bearer门禁下重排,仅重置failed items并重新验证sealed snapshot。API和Release Worker使用两个独立默认关闭的门禁,UI只展示执行审计,不提供一键生产授权按钮。
Release Worker按Knowledge Base精确路由,固定WeKnora `v0.7.1 / migration 74 / elasticsearch_v8 / 1024维`契约;同一release package协调复制以下内容且不触发生产解析/Embedding:
```text
PostgreSQL: knowledges/chunks/knowledge_tags/relations/resources/bindings
对象存储: referenced assets,staging→SHA校验→相同relative key
Elasticsearch: chunk text+vectors,先upsert新文档再清理同Knowledge旧_id
```
每个release revision写入0700目录中的0600 bundle并重验整包SHA。失败恢复旧bundle;首次发布无旧revision时禁用Knowledge/Chunk/ES,不删除行、文档或对象。生产WeKnora官方API最终验证Knowledge、Chunk数和knowledge-scoped检索。
本地真实验收使用完整v0.7.1 75个migration的source/target PostgreSQL、真实MinIO、状态化ES协议服务,已通过`publish v1 → publish v2 → rollback v1`以及PG disable、S3 Open/Put/Copy、Worker受限配置装配。模板见`config/release.v071.example.json`,完整边界与结果见`docs/P6_RELEASE_WORKER_ACCEPTANCE.md`。
当前生产状态保持:
```text
PRODUCTION_RELEASE_REQUESTS_ENABLED=false
RELEASE_WORKER_ENABLED=false
release executions/items/jobs=0/0/0
```
## 服务配置在哪配(四项)
「系统设置 → 服务配置」只保留四项,其他都不在这页:
| 项 | 含义 | 校验 | 谁来写 |
| --- | --- | --- | --- |
| 测试地址 | 控制台探活的测试 WeKnora 地址 | 裸 http(s) 源地址 | 仅管理员 |
| 生产地址 | 控制台探活的生产 WeKnora 地址 | 裸 http(s) 源地址 | 仅管理员 |
| 生产 ID | 发布契约里的租户 ID | 十进制整数(空 = 未配置);保存时真查它是否就是该知识库的租户 | 仅管理员 |
| 知识库 ID | 生产知识库标识 | 不含空格、`/`、`?`、`#`、`\`(空 = 未配置);保存时必须在该地址上真实存在 | 仅管理员 |
一次点「保存并启用服务配置」四项一起提交:服务端先格式校验,再写版本化的活动设置(候选 → 激活 →
旧版本保留审计),网页只录裸地址、绝不夹带凭据。四项里任何一项相对活动版本发生变化都要管理员角色,
普通工程师提交会被 403(只改刷新参数不受影响)。留空表示未配置,界面如实显示「未配置」。
- 地址探活:后端按地址真打 `/health`,显示「端点可达 / 不可达 · 未做接单验证」;测试地址留空时回落到
容器 env `TEST_WEKNORA_BASE_URL`,生产地址回落到 `PRODUCTION_WEKNORA_BASE_URL`(可选
`PRODUCTION_WEKNORA_API_KEY_FILE`、`PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP=false`)。探活只打
`/health`,从不读写知识库、从不参与发布,所以生产端点抖动不会阻断测试流水线。
- **两个 ID 的真校验(保存时执行)**:保存/激活服务配置时,后端拿「生产地址 + 生产 API Key 文件」
调 WeKnora 列库接口(`GET /api/v1/knowledge-bases/{id}`,回落到 `GET /api/v1/knowledge-bases`
列租户)真查一次:
- 查到 → 通过,界面显示知识库名与观测到的租户(`verification.knowledge_base_name` /
`observed_tenant_id`,服务端只读,从不写回配置);
- 端点明确回答「没有这个知识库」或租户与该库不符 → **拒绝保存**(HTTP 400 + 原话),一行都不写;
- 端点抖动、超时、`401/403`、未配置生产凭据 → **只警告不拦**(响应里带 `verification.status`
= `unreachable` / `unauthorized` / `cannot_verify`),保存照常落地——运维不能因为端点故障就改不了地址。
真校验需要 api 容器能读到生产凭据:`PRODUCTION_WEKNORA_API_KEY_FILE` 指向 0600 文件(值永不进网页、
永不进响应);留空时该项显示「未核验:没有可用的生产 API Key 文件」,其余行为不变。
激活前会再查一次(候选可以躺几天,库可能已被删);字段与线上一致的回放不重复核验,幂等重试不受端点影响。
覆盖率要点:租户校验只在「该库的 tenant_id」这一条路径上成立;只填生产 ID 时按列库结果的租户去比对。
- 排列组合之外的东西(依赖探活矩阵、对内/对外知识库绑定下拉)已从这页摘掉;依赖状态在总览页,
绑定数据与 API 仍在(`internal/httpapi/routing_bindings.go`,迁移 000034)。
发布目标与凭据仍然是另一件事,刻意不进网页:
1. 发布通道的目标(唯一真源,Release Worker 读它):`RELEASE_CONFIG_FILE` 指向的 0600 JSON 里
`routes[].target.weknora.base_url`(同级 `api_key_file`),模板见 `config/release.v071.example.json`。
硬约束:绝对路径、0600 普通文件、严格 JSON(多字段即报错)、source/target 不能是同一库或同一桶。
凭据与发布目标刻意不进网页、不回读——它只在管理员受限的运行时配置里。
所以:网页上配的地址管「控制台看到的真实状态」,真正发单时的目标仍由发布运行时配置决定。改了一处却
忘了另一处时,控制台会显示旧地址的真实状态——这是提示,不是故障。
## 本地验证
要求Go 1.26、Node.js 22和npm。
```bash
make test
make vet
make build
make web-test
make web-typecheck
make web-build
```
## Docker Compose(由操作者执行)
先创建仅限本机的密钥文件:
```bash
cp secrets/postgres_password.example secrets/postgres_password
cp secrets/pgpass.example secrets/pgpass
# 将两个文件中的CHANGE_ME_LOCAL_ONLY替换为同一随机本地密码
chmod 600 secrets/postgres_password secrets/pgpass
docker compose up --build -d
```
Compose默认让三个Go进程以`1000:1000`运行,以读取宿主机Owner为1000且权限为600的密钥文件。若操作者UID/GID不同,启动前设置`APP_UID`和`APP_GID`为密钥文件Owner。
访问:
- 状态页:`http://127.0.0.1:8088/`
- 同源健康检查:`http://127.0.0.1:8088/health`
停止时保留数据库:
```bash
docker compose down
```
不要执行`docker compose down -v`,除非明确决定删除本地数据库数据。
## P1 API
创建普通演示任务:
```bash
curl -fsS -X POST http://127.0.0.1:8088/api/v1/jobs \
-H 'Content-Type: application/json' \
-d '{"type":"demo","payload":{},"idempotency_key":"demo:001"}'
```
创建可用于租约恢复验收的30秒任务:
```bash
curl -fsS -X POST http://127.0.0.1:8088/api/v1/jobs \
-H 'Content-Type: application/json' \
-d '{"type":"demo.sleep","payload":{"sleep_ms":30000},"idempotency_key":"demo:sleep:001"}'
```
查看最近任务:
```bash
curl -fsS 'http://127.0.0.1:8088/api/v1/jobs?limit=50'
```
完整人工步骤见`docs/P1_ACCEPTANCE_TEST_PLAN.md`。