package-workshop-onboarding-online.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. """Prepare the attachment-free distribution from the sealed, locally tested kit."""
  2. from pathlib import Path
  3. import hashlib
  4. import json
  5. import zipfile
  6. ROOT = Path(__file__).resolve().parents[1]
  7. PRIOR = ROOT / 'docs/workshop-onboarding-20261109'
  8. OUT = ROOT / 'docs/workshop-onboarding-20261109-r2'
  9. RELEASE = 'ksf-workshop-onboarding-20261109-r2'
  10. sha = lambda raw: hashlib.sha256(raw).hexdigest()
  11. encode = lambda value: (json.dumps(value, ensure_ascii=False, indent=2) + '\n').encode('utf-8')
  12. GUIDE = '''# 康师傅产品创新工作坊 · 安装与初始化
  13. 学员只需复制初始化提示词。由当前数字人读取本说明、下载资源并安装,不要求学员上传附件、填写技术目录或手动执行命令。
  14. ## 资源与核验
  15. 以本说明所在固定 Git 提交目录为下载根目录,读取同目录 `distribution.json`。按其中相对路径下载完整 ZIP,核对字节数和 SHA-256;HTTP 200 或网页预览不算下载成功。该发行版内含完整康师傅产品创新 Skill r7(43 个公开文件);VOC 用户研究 Skill 由安装器从官方 npm 下载 `@vocmarket/voc-skill@0.3.42`,并校验原包摘要及 169 个成员。无需先安装任何旧版本。
  16. 仅使用清单内资源。Git 提交固定本次安装版本,不自动切换 main 或最新版。已核包的 ZIP 在新的临时目录解压,先检查无重复路径、路径越界或符号链接,再按 `FILES.sha256` 逐项核验。安装器和本地检查脚本的发行指纹同时列在 distribution.json。
  17. ## 识别当前环境
  18. 从宿主实际的技能管理、配置说明或受支持接口识别 Skills 目录和工具接入方法。不要猜 `.claude/skills`、`.codex/skills` 或 `/opt/data/skills`。有原生导入机制则使用;有文件及命令能力时按下文执行。只有聊天/网页阅读能力时,说明实际缺少的安装入口,给出当前平台真实可用的下一步,不把读文件称为已安装。
  19. 运行准备需要 Python 3.9+、Node.js 18+ 和 npm。Skill 不绑定模型供应商。
  20. ## 安装
  21. 在解压目录执行,参数由数字人根据当前环境填写实际绝对路径:
  22. ```text
  23. python -B install.py --skills-dir <实际Skills目录>
  24. python -B install.py --skills-dir <实际Skills目录> --out <新的结果目录> --apply --prepare-runtime
  25. ```
  26. 第一步只预检。已有相同完整版本时复用;不同版本或混装时保留现状、查明差异,沿当前环境实际支持的升级机制处理,不覆盖或另建重复入口。结果目录不覆盖。保留其他技能、宿主配置和已授权企业资源。
  27. 官方 VOC 原包 `.mcp.json` 含作者机器路径,安装器在校验原包后只适配安装副本的这一份配置,并记录前后指纹;另外输出当前路径的 `voc-mcp.fragment.json`。用宿主正常机制注册工具,保留其他配置。生成配置片段不算注册或调用通过。
  28. ## 加载与检查
  29. 实际加载两个 Skill 入口;读取产品创新的研究闭环、主动澄清和报告设计细则,以及 VOC 的接口说明。验证官方本地工具发现、接口搜索和参数文档读取。本地安装及 SDK 已测试通过;这不替代当前宿主的实际加载与工具接入验证。
  30. 企业登录及已有资料/成果保存通道复用平台当前授权。需要登录时使用正常登录界面;不在对话索要或展示密钥,不复制其他人的身份、绑定或研究库。本次仅初始化,不采业务数据、不建业务记录、不续跑旧案例。企业资源未接通时单列状态,仍保留可用的分析与本地报告能力。
  31. ## 协作方式
  32. 围绕面类研发创新,按“专业分析 → 结合已有资料 → 自查关键缺口 → 调用可用 VOC 补证 → 核原文并修订 → 产出”协作。业务输入缺少会改变研究路线的条件时主动澄清;能自行核查的事实直接核查。报告要回答产品选择、消费者任务、研发取舍及下一项判别,避免用泛行业综述或资料堆砌代替判断。
  33. ## 完成反馈
  34. 用一张简表分别说明产品创新 Skill 安装/加载、VOC Skill 安装/加载、本地工具、已有资料读取和成果保存的实际状态。区分安装、本地运行、宿主接入和真实数据访问;未核验项说明具体条件,不把部分通过称为全部就绪。详细日志留本地,不向学员堆放技术过程。
  35. '''
  36. PROMPT = '''请从本次提示词给定的线上安装说明开始,自动下载并核验固定版本资源,安装 VOC 用户研究 Skill 和康师傅产品创新 Skill。无需附件,不要求我下载、上传文件或选择技术目录。
  37. 先识别当前环境实际支持的安装和执行方式。按发行清单核验资源,先预检再安装,已有相同完整版本复用,版本不符时保留现状并查明差异。实际加载两个入口,验证本地接口搜索和参数文档;文件存在不代替加载或调用。
  38. 按“专业分析→已有资料→自查缺口→VOC 补证→原文核查→产出”协作。只初始化,不启动案例、不采业务数据、不创建业务记录。企业登录和资料连接复用平台正常授权方式,不在聊天索要密钥。
  39. 完成后简表说明两项技能、VOC 工具、已有资料和成果保存各自的实际状态。若当前环境不支持安装,说明具体缺项及实际可用入口,不声称已安装。
  40. '''
  41. def main():
  42. prior_receipt = json.loads((PRIOR / 'release-ready.json').read_bytes())
  43. old_archive = PRIOR / prior_receipt['filename']
  44. assert sha(old_archive.read_bytes()) == prior_receipt['sha256']
  45. assert not (OUT / 'publish').exists(), 'Never replace a prepared distribution'
  46. with zipfile.ZipFile(old_archive) as source:
  47. package = {name: source.read(name) for name in source.namelist() if name != 'FILES.sha256'}
  48. meta = json.loads(package['release.json'])
  49. meta['releaseId'] = RELEASE
  50. meta['distributionMode'] = 'fixed-git-commit-guide-and-download-no-user-attachment'
  51. meta['basedOn'] = {'releaseId': prior_receipt['releaseId'], 'sha256': prior_receipt['sha256']}
  52. package['release.json'] = encode(meta)
  53. package['README.md'] = GUIDE.encode('utf-8')
  54. package['初始化提示词.txt'] = PROMPT.encode('utf-8')
  55. validation = json.loads(package['local-validation.json'])
  56. validation['distributionOnlyChange'] = True
  57. validation['previousSealedPackageSha256'] = prior_receipt['sha256']
  58. assert validation['installerSha256'] == sha(package['install.py'])
  59. assert validation['runtimeCheckSha256'] == sha(package['check-voc.cjs'])
  60. package['local-validation.json'] = encode(validation)
  61. package['FILES.sha256'] = ''.join(sha(raw) + ' ' + name + '\n'
  62. for name, raw in sorted(package.items())).encode('utf-8')
  63. pub = OUT / 'publish'
  64. pub.mkdir(parents=True)
  65. target = pub / (RELEASE + '.zip')
  66. for name, raw in package.items():
  67. path = OUT / 'package' / name
  68. path.parent.mkdir(parents=True, exist_ok=True)
  69. path.write_bytes(raw)
  70. with zipfile.ZipFile(target, 'x', zipfile.ZIP_DEFLATED) as z:
  71. for name, raw in sorted(package.items()):
  72. info = zipfile.ZipInfo(name, (2026, 11, 9, 0, 0, 0))
  73. info.external_attr = 0o100600 << 16
  74. z.writestr(info, raw, compress_type=zipfile.ZIP_DEFLATED)
  75. with zipfile.ZipFile(target) as z:
  76. assert z.testzip() is None and all(z.read(n) == raw for n, raw in package.items())
  77. distribution = {
  78. 'schema': 'ksf.workshop-onboarding-distribution.v1', 'releaseId': RELEASE,
  79. 'archive': {'path': target.name, 'bytes': target.stat().st_size, 'sha256': sha(target.read_bytes())},
  80. 'installerSha256': sha(package['install.py']), 'runtimeCheckSha256': sha(package['check-voc.cjs']),
  81. 'workshopSkill': {'version': 'r7', 'files': 43, 'entrySha256': meta['workshop']['files']['SKILL.md']},
  82. 'voc': {k: meta['voc'][k] for k in ('name', 'version', 'url', 'sha256', 'bytes')},
  83. 'thirdPartyArtifactIncluded': False, 'credentialsIncluded': False, 'databaseIncluded': False,
  84. 'userAttachmentRequired': False, 'localRuntimeValidated': True, 'targetHostAcceptanceVerified': False,
  85. }
  86. (pub / 'distribution.json').write_bytes(encode(distribution))
  87. (pub / 'INSTALL.md').write_text(GUIDE, encoding='utf-8')
  88. (OUT / '一键初始化正文.txt').write_text(PROMPT, encoding='utf-8')
  89. (OUT / 'README.md').write_text(GUIDE, encoding='utf-8')
  90. (OUT / 'build-receipt.json').write_bytes(encode(distribution))
  91. print(json.dumps(distribution, ensure_ascii=False, indent=2))
  92. if __name__ == '__main__':
  93. main()