| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- """Prepare/finalize the query-rule handoff without copying private databases."""
- import argparse
- import hashlib
- import json
- from pathlib import Path
- import shutil
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- DIST = Path('F:/文档/康师傅skill')
- WORK = ROOT / 'work/agent-query-20261106'
- OUT = ROOT / 'artifacts/数据库精准检索规则-20261106'
- NAME = 'ksf-agent-query-rules-20261106-r1'
- RELEASE = OUT / NAME
- SKILLS = ('ksf-workshop-runtime', 'ksf-voc-library')
- def sha(path):
- with path.open('rb') as stream:
- return hashlib.file_digest(stream, 'sha256').hexdigest()
- def write(path, data):
- path.parent.mkdir(parents=True, exist_ok=True)
- path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
- def files(folder):
- return {p.relative_to(folder).as_posix(): sha(p) for p in sorted(folder.rglob('*')) if p.is_file() and '__pycache__' not in p.parts}
- def manifest(folder):
- entries = {name: value for name, value in files(folder).items() if name != 'FILES.sha256'}
- (folder / 'FILES.sha256').write_text(''.join(value + ' ' + name + '\n' for name, value in entries.items()), encoding='utf-8', newline='\n')
- def prepare():
- if RELEASE.exists():
- raise ValueError('RELEASE_ALREADY_PREPARED')
- for name in ('query_voc.py', 'voc_db.py', 'retrieval.py', 'schema.sql', 'taxonomy.json'):
- shutil.copyfile(ROOT / 'server/voc-library' / name, ROOT / 'skills/ksf-voc-library/scripts' / name)
- for name in SKILLS:
- manifest(ROOT / 'skills' / name)
- shutil.copytree(ROOT / 'skills' / name, RELEASE / 'skills' / name)
- baselines = {name: [files(WORK / 'baseline-skills' / name)] for name in SKILLS}
- with zipfile.ZipFile(DIST / '历史归档/20261104-先查库/ksf-workshop-runtime-20261104.zip') as archive:
- baselines['ksf-workshop-runtime'].append({name: hashlib.sha256(archive.read(name)).hexdigest() for name in archive.namelist() if not name.endswith('/')})
- write(RELEASE / 'baselines.json', baselines)
- shutil.copyfile(ROOT / 'scripts/install-query-skills.py', RELEASE / 'install-query-skills.py')
- for source in (ROOT / 'docs/voc-agent-query-20261106').glob('*.md'):
- target = RELEASE / 'docs' / source.name
- target.parent.mkdir(parents=True, exist_ok=True)
- target.write_text(source.read_text(encoding='utf-8').replace('../../skills/', '../skills/'), encoding='utf-8')
- scope = dict(platform='jd', brand='ksf', category='banmian')
- examples = {
- '01-jd-ksf-banmian-products': dict(entities=['products'], filters=scope, limit=20),
- '02-jd-ksf-banmian-reviews': dict(entities=['reviews'], filters=scope, quality='analysis', limit=20),
- '03-jd-ksf-banmian-review-audit': dict(entities=['reviews'], filters=scope, quality='audit', groupBy=['isDefault', 'identityState', 'categoryState'], limit=100),
- '04-brand-products': dict(entities=['products'], filters=dict(brand='ksf'), limit=20),
- '05-nutrition-documents': dict(entities=['documents', 'clauses'], text=dict(any=['营养标签', '营养成分表']), limit=20),
- '06-jd-ksf-banmian-prices-specs': dict(entities=['prices', 'specs'], filters=scope, limit=20),
- '07-jd-ksf-banmian-portion': dict(entities=['reviews'], filters=scope, text=dict(concepts=['portion_size']), limit=20),
- '08-jd-ksf-banmian-review-statistics': dict(entities=['reviews'], filters=scope, groupBy=['platform', 'brand', 'category'], limit=100),
- '09-jd-ksf-banmian-coverage': dict(entities=['coverage'], filters=scope, limit=20),
- }
- for name, data in examples.items():
- write(RELEASE / 'examples' / (name + '.json'), data)
- write(RELEASE / 'release.json', dict(version=NAME, agentRulesVersion='ksf.agent-data-rules.20261106.v1',
- schema='ksf.voc-library.v2', retrievalContract='ksf.voc-retrieval.v2', databaseIncluded=False,
- databaseMigrationRequired=False, productionInstalled=False, realMessageVerified=False,
- workshopRuntimeScriptsChanged=False, researchScriptsChanged=['voc_db.py', 'retrieval.py']))
- print(json.dumps(dict(prepared=str(RELEASE), next='Run verify-query-skills.py before finalize'), ensure_ascii=False))
- def finalize():
- validation = json.loads((WORK / 'validation.json').read_text(encoding='utf-8'))
- if not validation['ok'] or (OUT / (NAME + '.zip')).exists():
- raise ValueError('VALIDATION_REQUIRED_OR_FROZEN_ZIP_EXISTS')
- for name in SKILLS:
- if files(ROOT / 'skills' / name) != files(RELEASE / 'skills' / name):
- raise ValueError('SKILL_CHANGED_AFTER_PREPARATION')
- shutil.copyfile(WORK / 'validation.json', RELEASE / 'local-validation.json')
- manifest(RELEASE)
- archive = OUT / (NAME + '.zip')
- with zipfile.ZipFile(archive, 'x', zipfile.ZIP_DEFLATED) as z:
- for path in sorted(RELEASE.rglob('*')):
- if path.is_file():
- z.write(path, path.relative_to(RELEASE).as_posix())
- receipt = dict(release=NAME, filename=archive.name, bytes=archive.stat().st_size, sha256=sha(archive),
- files=len(files(RELEASE)), localValidated=True, productionInstalled=False, realMessageVerified=False)
- write(OUT / 'delivery-receipt.json', receipt)
- for name in SKILLS:
- source, target = ROOT / 'skills' / name, DIST / name
- for path in source.rglob('*'):
- if path.is_file():
- out = target / path.relative_to(source)
- out.parent.mkdir(parents=True, exist_ok=True)
- shutil.copyfile(path, out)
- shutil.copyfile(archive, DIST / archive.name)
- write(DIST / '版本与校验.json', dict(preparedDate='2026-11-06', release=NAME,
- skills={name: dict(files=files(DIST / name), manifestSha256=sha(DIST / name / 'FILES.sha256')) for name in SKILLS},
- zip=receipt, previousPreparedVersion='历史归档/20261105-r2-查询规则更新前.zip',
- databaseIncluded=False, productionInstalled=False, realMessageValidated=False))
- (DIST / 'README.md').write_text('''# 当前Skill分发 · 2026-11-06
- 当前安装入口:ksf-agent-query-rules-20261106-r1.zip;ZIP、两份展开Skill与版本与校验.json一致。
- - ksf-workshop-runtime:工作坊技能,研究底料先查研究库、报告和项目查业务库;仅说明和清单升级,身份、昵称、上传与运行脚本沿用原版。
- - ksf-voc-library:只读研究查询,精确字段AND、商品评价关联、来源回读、统计口径及同一generation校验;支持与看板使用同一registry/current.json。
- - 不含数据库,沿用已安装v2研究库。缺库时需先用原完整r2资料包建立授权库;不能只复制Skill就声称能查到资料。
- 本机准备和验证完成,未在王刚001生产实例安装/加载/真实消息验收。请将当前完整ZIP与包内docs/发给运维001.md一起转运维;安装后将docs/发给王刚001.md交本人真实消息验证。历史ZIP不再作为当前升级入口,旧r2展开内容已归档,原ZIP未修改。
- 新增同结构数据通过新代际更新,Agent新研究读取当前代际;新增字段/实体/分类定义才更新合同与Skill。没有新增采集工具,不调用付费上游。
- ''', encoding='utf-8')
- handoff = Path('F:/文档/新数字人交接')
- (handoff / '10-精准查库规则增量-20261106.md').write_text('''# 精准查库规则增量 · 2026-11-06
- 当前增量包:F:/文档/康师傅skill/ksf-agent-query-rules-20261106-r1.zip。
- 原00—09、materials与旧ZIP保持历史快照;新增实例在完成原有身份/运行时开通和授权研究库后,使用此增量包对齐两份Skill。不要把旧materials直接当成当前查询规则,或复用其他员工身份/会话/凭据。
- 包内含精确基线升级器、两份Skill、数据与来源合同、9个查询示例、运维与王刚001验收单及本地验证回执。不含数据库,无Schema迁移;同一研究任务固定generation,新增数据经同一current.json取新代际。
- 目前仅本地准备和测试完成,生产安装、Agent实际加载、真实消息验收仍待运维与本人回报。
- ''', encoding='utf-8')
- print(json.dumps(receipt, ensure_ascii=False, indent=2))
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('phase', choices=['prepare', 'finalize'])
- args = parser.parse_args()
- prepare() if args.phase == 'prepare' else finalize()
|