| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- """Sync only the current handbook's installation step to a verified publication."""
- import hashlib
- import json
- from pathlib import Path
- ROOT = Path(__file__).resolve().parents[1]
- OUT = ROOT / 'docs/workshop-quality-r13-20261110'
- PUBLICATION = ROOT / 'docs/ksf-workshop-skill-20261110-r13-r2'
- KEY_EVIDENCE_FEEDBACK = '''反馈关键分析依据信息:涉及数据判断时,只呈现直接支撑结论或会改变建议的来源、分析对象与时间范围、实际有效样本,并用代表原话、产品对照或关键统计解释依据与判断的关系;比例注明n/N及口径,复用资料与本轮新增采集分开,关键反例和适用限制紧邻结论。未取得的依据如实标明,不补造数字。完整采集明细、逐条处理记录和工具日志按Skill保留供追溯,无需在对话中展开;本节核心报告与成果仍须完整交付。'''
- def sha(path):
- return hashlib.sha256(path.read_bytes()).hexdigest()
- def dump(path, value):
- path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + '\n', encoding='utf8')
- def configure_install(data):
- if data['release'].get('entryMode') == 'fmode-enterprise-agent':
- raise ValueError('This handbook uses website onboarding; attendee installation is retired.')
- receipt = json.loads((PUBLICATION / 'online-release-receipt.json').read_bytes())
- assert receipt['ok'] and receipt['publicResourcesPublished']
- assert receipt['workshopVersion'] == 'r13'
- archive = receipt['archive']
- assert sha(PUBLICATION / 'publish' / archive['path']) == archive['sha256']
- prompt = (PUBLICATION / '员工安装正文.txt').read_text('utf8').strip()
- assert receipt['guideUrl'] in prompt
- data['release'].update(
- version='r13', installVersion='r13', guide=receipt['guideUrl'],
- installReleaseId=receipt['releaseId'],
- deploymentStatus='manual-local-skill-published')
- step = next(s for s in data['steps'] if s['id'] == 'install')
- step['prompt'] = prompt
- step['deliverables'][0] = '工作坊Skill正式r13的安装或核验结果(45个公开文件)'
- return receipt
- def configure_website_entry(data):
- data['release'].update(entryMode='fmode-enterprise-agent', skillManagedBy='organizer')
- group = next(g for g in data['groups'] if g['id'] == 'prepare')
- group.update(title='进入工作坊,明确本组课题',
- description='登录企业网站,在本组独立会话中使用统一的工作坊智能体。')
- step = next(s for s in data['steps'] if s['id'] == 'install')
- step.clear()
- step.update(
- id='install', group='prepare', kind='introduction', number='00', title='使用说明',
- introduction='本手册采用 fmode 企业网站的统一智能体。工作坊 Skill 和相关工具由组织方统一配置;学员无需安装 Skill,也无需复制安装提示词。',
- usage=[
- {'title': '登录并进入智能体', 'text': '打开组织方提供的 fmode 企业网站入口,登录后进入指定的工作坊智能体。'},
- {'title': '保留本组独立会话', 'text': '各组使用同一个智能体入口,每组保留独立会话。先说明小组名称和研究课题,后续在本组会话中接续,避免混入其他小组的成果。'},
- {'title': '从 01 开始逐节推进', 'text': '复制各节提示词发送给智能体,回答必要的业务问题,检查本节产出后再进入下一节。不同小组按各自课题与证据展开研究。'},
- {'title': '核对报告与成果', 'text': '每节检查完整报告和关键分析依据;保存后核对成果编号、回读内容和本组归属。若保存未成功,让智能体明确说明。'},
- {'title': '确认方案后生成示意图', 'text': '最终方案经本组明确确认后,再由智能体调用 image 技能(image 2.6)生成示意图,并与对应方案一起保存。'}
- ],
- checks=['已找到组织方指定的工作坊智能体。', '本组使用独立会话,课题和小组名称清楚。'],
- prompt='')
- for index, item in enumerate(data['steps']):
- item['number'] = f'{index:02d}'
- def configure_evidence_feedback(data):
- if KEY_EVIDENCE_FEEDBACK not in data['common']:
- data['common'] += '\n\n' + KEY_EVIDENCE_FEEDBACK
- for step in data['steps']:
- if step['id'] != 'install' and step.get('prompt') and KEY_EVIDENCE_FEEDBACK not in step['prompt']:
- step['prompt'] += '\n\n' + KEY_EVIDENCE_FEEDBACK
- def render_manual(data, prompts):
- manual = OUT / 'manual'
- content = 'window.WORKSHOP_MANUAL_DATA = ' + json.dumps(data, ensure_ascii=False, indent=2) + ';\n'
- (manual / 'content.js').write_text(content, encoding='utf8')
- js = (manual / 'manual.js').read_text('utf8')
- start = js.index(' const promptMap = ')
- end = js.index('\n', start)
- mapping = {s['id']: s['fullPrompt'] for s in prompts['steps']}
- js = js[:start] + ' const promptMap = ' + json.dumps(mapping, ensure_ascii=False) + ';' + js[end:]
- (manual / 'manual.js').write_text(js, encoding='utf8')
- standalone_js = js.replace('href="../${escape(step.example)}"', 'href="${escape(step.example)}"').replace('href="../参考学习与类型对应.html"', 'href="参考学习与类型对应.html"')
- h = (manual / 'index.html').read_text('utf8')
- css = (manual / 'manual.css').read_text('utf8')
- standalone = h.replace('<link rel="stylesheet" href="manual.css">', '<style>' + css + '</style>').replace('<script src="content.js" defer></script>', '').replace('<script src="manual.js" defer></script>', '')
- standalone = standalone.replace('href="../index.html"', 'href="index.html"').replace('href="../案例报告合集.html"', 'href="案例报告合集.html"')
- safe = lambda text: text.replace('</script', '<\\/script')
- standalone = standalone.replace('</body>', '<script>' + safe(content) + '</script><script>' + safe(standalone_js) + '</script></body>')
- html_path = OUT / '康师傅工作坊操作手册.html'
- html_path.write_text(standalone, encoding='utf8')
- return html_path
- def update_key_evidence():
- source = OUT / 'manual-source.json'
- archive_path = OUT / 'prompts-and-outputs.json'
- data = json.loads(source.read_bytes())
- prompts = json.loads(archive_path.read_bytes())
- before = json.loads(json.dumps(data))
- old_common = data['common']
- skill = ROOT / 'skills/ksf-workshop-runtime'
- skill_hashes = {p.relative_to(skill).as_posix(): sha(p) for p in skill.rglob('*') if p.is_file()}
- configure_evidence_feedback(data)
- for step, saved in zip(data['steps'], prompts['steps'], strict=True):
- assert step['id'] == saved['id']
- if step['id'] == 'install':
- assert step == before['steps'][0]
- assert saved['fullPrompt'] == step['prompt']
- continue
- if step.get('prompt'):
- saved['prompt'] = step['prompt']
- saved['fullPrompt'] = step['prompt']
- else:
- assert saved['fullPrompt'].startswith(old_common)
- saved['fullPrompt'] = data['common'] + saved['fullPrompt'][len(old_common):]
- assert saved['fullPrompt'].count(KEY_EVIDENCE_FEEDBACK) == 1
- for old, new in zip(before['steps'], data['steps'], strict=True):
- assert {k: v for k, v in old.items() if k != 'prompt'} == {k: v for k, v in new.items() if k != 'prompt'}
- dump(source, data)
- dump(archive_path, prompts)
- html_path = render_manual(data, prompts)
- assert skill_hashes == {p.relative_to(skill).as_posix(): sha(p) for p in skill.rglob('*') if p.is_file()}
- manifest_path = OUT / '本地发行清单.json'
- manifest = json.loads(manifest_path.read_bytes())
- manifest.update(manualSha256=sha(html_path), feedbackRule='key-analysis-evidence')
- dump(manifest_path, manifest)
- update = dict(ok=True, scope='research-feedback-prompts-only',
- feedbackRule=KEY_EVIDENCE_FEEDBACK, researchPromptsUpdated=14,
- installPromptUnchanged=True, coreDeliverablesUnchanged=True,
- skillFilesUnchanged=len(skill_hashes), manualPublished=False, productionWrites=0)
- receipt_path = OUT / '关键分析依据反馈更新核验.json'
- dump(receipt_path, update)
- validation_path = OUT / '交付文件校验.json'
- validation = json.loads(validation_path.read_bytes())
- validation['keyEvidenceFeedbackUpdate'] = update
- for name in ['manual-source.json', 'prompts-and-outputs.json', 'manual/content.js',
- 'manual/manual.js', html_path.name, manifest_path.name, receipt_path.name]:
- validation['files'][name] = sha(OUT / name)
- dump(validation_path, validation)
- print(json.dumps(update, ensure_ascii=False))
- def main():
- source = OUT / 'manual-source.json'
- archive_path = OUT / 'prompts-and-outputs.json'
- data = json.loads(source.read_bytes())
- prompts = json.loads(archive_path.read_bytes())
- before = {s['id']: s for s in prompts['steps'] if s['id'] != 'install'}
- before_sha = hashlib.sha256(json.dumps(before, ensure_ascii=False, sort_keys=True).encode()).hexdigest()
- receipt = configure_install(data)
- install = next(s for s in data['steps'] if s['id'] == 'install')
- for i, step in enumerate(prompts['steps']):
- if step['id'] == 'install':
- prompts['steps'][i] = {**install, 'fullPrompt': install['prompt']}
- assert before == {s['id']: s for s in prompts['steps'] if s['id'] != 'install'}
- dump(source, data)
- dump(archive_path, prompts)
- html_path = render_manual(data, prompts)
- manifest_path = OUT / '本地发行清单.json'
- manifest = json.loads(manifest_path.read_bytes())
- manifest.update(manualSha256=sha(html_path), bundledSkillVersion='r13-local-quality',
- currentInstallVersion='r13', currentInstallReleaseId=receipt['releaseId'],
- currentInstallGuide=receipt['guideUrl'])
- dump(manifest_path, manifest)
- update = dict(ok=True, scope='installation-step-only', installVersion='r13',
- releaseId=receipt['releaseId'], guideUrl=receipt['guideUrl'],
- nonInstallStepsUnchanged=len(before), nonInstallStepsSha256=before_sha,
- installPromptSha256=hashlib.sha256(install['prompt'].encode()).hexdigest(),
- manualPublished=False, productionWrites=0)
- dump(OUT / '安装提示词更新核验.json', update)
- validation_path = OUT / '交付文件校验.json'
- validation = json.loads(validation_path.read_bytes())
- validation['installationPromptUpdate'] = update
- for name in ['manual-source.json', 'prompts-and-outputs.json', 'manual/content.js',
- 'manual/manual.js', html_path.name, manifest_path.name, '安装提示词更新核验.json']:
- validation['files'][name] = sha(OUT / name)
- dump(validation_path, validation)
- print(json.dumps(update, ensure_ascii=False))
- if __name__ == '__main__':
- import argparse
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('--key-evidence', action='store_true')
- args = parser.parse_args()
- update_key_evidence() if args.key_evidence else main()
|