56 lines
5.3 KiB
Python
56 lines
5.3 KiB
Python
from pathlib import Path
|
||
import json, hashlib, tarfile
|
||
root = Path('C:/kefu')
|
||
name = 'knowledge-v4-review-20260916'
|
||
release = root/'deploy'/name; release.mkdir(exist_ok=True)
|
||
old = 'knowledge-v3-field-fix-20260916'
|
||
previous = root/'deploy'/old
|
||
p = root/'wechat_rpa/KNOWLEDGE.md'
|
||
s = p.read_text(encoding='utf-8')
|
||
section = '''
|
||
|
||
## 全量审核(2026-09-16)
|
||
|
||
知识与审核页支持指定账号或全部授权账号的一键审核,覆盖全部页面,不受当前搜索、状态筛选与勾选影响。对象是已经整理出的待审核知识草稿;原始聊天需先整理为草稿。审核通过只进入待发布,不发布、不启用客服检索、不调用模型。
|
||
|
||
流程:选择范围 → 预览待审/可通过/需处理数量与按账号统计 → 一次确认范围已完成必要审核 → 后台分批执行。预览以草稿 ID、租户、版本及更新时间固定快照,有效期 30 分钟;同一用户新预览使旧预览失效。之后新增、修改、删除、已驳回或已发布条目均不会被旧快照误审核。
|
||
|
||
SQL 检查必填及长度、适用条件、有效期、来源存在及最新版本。每批最多 100 条,状态、审核历史、审计、结果及进度在同一短事务内提交。后台在独立线程运行,不受模型网络调用阻塞;关闭页面、进程重启后按已提交进度继续,支持暂停、继续、停止。失败事务不会半批提交。
|
||
|
||
每批重查操作者的审核、知识读取、原文权限和账号授权。权限撤回或用户停用会暂停为需处理状态;某个账号授权撤回则跳过该账号。任务只对创建者可见,结果清单再次按当前账号权限分页过滤。未通过知识保持原状态并列出原因,可直接打开对应账号的知识详情。
|
||
|
||
表:knowledge_review_job、knowledge_review_target;应用启动自动创建。回滚旧应用保留这些表及任务进度,旧版不执行新审核队列,已完成审核保留为待发布。
|
||
|
||
接口(均要求 knowledge:read、knowledge:review、im:content:read):POST /api/v2/knowledge/review-tasks/preview?account_id=-1;GET /review-tasks;GET /review-tasks/{id};GET /review-tasks/{id}/results;POST /review-tasks/{id}/{start|pause|resume|cancel}。start 需 confirmed=true,重复提交不会重复入队,results 默认每页 30、最大 100。
|
||
|
||
验证:92 项后端回归,前端类型检查、生产构建、隔离浏览器跨页及跨账号流程。50 万条虚构知识预览 6.547 秒,连续 10 批共通过 1000 条、平均每 100 条 0.066 秒,Python tracemalloc 峰值 0.127 MB(不含 SQLite 原生内存和系统缓存),预览响应 636 字节;性能是本地模拟数据结果。线上部署验证不提交审核任务,不修改真实知识审核状态。
|
||
'''
|
||
if '## 全量审核(2026-09-16)' not in s:
|
||
p.write_text(s+section, encoding='utf-8', newline='\n')
|
||
files = [root/'wechat_rpa'/f for f in ['knowledge_review.py','knowledge_store.py','knowledge_api.py','knowledge_worker.py','test_knowledge_review.py','KNOWLEDGE.md']]
|
||
files += [root/'admin-web/apps/web-antd/src'/f for f in ['api/knowledge.ts','views/knowledge/center.vue','views/knowledge/review-all.vue']]
|
||
files += [f for f in (root/'admin-web/apps/web-antd/dist').rglob('*') if f.is_file()]
|
||
manifest = []
|
||
with tarfile.open(release/'payload.tar.gz','w:gz') as tf:
|
||
for path in files:
|
||
relative = path.relative_to(root).as_posix(); data=path.read_bytes()
|
||
tf.add(path, arcname=relative)
|
||
manifest.append({'path':relative,'bytes':len(data),'sha256':hashlib.sha256(data).hexdigest()})
|
||
(release/'manifest.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2),encoding='utf-8')
|
||
for filename in ['01-stage.sh','02-backup-build.sh','03-deploy.sh','04-verify.sh','Dockerfile.overlay','rollback.sh']:
|
||
text = (previous/filename).read_text(encoding='utf-8').replace(old,name)
|
||
if filename == '01-stage.sh':
|
||
text = text.replace('sha256:15bf0aa7a0041b5b79cee60c3fc21e888ca87d3268abf183e5644ac5efd784de', 'sha256:b8221de05e2ab92afc66ab8b725ed24600da3e717a543998ae17a6611a011acf')
|
||
text = text.replace('/home/ps/knowledge-v3-20260916/manifest.json', f'/home/ps/{old}/manifest.json')
|
||
text = text.replace('verified third release', 'verified model-field fix release')
|
||
if filename == '02-backup-build.sh':
|
||
text = text.replace('test_knowledge_model_output test_model_gateway', 'test_knowledge_model_output test_knowledge_review test_model_gateway')
|
||
if filename == '03-deploy.sh':
|
||
text = text.replace('Third-version', 'Full-review version')
|
||
if filename == '04-verify.sh':
|
||
text = text.replace("'knowledge_job','knowledge_watch']", "'knowledge_job','knowledge_watch','knowledge_review_job','knowledge_review_target']")
|
||
text = text.replace("('/api/v2/knowledge/overview',401,'GET'),", "('/api/v2/knowledge/overview',401,'GET'),('/api/v2/knowledge/review-tasks',401,'GET'),('/api/v2/knowledge/review-tasks/preview',401,'POST'),")
|
||
text = text.replace("assert after['enabled_tenants']==", "assert after['counts']['knowledge_review_job']==0 and after['counts']['knowledge_review_target']==0, 'Deployment must not submit reviews'\nassert after['enabled_tenants']==")
|
||
(release/filename).write_text(text,encoding='utf-8',newline='\n')
|
||
print(json.dumps({'files':len(manifest),'payload_bytes':(release/'payload.tar.gz').stat().st_size,'release':str(release)}))
|