Files
kefu/deploy/protocol-integration-20260916/finish_docs.py
T
2026-09-21 10:34:06 +08:00

150 lines
12 KiB
Python

from pathlib import Path
import json,hashlib,shutil,ast
folder=Path(__file__).resolve().parent
target=Path('C:/wechat_rpa');payload=folder/'payload'
record=json.loads((folder/'applied.json').read_text(encoding='utf-8'))
backup=Path(record['backup'])
original={r['path']:r for r in record['manifest']}
def sha(p): return hashlib.sha256(p.read_bytes()).hexdigest()
def put(name,text):
path=target/name
if name not in original:
before=sha(path) if path.is_file() else None
if before:
saved=backup/name;saved.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(path,saved)
original[name]={'before_sha256':before}
for root in [target,payload]:
dest=root/name;dest.parent.mkdir(parents=True,exist_ok=True)
dest.write_text(text,encoding='utf-8',newline='\n')
put('INTEGRATION_20260916.md',r'''# 协议版与知识中心整合
2026-09-16 将 C:\kefu 的近期功能整合到 C:\wechat_rpa。保留协议版 v1.4.16 的客户端、视觉/协议模式、原账号发送、回执去重、联系人验证跳过及企业微信版本适配。
## 已接入
- 智能体角色、规则与全部授权账号视图:原有后端能力保留,补齐完整管理前端。
- 归档性能:分页与游标、租户过滤、账号统计按需读取、导出查询优化。
- 知识加工:范围预览、规则/指定模型整理、任务进度、暂停/继续/取消、失败换模型续跑与增量整理。
- 异常处理:模型字段异常保留脱敏问答为待审草稿,已驳回知识支持删除。
- 全量审核:指定账号或全部授权账号,跨全部页面后台处理;通过后进入待发布,异常保留原因。
- 检索:只引用有效且已发布的知识;账号隔离、来源与版本复核、引用留痕、可读错误提示。
- 协议回复:经过模型网关的回复接入知识检索,保留任务 ID、知识引用和裁判风险;高风险回复进入原有审核流程。
- 客户端「管理知识」打开后台知识中心,数量来自云端已发布知识。知识入口使用正确的 hash 路由。
## 使用方式
使用当前已配置的后台时,退出并重新启动本目录的源码客户端;在客户端同步后台配置后,进入「AI 人格与能力 → 管理知识」。选择账号,整理聊天、审核、发布,再开启该账号「客服使用知识」。协议模式通过该账号的模型网关使用知识,本机直连模型不经过服务端知识检索。
在管理端「知识与审核」可一键审核此账号或全部授权账号,不需要逐页全选。审核通过仅为待发布,发布后才会参与检索。
本目录也能独立运行完整后台:
```powershell
cd C:\wechat_rpa
python run_backend.py --db C:\wechat_rpa\backend.db
```
启动管理 API、模型网关、知识 Worker。管理页面位于 `http://127.0.0.1:8766/#/knowledge/center`,无需为前端另起开发服务。已保留当前配置和数据库;本次验收仅使用临时测试数据库,没有启动此命令处理真实任务。
前端源码与生产资源位于 `admin-web`。再次修改前端时先安装其 pnpm 工作区依赖,再执行 `pnpm --filter @vben/web-antd typecheck`、`pnpm --filter @vben/web-antd build`。已附带本次通过浏览器验收的 dist。
Linux 服务端部署入口为 [deploy/im-admin/README.md](deploy/im-admin/README.md)。知识功能说明见 [KNOWLEDGE.md](KNOWLEDGE.md)。
## 验证
- 知识、任务、模型输出、全量审核、检索和网关:96 项通过。
- 新增协议与知识联动、独立管理端及桌面状态:7 项通过。
- 管理后台、归档、性能、智能体和路由:326 项通过。
- 协议、联系人、版本适配、打包规则和 Qt:221 项中 217 项通过,4 项因未安装可选 Unicorn CPU 模拟器跳过。
- 客户端登录、归档上传、回复安全、防自回复:496 项通过。
- 合计 1142 项通过、4 项跳过。协议发送使用模拟传输,没有向真实客户发送测试消息。
- 独立管理页:正常检索仅命中已发布知识;空白、单字、超长问题与后端错误显示正确,无页面脚本异常。
- 跨账号页面验收:77 条模拟草稿中 75 条通过进入待发布、2 条保留异常原因,发布数仍为 0;关闭弹窗后任务继续。
## 交付与回退
本次交付是源码、前端生产资源和部署配置。现有 dist 中已打包的 EXE/安装包未重建,线上服务器未部署;已有运行中的进程需重启才加载新源码。
修改前文件备份:`backups/integration-20260916-151604`。改动清单与 SHA-256 见 `INTEGRATION_MANIFEST_20260916.json`,测试结果见 `INTEGRATION_VERIFICATION_20260916.json`。原数据库、密钥、聊天归档和个人配置没有被本次整合覆盖。
需要回退时先停止使用本目录源码的服务和客户端,再按清单恢复备份中的原文件。不要覆盖运行数据。知识功能使用后不要恢复旧数据库,否则会丢失之后新增的聊天与知识。
''')
put('deploy/im-admin/README.md',r'''# 从协议版目录部署完整管理后台
本部署目录包含管理 API、模型网关和知识 Worker,使用同一数据库卷。构建上下文为 C:\wechat_rpa(Linux 上为其对应源码根目录)。桌面协议发送仍运行在 Windows 客户端,服务端不启动企业微信。
## 首次部署
1. 将本源码目录及 `admin-web/apps/web-antd/dist` 放到服务器。
2. 将 `.env.example` 复制为本目录 `.env`,填写现有账号服务地址。语义检索为可选项,参照根目录 KNOWLEDGE.md;未配置时使用关键词检索。
3. 在本目录建立 `data`,允许容器 UID 10001 写入。迁移已有库时使用 SQLite backup API 或停止写入后复制数据库及配套密钥;不要复制正在写入的裸数据库文件。
4. 在源码根目录运行:
```bash
docker compose -f deploy/im-admin/compose.yaml up -d --build api gateway knowledge-worker
docker compose -f deploy/im-admin/compose.yaml ps
docker compose -f deploy/im-admin/compose.yaml logs --tail=100 knowledge-worker
```
管理 API 与页面监听宿主机 `127.0.0.1:18766`,模型网关监听 `127.0.0.1:18770`。对外访问使用反向代理。同目录 Nginx 配置是原部署示例,需按实际域名、日志路径与 TLS 配置调整,不能原样覆盖现有站点。
镜像名称 `zyt/wecom-admin:protocol-integrated`。`data` 不参与镜像构建,`.dockerignore` 仅允许服务端 Python、依赖清单和前端生产资源。镜像构建不会打包本机配置、聊天数据或密钥。
## 更新与回退
更新前保留当前镜像并备份数据库及密钥。启动时自动补齐知识相关表和索引;知识 Worker 负责分批加工和全量审核任务。关闭页面不会停止已启动的后台任务。
需要停用知识引用,可先在管理端关闭对应账号的「客服使用知识」。回退旧服务前停止知识 Worker,保留新表与运行数据。不要用旧库覆盖更新后产生的聊天归档与知识。
本配置已适配独立协议目录的文件布局;本次没有执行线上部署或 Docker 镜像构建。根目录 `backend_deploy` 和 `BACKEND_DEPLOY.md` 保留为历史资料,当前知识版本使用本目录。
''')
name='KNOWLEDGE.md'
text=(target/name).read_text(encoding='utf-8')
text=text.replace(r'cd C:\kefu\wechat_rpa',r'cd C:\wechat_rpa')
text=text.replace('所有写操作必须选择具体账号。「全部账号」只提供授权范围内的汇总视图。知识不跨租户共享。','整理、编辑、发布与检索测试选择具体账号;全量审核支持全部授权账号并按租户分别处理。「全部账号」也提供授权范围内的汇总视图。知识不跨租户共享。')
text=text.replace('当前生产部署使用根目录', '本目录的完整部署入口使用根目录').replace('`wechat_rpa/backend_deploy`','`backend_deploy`')
text=text.replace('在生产部署目录执行:','在源码根目录执行(详见 `deploy/im-admin/README.md`):')
text=text.replace('docker compose up -d --build api gateway knowledge-worker','docker compose -f deploy/im-admin/compose.yaml up -d --build api gateway knowledge-worker')
text=text.replace('docker compose logs --tail=100 knowledge-worker','docker compose -f deploy/im-admin/compose.yaml logs --tail=100 knowledge-worker')
put(name,text)
name='BACKEND.md';text=(target/name).read_text(encoding='utf-8')
text=text.replace('项目内置了一个零额外依赖的配置后台,用于统一管理登录账号、角色、AI 模型和 MCP 配置。','项目内置配置后台,用于统一管理登录账号、角色、AI 模型、MCP 和聊天知识库。')
old='> 如果要把后台部署到另一台电脑、局域网服务器或云服务器,请直接查看独立文档 [BACKEND_DEPLOY.md](BACKEND_DEPLOY.md)。部署端只需要 `admin_api.py`(及它依赖的 `admin_backend.py` 数据层),不需要上传桌面端代码或安装桌面端依赖。'
new='> 当前协议版已包含完整前端、知识中心和 Worker。使用说明见 [INTEGRATION_20260916.md](INTEGRATION_20260916.md),服务端部署见 [deploy/im-admin/README.md](deploy/im-admin/README.md)。完整后台应使用 `python run_backend.py --db backend.db` 启动 API、网关和知识 Worker;单独启动 API 不执行后台知识任务。'
assert old in text;text=text.replace(old,new)
text=text.replace('[BACKEND_DEPLOY.md](BACKEND_DEPLOY.md)','[deploy/im-admin/README.md](deploy/im-admin/README.md)')
put(name,text)
name='AGENT_SYNC_20260916.md';text=(target/name).read_text(encoding='utf-8')
text+='\n\n## 后续完整整合\n\n同日后续整合已补齐本文当时尚未同步的知识库、归档性能和管理前端,另补充协议回复知识引用与风险审核。本文保留此前同步记录;当前功能和启动方式以 [INTEGRATION_20260916.md](INTEGRATION_20260916.md) 为准。\n'
put(name,text)
verification={'date':'2026-09-16','target':str(target),'passed':1142,'skipped':4,
'skip_reason':'Optional Unicorn CPU emulator not installed',
'suites':{'knowledge_and_gateway':{'passed':96},'protocol_knowledge_integration':{'passed':7},
'backend_archive_agents_router':{'passed':326},'protocol_qt_packaging':{'passed':217,'skipped':4},'client':{'passed':496}},
'browser_search':json.loads((folder/'qa/result.json').read_text(encoding='utf-8')),
'browser_review':json.loads((folder/'review-qa/result.json').read_text(encoding='utf-8')),
'artifacts':str(folder),'live_customer_messages_sent':False,'live_server_deployed':False,'installer_rebuilt':False}
put('INTEGRATION_VERIFICATION_20260916.json',json.dumps(verification,ensure_ascii=False,indent=2)+'\n')
# Validate complete staged payload and all protocol programs intentionally retained.
manifest=[]
for path in sorted(payload.rglob('*')):
if not path.is_file():continue
relative=path.relative_to(payload).as_posix()
assert sha(path)==sha(target/relative),relative
if path.suffix=='.py':ast.parse(path.read_text(encoding='utf-8-sig'))
manifest.append({'path':relative,'sha256':sha(path),'bytes':path.stat().st_size,
'before_sha256':original[relative]['before_sha256']})
for name,digest in record['preserved'].items():assert sha(target/name)==digest,name
record.update(manifest=manifest,files=len(manifest),verified=True,verification='INTEGRATION_VERIFICATION_20260916.json')
for path in [folder/'final.json',target/'INTEGRATION_MANIFEST_20260916.json']:
path.write_text(json.dumps(record,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({'files':len(manifest),'preserved_programs':len(record['preserved']),'passed':1142,'skipped':4,'backup':str(backup)},ensure_ascii=False))