| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- """Build a reviewable public-source packet. No business API calls or writes."""
- from datetime import datetime, timezone
- import hashlib
- import json
- from pathlib import Path
- import shutil
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- CAPTURE = ROOT / 'work/S4-web-research-20261103'
- DOCS = Path('F:/文档/工作坊当前资料')
- STAGE = ROOT / 'work/S4-public-materials-20261103'
- ZIP = DOCS / 'ksf-S4-public-materials-20261103.zip'
- assert not STAGE.exists() and not ZIP.exists()
- STAGE.mkdir()
- def sha(data):
- return hashlib.sha256(data).hexdigest()
- def put(name, data):
- target = STAGE / name
- target.parent.mkdir(parents=True, exist_ok=True)
- with target.open('xb') as stream:
- stream.write(data.encode('utf-8') if isinstance(data, str) else data)
- result = '''# 网上测试资料核查与推进说明
- 核查日期:2026年11月3日,Codex实际访问公开网页、匿名浏览器查看及原图目视核对。当前测试不用先由公司提供照片。40号状态C仍保留:它只说明此前数字人机器的检查范围没有实物原件;本轮新取得网上资料不否定旧回报。41号外部补料改为可选,实物研究需要时再用。
- 已经取得两组直接资料,不使用搜索摘要作证据:
- | 材料 | 实际查看到的内容 | 定性与限制 |
- |---|---|---|
- | 苏宁公开高清图片页,商品编号12447641634 | 标题“康师傅方便面 燃魂拌面 浓浓炸酱面117g*5袋 非油炸 煮面夜宵速食五连包”;参数净含量585克、袋装;5张关联商品图 | 本次可访问的国内平台页面展示;不代表已下单、库存、实际发货规格或当前包装版本已核 |
- | 同页第5张单袋正面图 | “浓浓炸酱面”“煮面6-7分钟”“净含量:面饼+配料117克 面饼:90克” | 原图可读的包装标示;117克是含配料净含量,90克是图示面饼重量,均不是本轮称量值 |
- | 什么值得买帖子aeqxm74k,显示2023-12-18 21:20:15,作者“没事儿聊聊呗” | 开袋内容、煮制过程、包装背面照片;正文说“包装上写的煮面6-7分钟,实测写少了,煮了10分钟面才软。” | 历史作者自述和其展示的包装照片;不把作者的10分钟当成我们的试验结果或现行统一说明 |
- | 该帖第10张图(本包编号post-10) | 背面名“浓浓炸酱面”;制备段可读“先准备1L以上水煮沸,加入面条”“2分钟后挑散面条,4分钟放入菜包,煮面6-7分钟捞出”;有配料和营养表 | 是网上历史包装图,拍摄时间、批次及完整条码未核;不是公司持有实物。后面三张比较图属于其他商品,本交付包不纳入 |
- 苏宁标题117g×5与参数585克算术一致;这不证明页面图与当前仓内实物相同。帖中背面与苏宁正面都显示浓浓炸酱面及6—7分钟,但没有将两来源合成同一SKU或同批次。旧cq-01至04与本次商品的精确SKU匹配继续未知。
- ## 可直接使用的测试对象
- P1:苏宁12447641634页面所示的“燃魂拌面·浓浓炸酱面·袋装117g×5”;面饼90克与煮6—7分钟均明确写为商品图标示。P1是公开资料的对象标记,没有创建KfProduct或其他产品实体。
- P2:2023年帖子展示的浓浓炸酱面包装及作者的制备叙述。单独保存为历史旁证,不能与P1拼成一份完整当前SKU档案。帖中配料评价“干净”“健康”不采信为营养或安全结论;作者展示的订单截图也没有被当作已验证购买。
- ## 可以推进什么
- 当前材料足以继续公开资料支持的流程测试。已整理43号《S4公开资料桌面评估草稿》,将事实、假设、证据缺口与下一步检验分开,不必再让王刚001重新写同一份准备稿。
- 1. 规格信息核对:在一个真实页面内分别提取含料净含量、面饼重量与组合件数,检查标题/参数/图片是否吻合。
- 2. 煮制告知桌面核对:确认页面标题含“煮面”,单袋正面含时间,历史背面展示步骤;不能据此声称消费者已看懂、页面已有效提升认知。
- 3. 份量假设的证据与可行性评估:以明确的网页对象记录已有标示和缺口;是否吃饱、自然搭配、支付意愿等仍未测试。
- 实际试吃、称量、招募、页面认知测试和量产评估需要对应实物、人员或企业资料,届时分别补充;它们不是当前全部软件流程测试统一必备的前置。
- ## 来源与归档
- - 苏宁原页:https://m.suning.com/bigimages/12447641634.html
- - 单袋正面原图:https://imgservice.suning.cn/uimg1/b2c/image/4_-1P8wJu87-d-WFGC3YAg.jpg
- - 历史帖子:https://post.smzdm.com/p/aeqxm74k/
- - 历史背面原图:https://am.zdmimg.com/202312/18/65804034eddf17405.jpg_e1080.jpg
- 原始HTTP响应、浏览器DOM与可见正文、匿名截图、选定7张图片原字节、来源索引、字段表及校验清单见同目录ksf-S4-public-materials-20261103.zip。HTTP正文的编码派生件不作唯一依据;报告引用采用浏览器可见正文,原始字节保留供复核。包装标示由原图目视核,不声称OCR自动逐字认证。
- 实际采集时间保存在SOURCE-INDEX.json,UTC与北京时间可区分;2023年发表时间保持页面原串,不推断时区。浏览器临时档案不入包。原图未修改,联系表是另存的阅读辅助件。
- ## 当前系统状态
- 系统仍为S3,会话btrFqkz0yw。S3第2版ZW1FB5zeHr和S1/S2不动。没有调用任何workshop接口,没有入库、创建产品、独立机会卡/项目或保存S4。43号是可审阅正文,不是可执行保存JSON。
- 若下一步要测试系统内S4,应先明确选择一个待验证方向,并按现有合同关联独立机会卡及课堂项目,再保存项目下的评估;课堂项目不等于企业审批。当前“份量与用餐目的适配”可作为沿用S3的候选方向,不能擅自称员工已经选定。无需为了进入S4虚构试吃、评分、成本或市场结论。
- '''
- assessment = '''# S4公开资料桌面评估草稿
- 性质:依据真实公开资料形成的证据与验证可行性评估,尚未保存系统S4。不是试吃结果、市场机会确认或量产结论。参考既有S3第2版ZW1FB5zeHr;四篇旧引文不修改、不重导。
- ## 评估对象与问题
- 沿用S3“燃魂份量与用餐目的适配”作为候选问题:对明确的一份,在实际用餐目的下是否需要额外补主食?本次P1仅锁定苏宁12447641634页面所示浓浓炸酱面117g×5;当前实物、版本及与2023年原话对应的精确SKU未知。
- ## 已做的核对及结果
- | 评估项 | 直接依据 | 本次结果 | 仍不能判定 |
- |---|---|---|---|
- | 页面内规格一致性 | 苏宁标题117g×5,参数585克,单袋图面饼+配料117克、面饼90克 | 标题总重与参数总重算术一致,图示单份含料净含量吻合;两种重量分开记录 | 现货、实发版本、实物称量、配料包实测重量 |
- | 制备说明是否存在 | 苏宁单袋正面煮6—7分钟;2023年帖子背面准备1L以上水、分时挑散/放菜包/捞出 | 这两个来源中能找到煮制信息,来源与时点分开保留 | 页面首屏是否被用户注意、是否看懂;不证明绝不能泡;不代表全系列当前说明 |
- | 份量假设的支持程度 | 旧cq-03/_9mUml0wmh、cq-04/2bFGP7axMo两篇历史自述;本次网页图示重量 | 有历史问题线索,也有可供桌面分析的具体网页对象 | 旧帖是否同SKU、当前用户是否吃不饱、需求强度、普遍程度及支付意愿 |
- | 烹饪表现 | 新帖作者称自己煮10分钟才软;背面图示6—7分钟 | 只保留为历史个体叙述,不用10分钟替代包装说明 | 水量、火力、软硬偏好等未控制;不能据此判包装错误或实际工艺表现 |
- | 量产、成本与合规 | 未取得公司配方、产线/原料依据、报价或相应检验资料 | 无法评估,保留未知 | 不填写可量产、成本几乎为零或合规已通过 |
- ## 三个假设如何处理
- 份量方向:保留为待验证问题,本次没有证明加量必要。加菜与补主食的动机分开记录,提前补食和吃后补食都可能相关,习惯与补偿可并存。不以重量本身换算能否吃饱。
- 煮制告知方向:本页标题已有“煮面”,单袋图可见6—7分钟,所以下一步问题收窄为真实购买路径上的可见性与理解。包装有字不等于消费者已理解;本次没有受试者或A/B数据,也没有证明现页误导。
- 香辣留水方向:本次没有取得该方向具体商品的新原件,保留原假设。不得套用燃魂浓浓炸酱面的重量、时间或说明,不泛称所有香辣款均可泡。
- ## 判断与下一步
- 本次完成的是公开资料核对,可继续进行软件中的来源关联、证据审阅和阶段评估测试;实际研究结论尚不成立。无需反复寻找本机不存在的公司样品,亦无需等待用户先拍照。
- 若继续系统S4测试,先按既有合同将员工选定的候选方向关联课堂机会卡/项目,再关联新来源与评估报告;不能把S3的artifactType=opportunity_card当成已存在独立机会卡。不在无项目时冒报项目阶段已推进。
- 若以后实施份量研究,再补实际样品、获取记录和适合目标的观察方案;若先研究购买告知,补真实购买路径与观察对象。样本数、判断门槛、成本、负责人、排期未定不虚填。取得相反证据或动机混杂时调整问题;证据不足不等于已经证明无效。
- 本草稿不生成数值评分、KANO分类、配方、采购或招募;没有业务写入。来源详情及原件包见42号。
- '''
- put('42-网上测试资料核查与推进说明.md', result)
- put('43-S4公开资料桌面评估草稿.md', assessment)
- checks = []
- for name in ('suning-photo-117', 'post-label-review'):
- http = json.loads((CAPTURE / (name + '.capture.json')).read_text(encoding='utf-8'))
- assert http['status'] == 200
- assert sha((CAPTURE / (name + '.response.bin')).read_bytes()) == http['responseSha256']
- for ext in ('.capture.json', '.response.bin', '.text.txt'):
- put('http/' + name + ext, (CAPTURE / (name + ext)).read_bytes())
- for ext in ('.dom.html', '.text.txt', '.png', '.detail-0.png'):
- put('browser/' + name + ext, (CAPTURE / 'browser' / (name + ext)).read_bytes())
- checks.append({'check': name + ': HTTP response bytes match captured SHA256', 'passed': True})
- put('browser/observations.json', (CAPTURE / 'browser/observations.json').read_bytes())
- for name in ('post-method-context', 'suning-label-image'):
- put('browser/' + name + '.png', (CAPTURE / 'browser-details' / (name + '.png')).read_bytes())
- all_images = json.loads((CAPTURE / 'images/index.json').read_text(encoding='utf-8'))
- selected = []
- for item in all_images:
- if not (item['id'].startswith('suning-') or item['id'] in ('post-04', 'post-10')):
- continue
- data = (CAPTURE / item['file']).read_bytes()
- assert sha(data) == item['sha256'] and item['status'] == 200
- put(item['file'], data)
- selected.append(item)
- assert len(selected) == 7
- put('images/index.json', json.dumps(selected, ensure_ascii=False, indent=2) + '\n')
- put('images/suning-contact-sheet.png', (CAPTURE / 'images/suning-contact-sheet.png').read_bytes())
- observations = json.loads((CAPTURE / 'browser/observations.json').read_text(encoding='utf-8'))
- retail = next(x for x in observations if x['id'] == 'suning-photo-117')
- post = next(x for x in observations if x['id'] == 'post-label-review')
- for phrase in ('浓浓炸酱面117g*5袋', '585克', '袋装'):
- assert phrase in retail['text']
- checks.append({'check': 'Browser retail body contains: ' + phrase, 'passed': True})
- for phrase in ('2023-12-18 21:20:15', '没事儿聊聊呗', '包装上写的煮面6-7分钟,实测写少了,煮了10分钟面才软。'):
- assert phrase in post['text']
- checks.append({'check': 'Browser post body contains: ' + phrase, 'passed': True})
- checks.append({'check': 'Seven selected original image files match HTTP capture hashes', 'passed': True})
- index = {
- 'createdAtUtc': datetime.now(timezone.utc).isoformat(), 'businessApiCalls': 0,
- 'businessWrites': 0, 'authenticated': False, 'physicalSampleVerified': False,
- 'purpose': 'Public-source workflow testing and desk assessment; not physical validation',
- 'sources': [{k: obs[k] for k in ('id', 'requestedUrl', 'url', 'title', 'startedAt', 'observedAt', 'documents')}
- for obs in observations],
- 'images': selected,
- 'manualImageReadings': [
- {'file': 'images/suning-05.jpg', 'region': 'lower-left label',
- 'visibleText': '净含量:面饼+配料117克 面饼:90克',
- 'scope': 'Visible product image label, not measured values'},
- {'file': 'images/suning-05.jpg', 'region': 'middle front', 'visibleText': '煮面6-7分钟'},
- {'file': 'images/post-10.jpg', 'region': 'upper back preparation strip',
- 'visibleText': ['先准备1L以上水煮沸,加入面条', '2分钟后挑散面条,4分钟放入菜包,煮面6-7分钟捞出'],
- 'scope': 'Historical image; complete barcode, batch and photograph date unknown'}
- ],
- 'checks': checks,
- 'excluded': ['Search snippets are discovery only', 'Unrelated comparison products in post photos 11-13',
- 'Unverified shopping, identity and health claims', 'Browser profiles and credentials'],
- }
- put('SOURCE-INDEX.json', json.dumps(index, ensure_ascii=False, indent=2) + '\n')
- put('README.txt', '真实公开资料测试包,2026-11-03\n先读42号结果与43号桌面评估。未包含执行命令或保存输入,不直接import/save。\n七张选定图片保留下载原字节,索引含URL/时间/SHA256;网页和历史包装不合并SKU。\nFILES.sha256为除自身外所有文件的校验清单;没有浏览器档案、凭据或无关商品照片。\n实际系统仍S3,未调用业务接口。\n')
- manifest = []
- for file in sorted(STAGE.rglob('*')):
- if file.is_file():
- manifest.append(f'{sha(file.read_bytes())} {file.relative_to(STAGE).as_posix()}')
- put('FILES.sha256', '\n'.join(manifest) + '\n')
- with zipfile.ZipFile(ZIP, 'x', compression=zipfile.ZIP_DEFLATED, compresslevel=7) as archive:
- for file in sorted(STAGE.rglob('*')):
- if file.is_file():
- archive.write(file, file.relative_to(STAGE).as_posix())
- with zipfile.ZipFile(ZIP) as archive:
- assert archive.testzip() is None
- for row in manifest:
- digest, name = row.split(' ', 1)
- assert sha(archive.read(name)) == digest
- receipt = {'zip': str(ZIP), 'bytes': ZIP.stat().st_size, 'sha256': sha(ZIP.read_bytes()),
- 'fileCount': len(manifest) + 1, 'manifestEntriesVerified': len(manifest),
- 'checksPassed': len(checks), 'businessApiCalls': 0, 'businessWrites': 0}
- with (ROOT / 'work/S4-public-materials-20261103-receipt.json').open('x', encoding='utf-8') as stream:
- json.dump(receipt, stream, ensure_ascii=False, indent=2)
- for name in ('42-网上测试资料核查与推进说明.md', '43-S4公开资料桌面评估草稿.md'):
- with (DOCS / name).open('xb') as stream:
- stream.write((STAGE / name).read_bytes())
- print(json.dumps(receipt, ensure_ascii=True, indent=2))
|