원클릭으로
tdd-workflow
Amazon 商品 HTML 提取器的 TDD 工作流。先写测试后写代码,9 步执行顺序(每步带 CHECKPOINT),Smoke Fixture 选取策略,测试代码风格模板。当 agent 需要按 TDD 流程开发提取器时调度此技能。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Amazon 商品 HTML 提取器的 TDD 工作流。先写测试后写代码,9 步执行顺序(每步带 CHECKPOINT),Smoke Fixture 选取策略,测试代码风格模板。当 agent 需要按 TDD 流程开发提取器时调度此技能。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
当需要分析 Amazon Bestsellers Top50 的 A+ 页面内容、视觉营销策略、品牌故事、产品对比表、图片素材质量时触发此 skill。 包括但不限于:A+ 模块结构分析、图片视觉风格对比、Comparison Table 分析、品牌叙事策略、A+ 文本内容提取等。 示例触发语:「分析 A+ 页面」「哪些产品的 A+ 做得好」「A+ 图片风格对比」「Comparison Table 怎么设计的」「A+ 文案分析」
当需要对 Amazon Bestsellers Top50 做逐商品更细分类分析时触发此 skill。 适用于平台类目过粗而业务需要细粒度标签的场景。该技能基于 PPD + Product Details + 商品图证据, 输出全量 Top50 的细分类明细表、分布统计和机会判断,且支持全类目通用。
当需要分析 Amazon Bestsellers Top50 的市场竞争格局、排名坑位结构、品牌集中度、价格带分布、卖家结构时触发此 skill。 包括但不限于:类目概览骨架判断、坑位门槛分析、品牌/卖家占坑、新卖家进入路径、市场体量代理判断、自营占比等。 示例触发语:「分析这个类目的市场竞争格局」「排名前10被谁占了」「新卖家有没有机会进入」「品牌集中度怎么样」「价格带分布如何」
当需要分析 Amazon Bestsellers Top50 的用户评论结构、购买动机、高频痛点、产品实用性判断时触发此 skill。 包括但不限于:评论层级分布、用户核心购买理由、差评痛点提炼、产品切入机会识别等。 示例触发语:「分析用户评论」「用户最在意什么」「差评集中在哪里」「产品体验痛点是什么」「新品应该改进什么」
Amazon 商品完整 HTML 页面的分块器。读取 ≈2MB 的完整详情页 HTML,按稳定 DOM id 切出 4 个子 HTML(ppd / customer_reviews / product_details / aplus)。当 agent 需要对完整 HTML 进行分块切分时调度此技能。
Amazon 商品子 HTML 块的结构化提取器(清洗器)。读取分块后的子 HTML,清洗 DOM 噪声,输出结构化 Markdown。包含 4 个子技能(ppd / customer-reviews / aplus / product-details),每个负责一种块类型。当 agent 需要从子 HTML 中提取结构化数据时调度此技能。
| name | tdd-workflow |
| description | Amazon 商品 HTML 提取器的 TDD 工作流。先写测试后写代码,9 步执行顺序(每步带 CHECKPOINT),Smoke Fixture 选取策略,测试代码风格模板。当 agent 需要按 TDD 流程开发提取器时调度此技能。 |
| type | skill |
<script>, <br>, 导航文本等)test_*_golden_comparison 用例(详见 skills/golden-fixture/SKILL.md)每个步骤必须 实际运行 pytest 并确认通过后才能进入下一步。
1. Smoke Fixture 选取:
a. 从 `products/{ASIN}/product.html` 中手动指定 3–4 个代表性 ASIN(优先 Top1/Top25)
b. 运行 `python -m chunker.batch_run --products-dir <products_dir> --rankings-jsonl <rankings_jsonl> --out-dir chunks/ --limit 4`
确认能正常分块生成 chunks
c. 这 3–4 个产品的 chunk 目录就是后续所有提取器测试的固定 fixture
2. DOM 探测:
读取上述 3–4 个样本 → 用 Python 脚本探测 DOM 结构(临时脚本,写到 tests/ 下,用完删除)
3. 分块器:
a. 写测试 tests/test_static_chunker.py
b. 实现 chunker/static_chunker.py
c. ✅ CHECKPOINT: `python -m pytest tests/test_static_chunker.py -v`
4. Reviews 提取器:
a. 复制 skills/amazon-extractor/skills/customer-reviews/customer_reviews_extract.py 到 chunker/customer_reviews_extract.py
b. 写测试 tests/test_customer_reviews_extract.py(结构断言 + Golden 比对)
c. ✅ CHECKPOINT: `python -m pytest tests/test_customer_reviews_extract.py -v`
d. 失败只微调模版,不得重写
5. Details 提取器:
a. 写测试 tests/test_product_details_extract.py(结构断言 + Golden 比对)
b. 实现 chunker/product_details_extract.py
c. ✅ CHECKPOINT: `python -m pytest tests/test_product_details_extract.py -v`
6. PPD 提取器:
a. 写测试 tests/test_ppd_extract.py(Core/Buybox/Twister/Overview/Bullets/Images + Golden 比对)
b. 逐子阶段实现
c. ✅ CHECKPOINT: `python -m pytest tests/test_ppd_extract.py -v`
7. A+ 提取器:
a. 写测试 tests/test_aplus_extract.py(Comparison/Assets/Story + 两个 Golden 比对)
b. 逐子阶段实现
c. ✅ CHECKPOINT: `python -m pytest tests/test_aplus_extract.py -v`
8. 编排脚本:
a. 写测试 tests/test_batch_run.py
b. 实现 chunker/batch_run.py
c. ✅ CHECKPOINT: `python -m pytest tests/test_batch_run.py -v`
9. ✅ FINAL GATE: `python -m pytest tests/ -v` 全部通过
# 文件位置:tests/test_ppd_extract.py
import tempfile
import unittest
from pathlib import Path
# Smoke Fixture:只针对 3–4 个样本,不面向全量数据
CHUNKS_DIR = Path(__file__).resolve().parents[1] / "chunks"
SMOKE_SAMPLE_COUNT = 4
SMOKE_DIRS = sorted(
[d for d in CHUNKS_DIR.iterdir() if d.is_dir() and (d / "ppd.html").exists()]
)[:SMOKE_SAMPLE_COUNT]
class TestPpdExtractor(unittest.TestCase):
def test_product_001_core_fields(self):
self.assertTrue(len(SMOKE_DIRS) >= 1, "No smoke fixture chunks found")
html_path = SMOKE_DIRS[0] / "ppd.html"
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "ppd.md"
result = extract_ppd_markdown(html_path, out)
content = result.read_text(encoding="utf-8")
self.assertIn("# PPD Extracted", content)
self.assertIn("## Core", content)
self.assertIn("- Title:", content)
self.assertIn("- Current price: $", content)
if "| Field | Value |" in content:
self.assertIn("| --- | --- |", content)
self.assertNotIn("<br", content.lower())
self.assertNotIn("<script", content.lower())
def test_product_002_also_works(self):
if len(SMOKE_DIRS) < 2:
self.skipTest("Only 1 smoke fixture available")
html_path = SMOKE_DIRS[1] / "ppd.html"
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "ppd.md"
result = extract_ppd_markdown(html_path, out)
content = result.read_text(encoding="utf-8")
self.assertIn("# PPD Extracted", content)
self.assertIn("## Core", content)
def test_golden_comparison(self):
"""Compare output against Golden fixture; retry 3x; escalate to human review if unstable."""
self.assertTrue(len(SMOKE_DIRS) >= 1, "No smoke fixture chunks found")
html_path = SMOKE_DIRS[0] / "ppd.html"
golden_path = TEMPLATES_DIR / "ppd_extracted.md"
if not html_path.exists():
self.skipTest("Reference sample chunk not found")
if not golden_path.exists():
self.skipTest("Golden fixture not found")
_run_with_golden_retry(self, extract_ppd_markdown, html_path, golden_path)
CHUNKS_DIR = Path(__file__).resolve().parents[1] / "chunks"sorted([d for d in CHUNKS_DIR.iterdir() if d.is_dir() and (d / "<block>.html").exists()])[:SMOKE_SAMPLE_COUNT]