# 原 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`。