Skip to content
Back to skills

Listing Diff Meter

ASecurity

评估生成的 listing 跟对标 ASIN 的差异度,返回标题/五点/描述三段的量化分数

  • 417 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 8, 2026
ai-agentspython

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned November 8, 2026

npx -y skills add infometa/workbuddyskills --skill listing-diff-meter --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Listing Diff Meter?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Listing Diff Meter
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/infometa-listing-diff-meter/badge)](https://www.skillsdirectory.com/skills/infometa-listing-diff-meter)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: listing-diff-meter
description: 评估生成的 listing 跟对标 ASIN 的差异度,返回标题/五点/描述三段的量化分数
  + 不达标字段定位 + 重写建议。是防侵权的核心质量门——铺货卖家被判抄袭最直接的指标。
  Use when pipeline finishes L4 writing. Output gate decides whether to ship or recycle.
  NOT for cross-batch comparison (use listing-batch-novelty-report instead).
---

# Listing · Diff Meter

## Core Concepts

L5 质量门的核心 Skill,也是 listing 工具最重要的差异化能力之一。给铺货卖家提供
"复刻安全度"的量化指标,让他们敢按下"开始批量"的按钮。算法上是字符 n-gram + 关键词 overlap +
句式 embedding 的综合分;产品上是直接对应到亚马逊"判定为重复 listing 的内部阈值"的代理指标。

## Invoke Schema

```yaml
inputs:
  - name: batch_id
    type: string
    required: true
  - name: row_index
    type: int
    required: true
  - name: fields_to_check
    type: array<enum>
    required: false
    default: [title, bullets, description]
    values: [title, bullets, description, search_terms]
  - name: comparison_targets
    type: array<string>
    required: false
    description: "对标 ASIN 列表,省略=只跟当前 row 的 target_detail 比对"
  - name: metric
    type: enum
    required: false
    default: difference
    values: [difference, similarity]
    description: "阈值语义。difference=差异度下限(现有行为,score≥threshold 通过);
      similarity=相似度/重复率上限(用户说『重复率≤30%』时用,等价 diff_threshold=0.70,
      由调用方或本 skill 内部换算,报告中按用户语义回显)"
  - name: per_field_thresholds
    type: object
    required: false
    description: "字段级阈值覆盖,如 {title: 0.30, bullets: 0.30, description: 0.30}
      (metric=similarity 语义)。省略的字段回退 overall 阈值。来源:用户 spec
      (plan_source=user_flow 时由用户流程直接给出)"

outputs:
  - name: diff_report
    type: DiffReport
  - name: pass
    type: boolean
    description: "overall_score >= rule.hard.diff_threshold"

events:
  - name: diff_failed
    payload: { row_index, overall_score, threshold, weakest_field }

side_effects:
  - write_diff_score_to_asin_context
  - log_qa_attempt
```

## Dependencies

- 当前 harness 的本地 Python 执行能力:跑相似度计算(n-gram + embedding cosine)
- 内部 embedding 服务

## Execution Playbook

```
1. 加载
   ctx = runtime.state_reader.load(batch_id, row_index)
   draft = ctx.draft
   targets = [ctx.target_detail] if not comparison_targets else load_targets(comparison_targets)
   # 阈值解析:用户 spec > rule_snapshot。metric=similarity 时先换算 diff = 1 - sim
   thresholds = resolve_thresholds(
       per_field=inputs.per_field_thresholds,   # 用户字段级阈值(可缺)
       metric=inputs.metric or 'difference',
       fallback=ctx.rule_snapshot.hard.diff_threshold,
   )
   # 用户流程给出的阈值不受 rule-resolver [0.5, 0.95] 区间限制,
   # 仅要求换算后 diff 阈值落在 (0, 1);换算与来源写入 diff_report.threshold_meta

2. 缓存检查
   cache_key = f"diff:{draft_hash}:{target_hashes}"
   if cached := runtime.cache.get(cache_key):
       return cached

3. 文本归一化
   def normalize(text):
       text = text.lower()
       text = remove_punctuation(text)
       text = stem_words(text)        # running → run
       text = remove_stopwords(text)
       return text
   
   for field in fields_to_check:
       draft_norm[field] = normalize(draft[field])
       target_norm[field] = normalize(targets[field])  # 多对标时拼接

4. 三种相似度计算

   4.1 字符 n-gram Jaccard(5-gram)
       def ngram_jaccard(a, b, n=5):
           grams_a = set(get_ngrams(a, n))
           grams_b = set(get_ngrams(b, n))
           return len(grams_a & grams_b) / len(grams_a | grams_b) if grams_a | grams_b else 0

   4.2 关键词 overlap 比率
       def keyword_overlap(a, b):
           kws_a = extract_top_keywords(a, top=20)
           kws_b = extract_top_keywords(b, top=20)
           return len(set(kws_a) & set(kws_b)) / len(set(kws_a) | set(kws_b))

   4.3 句式 embedding 相似度(cosine)
       def sentence_similarity(a, b):
           emb_a = embed(a)
           emb_b = embed(b)
           return cosine(emb_a, emb_b)

5. 综合分数(每个字段独立算)
   for field in fields_to_check:
       n_score = ngram_jaccard(draft_norm[field], target_norm[field])
       k_score = keyword_overlap(draft_norm[field], target_norm[field])
       s_score = sentence_similarity(draft[field], target[field])  # 不归一化以保留语义
       
       similarity = 0.4 * n_score + 0.4 * k_score + 0.2 * s_score
       diff_score = 1 - similarity
       
       diff_report[field] = {
           score: diff_score,
           pass: diff_score >= thresholds[field],   # 字段级阈值;未指定的字段用 overall 阈值
           breakdown: { ngram: 1-n_score, keyword: 1-k_score, sentence: 1-s_score },
       }

6. 五点的特殊处理(per-bullet diff)
   for i, bullet in enumerate(draft.bullets):
       # 跟对标每条 bullet 比,取最高相似度(最危险的对比)
       max_sim = max(
           combined_similarity(bullet, target_bullet)
           for target_bullet in target.bulletPoints
       )
       per_bullet[i] = { score: 1 - max_sim, pass: ... }
   
   bullets_overall = min([b.score for b in per_bullet])  # 木桶原理:最低的算总分

7. 标记冲突片段
   for field in fields_to_check:
       conflict_segments = find_consecutive_overlaps(
           draft[field], target[field],
           min_length=6  # 连续 ≥ 6 字符与对标重叠的片段
       )
       diff_report[field].conflict_segments = conflict_segments

8. 综合 pass 判定
   overall_score = weighted_avg({
       title: 0.4,
       bullets: 0.4,
       description: 0.2,
   })
   overall_pass = overall_score >= thresholds.overall and all(f.pass for f in diff_report.fields)

9. 生成 recycle_advice(如果 fail)
   if not overall_pass:
       weakest = min(fields, by=score)
       advice = generate_advice(weakest, conflict_segments[weakest])
       # 例: "标题中 'premium HEPA 13 filter' (字符 12-32) 跟对标完全一致,
       #      建议换成 'medical-grade HEPA filtration' 或 '13-grade purification core'"

10. 写入 + 记录
    ctx.diff_score = diff_report
    ctx.qa_log.append({
        attempt: ctx.draft_version,
        stage: 'diff_meter',
        pass: overall_pass,
        details: diff_report,
        timestamp: now()
    })
    runtime.state_writer.save(ctx, emit_phase='diff_measured')
    runtime.cache.set(cache_key, diff_report, ttl=infinity)
    
    if not overall_pass:
        emit diff_failed
```

## 输出 schema 示例

```json
{
  "title": {
    "score": 0.62,
    "pass": false,
    "breakdown": { "ngram": 0.55, "keyword": 0.68, "sentence": 0.72 },
    "conflict_segments": [
      {
        "text": "premium HEPA 13 filter",
        "draft_position": [12, 34],
        "target_position": [8, 30],
        "similarity": 1.0
      }
    ]
  },
  "bullets": {
    "score": 0.71,
    "pass": true,
    "per_bullet": [
      { "idx": 0, "score": 0.68, "pass": false, "conflicts": [...] },
      { "idx": 1, "score": 0.82, "pass": true, "conflicts": [] },
      { "idx": 2, "score": 0.75, "pass": true, "conflicts": [] },
      { "idx": 3, "score": 0.79, "pass": true, "conflicts": [] },
      { "idx": 4, "score": 0.69, "pass": false, "conflicts": [...] }
    ]
  },
  "description": {
    "score": 0.78,
    "pass": true,
    "breakdown": { "ngram": 0.82, "keyword": 0.71, "sentence": 0.81 }
  },
  "overall_score": 0.69,
  "pass": false,
  "weakest_field": "title",
  "recycle_advice": "标题里 'premium HEPA 13 filter'(字符 12-34)跟对标 1:1 重叠,建议换成 'medical-grade HEPA filtration' 或 '13-grade purification core'。五点第 1、5 条也有局部 6+ 字符重叠,需要打散改写。"
}
```

## recycle_advice 生成规则

为下次重写的 writer 提供"可执行"的具体反馈:

```python
def generate_advice(weakest_field, conflicts):
    if weakest_field == 'title':
        worst_segment = max(conflicts, key=lambda c: c.similarity)
        return f"标题中 '{worst_segment.text}' 跟对标 1:1 重叠,建议替换为同义表达"
    
    elif weakest_field == 'bullets':
        failed_indices = [b.idx for b in per_bullet if not b.pass]
        return f"第 {failed_indices} 条五点跟对标相似度过高,按 {failed_themes} 主题重写"
    
    elif weakest_field == 'description':
        return f"描述段落 {paragraph_indices} 跟对标雷同,建议换用 {alt_style} 风格重写"
```

writer 收到这个建议作为 `retry_after_diff_fail` 模式的 input。

## Failure Modes

| 错误 | 触发 | 处理 |
|---|---|---|
| target_detail_missing | ctx.target_detail 为空 | skip diff 计算,emit diff_skipped_warning,pass=true 兜底 |
| embedding_service_down | 句式相似度计算失败 | 降级到仅 ngram + keyword(权重重分配为 0.6 + 0.4) |
| draft_empty | draft 某个字段为空 | 该字段 score=0, pass=false |
| sandbox_timeout | 沙箱超时 | retry 1 次,仍失败则该字段 fail |

## Cache Strategy

```
key: diff:{draft_hash}:{target_hashes_sorted}
value: 完整 DiffReport
ttl: infinity
```

draft_hash 和 target_hashes 都进 key,任一改变就失效。

## Not Applicable

- 跨 batch 整体新颖度评估(用 `listing-batch-novelty-report`)
- 实时编辑期间的 diff 预览(独立 Skill,本 Skill 是一次性评分)
- 检测 listing 是否"足够像对标"(反向需求,本 Skill 是检测"够不够不像")

## 设计权衡

**为什么综合分数权重是 0.4/0.4/0.2 而不是均分**:n-gram 和 keyword 是 Amazon 算法层面
重要的两个信号(直接影响"是否判定为重复 listing"),sentence embedding 主要用于
"看起来雷同但措辞不同"的微妙场景,权重低。

**为什么五点用 min(木桶原理)而不是 avg**:5 条里有 1 条跟对标 1:1 重叠就足够构成
侵权指控,平均掉这条变成"整体看着 OK"是危险的虚假安全感。

**为什么默认 threshold 是 0.7 而不是 0.8**:0.7 是亚马逊算法判定为"明显重复"和"还能争辩"
的经验分界。0.8 太保守,导致 writer 无法借鉴对标的高效结构。0.7 是产业实践的标准值。

**为什么 recycle_advice 要生成可执行建议**:单纯告诉 writer "diff 太低重写吧"等于没说,
writer 不知道改哪里。具体到"哪个片段 1:1 重叠 + 建议替换为什么",writer retry 的成功率
从 30% 提升到 80%+。

## 产物与落盘

本 skill 不依赖专用保存器。调用方可直接消费上方 schema,或在自己的工作目录中保存为
UTF-8 JSON;不得要求 LinkFox 会话目录、HTML 渲染器或上传服务。

Core batch 如显式启用跨商品反聚类,本 Skill 只能在所有成功行完成各自 Core finalize 后作为批次级附加检查;不得替代每行 QA,也不得阻断 `finalize_batch.py` 对 Core manifest 的完成校验。结果必须落盘并传递真实绝对路径。

Files in this skill

  • SKILL.md11.1 KB
  • _meta.json80 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.