Ver Fonte

feat(tihao): 资料驱动升级 v1.0.19(规则系统 + 案例系统 + schema 字段沉淀)

- 规则系统:platform-quality-rules / goal-strategy / compliance-rules / brief-rule-patterns 接入 ranker 与 brief-parser
- 案例系统:case-template-import 零依赖 xlsx 导入器 + few-shot-cases 运行时历史样本匹配
- 数据库字段沉淀:pgy-schema / media-library-schema(供未来建库)
- API 目录:voc-e-commerce-endpoints 清单 + api-catalog + ecommerce/social gateway
- 提号口径统一 1.5 倍候选池;达人 IP 正则收窄;few-shot 案例库打包闭环(docs/case-library)

Co-Authored-By: Claude <noreply@anthropic.com>
gangvy há 1 mês atrás
pai
commit
a301ac367a
74 ficheiros alterados com 6625 adições e 116 exclusões
  1. 10 0
      .gitignore
  2. 1 1
      claude-code-tihao-sourcing/.claude-plugin/plugin.json
  3. 2 2
      claude-code-tihao-sourcing/README.md
  4. 4 4
      claude-code-tihao-sourcing/docs/acceptance-checklist.md
  5. 473 0
      claude-code-tihao-sourcing/docs/brief-rule-patterns.json
  6. 49 0
      claude-code-tihao-sourcing/docs/case-library/fewshot-cases.json
  7. 5 5
      claude-code-tihao-sourcing/docs/live-provider-integration-runbook.md
  8. 13 0
      claude-code-tihao-sourcing/docs/media-library-collection-ddl.txt
  9. 304 0
      claude-code-tihao-sourcing/docs/media-library-field-schema.json
  10. 39 0
      claude-code-tihao-sourcing/docs/media-library-query-map.md
  11. 1 1
      claude-code-tihao-sourcing/docs/payment-package-links.md
  12. 15 0
      claude-code-tihao-sourcing/docs/pgy-collection-ddl.txt
  13. 743 0
      claude-code-tihao-sourcing/docs/pgy-field-schema.json
  14. 36 0
      claude-code-tihao-sourcing/docs/pgy-sourcing-query-map.md
  15. 2 2
      claude-code-tihao-sourcing/docs/reference-evidence-roadmap.md
  16. 5 5
      claude-code-tihao-sourcing/docs/release-notes.md
  17. 4 4
      claude-code-tihao-sourcing/docs/skill-database-record.json
  18. 7 7
      claude-code-tihao-sourcing/docs/tihao-experience-implementation-log.md
  19. 1 1
      claude-code-tihao-sourcing/docs/tihao-experience-optimization-plan.md
  20. 1 1
      claude-code-tihao-sourcing/docs/tihao-experience-optimization-plan.zh-CN.md
  21. 2 2
      claude-code-tihao-sourcing/docs/tihao-handoff-index.md
  22. 78 0
      claude-code-tihao-sourcing/docs/tihao-material-driven-upgrade.md
  23. 22 0
      claude-code-tihao-sourcing/mcp/catalog/params/douyin.json
  24. 16 0
      claude-code-tihao-sourcing/mcp/catalog/params/xiaohongshu.json
  25. 1452 0
      claude-code-tihao-sourcing/mcp/catalog/voc-e-commerce-endpoints.json
  26. 121 0
      claude-code-tihao-sourcing/mcp/src/core/api-catalog.js
  27. 111 16
      claude-code-tihao-sourcing/mcp/src/core/credentials.js
  28. 68 0
      claude-code-tihao-sourcing/mcp/src/core/files.js
  29. 44 4
      claude-code-tihao-sourcing/mcp/src/core/payment-links.js
  30. 17 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/brief-parser.js
  31. 212 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/brief-rule-patterns.js
  32. 100 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/compliance-rules.js
  33. 8 1
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/experience-rules.js
  34. 78 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/few-shot-cases.js
  35. 104 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/goal-strategy.js
  36. 41 13
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/live-provider.js
  37. 137 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/media-library-schema.js
  38. 250 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/pgy-schema.js
  39. 153 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/platform-quality-rules.js
  40. 3 3
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/progress-reporter.js
  41. 9 4
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/ranker.js
  42. 1 1
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/reference-enrichment-provider.js
  43. 112 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/report.js
  44. 2 0
      claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/sourcing-workflow.js
  45. 172 0
      claude-code-tihao-sourcing/mcp/src/providers/ecommerce-gateway.js
  46. 51 0
      claude-code-tihao-sourcing/mcp/src/providers/social-gateway.js
  47. 102 3
      claude-code-tihao-sourcing/mcp/src/server.js
  48. 329 0
      claude-code-tihao-sourcing/mcp/src/tools/tihao-api-catalog-run.js
  49. 2 2
      claude-code-tihao-sourcing/package-lock.json
  50. 13 2
      claude-code-tihao-sourcing/package.json
  51. 48 0
      claude-code-tihao-sourcing/scripts/brief-rule-patterns-smoke.js
  52. 77 0
      claude-code-tihao-sourcing/scripts/case-template-import-smoke.js
  53. 337 0
      claude-code-tihao-sourcing/scripts/case-template-import.js
  54. 1 1
      claude-code-tihao-sourcing/scripts/create-video-intake-pack.js
  55. 36 0
      claude-code-tihao-sourcing/scripts/few-shot-cases-smoke.js
  56. 50 0
      claude-code-tihao-sourcing/scripts/generate-brief-rule-patterns.js
  57. 65 0
      claude-code-tihao-sourcing/scripts/generate-media-library-schema.js
  58. 62 0
      claude-code-tihao-sourcing/scripts/generate-pgy-schema.js
  59. 55 0
      claude-code-tihao-sourcing/scripts/goal-compliance-rules-smoke.js
  60. 1 1
      claude-code-tihao-sourcing/scripts/homepage-evidence-readiness-audit.js
  61. 2 2
      claude-code-tihao-sourcing/scripts/intake-readiness-smoke.js
  62. 4 4
      claude-code-tihao-sourcing/scripts/live-preflight.js
  63. 2 0
      claude-code-tihao-sourcing/scripts/live-provider-normalization-smoke.js
  64. 3 3
      claude-code-tihao-sourcing/scripts/local-seed-material-index.js
  65. 1 1
      claude-code-tihao-sourcing/scripts/local-seed-to-intake-worklist.js
  66. 66 0
      claude-code-tihao-sourcing/scripts/media-library-schema-smoke.js
  67. 56 0
      claude-code-tihao-sourcing/scripts/pgy-schema-smoke.js
  68. 49 0
      claude-code-tihao-sourcing/scripts/platform-quality-rules-smoke.js
  69. 9 2
      claude-code-tihao-sourcing/scripts/product-acceptance-audit.js
  70. 5 5
      claude-code-tihao-sourcing/scripts/provider-contract-audit.js
  71. 4 2
      claude-code-tihao-sourcing/scripts/smoke-package.js
  72. 3 3
      claude-code-tihao-sourcing/skill-package-manifest.json
  73. 97 8
      claude-code-tihao-sourcing/skills/tihao/SKILL.md
  74. 67 0
      claude-code-tihao-sourcing/skills/tihao/references/error-codes.md

+ 10 - 0
.gitignore

@@ -58,3 +58,13 @@ Thumbs.db
 .claude/
 .claude/
 .mcp.json
 .mcp.json
 _relcheck.ps1
 _relcheck.ps1
+
+# 原始喂养资料(大文件 + 业务敏感,不进 VCS)
+/docs/20260813 - AI提号规则及喂养资料/
+
+# 本地临时调试目录
+/_devin_tmp/
+
+# 临时发布脚本(含本机 token,不进 VCS)
+/_pub_*.bat
+/_pub_*.ps1

+ 1 - 1
claude-code-tihao-sourcing/.claude-plugin/plugin.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "tihao",
   "name": "tihao",
   "description": "Tihao AI skills for Claude Code: upload/read client briefs, generate business-ready blogger lists, analyze images through Fmode Doubao vision, handle token/recharge states, and keep sourcing preference memory.",
   "description": "Tihao AI skills for Claude Code: upload/read client briefs, generate business-ready blogger lists, analyze images through Fmode Doubao vision, handle token/recharge states, and keep sourcing preference memory.",
-  "version": "1.0.9",
+  "version": "1.0.19",
   "author": {
   "author": {
     "name": "nkkj-BrainHack"
     "name": "nkkj-BrainHack"
   }
   }

+ 2 - 2
claude-code-tihao-sourcing/README.md

@@ -55,13 +55,13 @@ docs/tihao-handoff-index.md
 
 
 - 第一版报告默认走 `live` 高质量首版:`collectionMode=live` + `resultFirstMode=true`,扩大参考感知召回、批量分析候选并按证据重排,直接产出商务可发客户的强名单。
 - 第一版报告默认走 `live` 高质量首版:`collectionMode=live` + `resultFirstMode=true`,扩大参考感知召回、批量分析候选并按证据重排,直接产出商务可发客户的强名单。
 - 普通“读取 brief 出博主名单/提号/找博主/选号/可直接发客户的名单”都按上面的 live 高质量首版处理;只有用户明确要小规模/省额度时才缩到 `keywordLimit=1`。
 - 普通“读取 brief 出博主名单/提号/找博主/选号/可直接发客户的名单”都按上面的 live 高质量首版处理;只有用户明确要小规模/省额度时才缩到 `keywordLimit=1`。
-- `live` 模式调用公司 `voc-e-commerce` 代理接口,使用 JustOne-backed 数据;缺 token 时返回友好的开通/充值提示并主动要 sessionToken,而不是退回 sample。
+- `live` 模式调用公司 `voc-e-commerce` 代理接口,使用真实电商数据;缺 token 时返回友好的开通/充值提示并主动要 sessionToken,而不是退回 sample。
 - `sample` 模式仅在用户明确要求演示、无消耗、不用真实数据或“先跑通 SOP”时使用,无 token 跑通 brief -> 博主名单流程。
 - `sample` 模式仅在用户明确要求演示、无消耗、不用真实数据或“先跑通 SOP”时使用,无 token 跑通 brief -> 博主名单流程。
 - 输出 Markdown、JSON、CSV 三份结果。
 - 输出 Markdown、JSON、CSV 三份结果。
 - 无 token、401、403、额度不足时返回友好的开通/充值提示,不暴露原始错误或 token。
 - 无 token、401、403、额度不足时返回友好的开通/充值提示,不暴露原始错误或 token。
 - 支持偏好记忆,把客户反馈沉淀到下次选号。
 - 支持偏好记忆,把客户反馈沉淀到下次选号。
 - 支持 Fmode 图片分析:用平台用户 token 调用 `doubao-seed-2-0-pro-260215`,识别截图、产品图、海报、UI 图和图片文字。
 - 支持 Fmode 图片分析:用平台用户 token 调用 `doubao-seed-2-0-pro-260215`,识别截图、产品图、海报、UI 图和图片文字。
-- 支持参考博主链接、TikHub-compatible 基线补证接口和 ffmpeg/ASR/Vision-ready 证据卡接口;真实 provider 通过 `docs/live-provider-integration-runbook.md` 单独验收。
+- 支持参考博主链接、参考账号补证接口和 ffmpeg/ASR/Vision-ready 证据卡接口;真实 provider 通过 `docs/live-provider-integration-runbook.md` 单独验收。
 - 已沉淀商务提号经验优化计划:硬指标、隐性规则、参考账号、主页最近 10/20 篇内容证据、平台相似推荐和客户选中率验收。
 - 已沉淀商务提号经验优化计划:硬指标、隐性规则、参考账号、主页最近 10/20 篇内容证据、平台相似推荐和客户选中率验收。
 - 支持 overnight 策略矩阵:`baseline-live`、`reference-account`、`homepage-evidence`、`video-enhanced`、`result-first`、`result-first-risk`、`result-first-broad`。
 - 支持 overnight 策略矩阵:`baseline-live`、`reference-account`、`homepage-evidence`、`video-enhanced`、`result-first`、`result-first-risk`、`result-first-broad`。
 - 支持人工复核指标统计:商务可用率、负样本率、负样本归因覆盖率、客户选中率、重复键和每个 brief 内排名连续性。
 - 支持人工复核指标统计:商务可用率、负样本率、负样本归因覆盖率、客户选中率、重复键和每个 brief 内排名连续性。

+ 4 - 4
claude-code-tihao-sourcing/docs/acceptance-checklist.md

@@ -48,13 +48,13 @@ npm run acceptance:pm
 npm run acceptance:providers:mock
 npm run acceptance:providers:mock
 ```
 ```
 
 
-它用于验证 TikHub 兼容参考账号补证合同、多模态证据卡合同、豆包视频分析 OpenAI 兼容合同。
+它用于验证参考账号补证合同、多模态证据卡合同、豆包视频分析 OpenAI 兼容合同。
 
 
 有真实 provider 权限时再执行:
 有真实 provider 权限时再执行:
 
 
 ```powershell
 ```powershell
-$env:TIKHUB_BASE_URL="<reference enrichment provider>"
-$env:TIKHUB_TOKEN="<optional tikhub token>"
+$env:SOCIAL_ANALYSIS_BASE_URL="<reference enrichment provider>"
+$env:VOC_SOCIAL_TOKEN="<optional reference provider token>"
 $env:TIHAO_EVIDENCE_BASE_URL="<multimodal evidence provider>"
 $env:TIHAO_EVIDENCE_BASE_URL="<multimodal evidence provider>"
 $env:TIHAO_EVIDENCE_TOKEN="<optional evidence provider token>"
 $env:TIHAO_EVIDENCE_TOKEN="<optional evidence provider token>"
 npm run acceptance:providers
 npm run acceptance:providers
@@ -164,7 +164,7 @@ production live ready:
 
 
 multimodal live ready:
 multimodal live ready:
 
 
-- `npm run acceptance:providers` 能连真实 TikHub/证据 provider;
+- `npm run acceptance:providers` 能连真实参考账号补证/证据 provider;
 - 至少一张真实 ffmpeg/ASR/Vision 证据卡经过人工复核。
 - 至少一张真实 ffmpeg/ASR/Vision 证据卡经过人工复核。
 
 
 reference-video live ready:
 reference-video live ready:

+ 473 - 0
claude-code-tihao-sourcing/docs/brief-rule-patterns.json

@@ -0,0 +1,473 @@
+{
+  "generatedAt": "2026-08-14T02:35:05.065Z",
+  "caseCount": 13,
+  "casePatterns": [
+    {
+      "id": "case-1-nest-space",
+      "caseName": "Nest空间探店",
+      "platform": "xiaohongshu",
+      "category": "本地生活/家居探店",
+      "accountTypes": [
+        "精致生活",
+        "家居探店"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [
+        "同省份KOL,不要跨区域(6城市探店)"
+      ],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "人均预算",
+          "min": 6000,
+          "max": 10000,
+          "note": "报备视频探店,图文价格也要给"
+        }
+      ],
+      "notes": [
+        "2位/城市",
+        "五一假期档期"
+      ]
+    },
+    {
+      "id": "case-2-dha",
+      "caseName": "DHA",
+      "platform": "xiaohongshu",
+      "category": "母婴",
+      "accountTypes": [
+        "干货科普(营养师/育婴师妈妈)",
+        "618合集(6个月-7岁孩子妈妈)"
+      ],
+      "dataRequirements": [
+        "CPE≤3",
+        "阅读中位数≥4000",
+        "预估阅读单价≤1.0"
+      ],
+      "audienceConstraints": [
+        "女性为主,25-44岁占比70%以上",
+        "兴趣前三标签含2个(母婴/萌娃/记录生活)"
+      ],
+      "regionConstraints": [
+        "地域广东山东河南占比多为正常"
+      ],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 1000,
+          "max": 5000
+        }
+      ],
+      "notes": []
+    },
+    {
+      "id": "case-3-music",
+      "caseName": "音乐类达人",
+      "platform": "douyin",
+      "category": "音乐",
+      "accountTypes": [
+        "音乐达人"
+      ],
+      "dataRequirements": [
+        "近3条视频平均播放:头部>100w、中腰部>30w、尾部>5w"
+      ],
+      "audienceConstraints": [
+        "年龄18-23岁为主(可接受18-30岁)",
+        "男女比例1:1或女性略高"
+      ],
+      "regionConstraints": [],
+      "tierRules": [
+        {
+          "tier": "头部",
+          "fans": "200w+",
+          "plays": ">100w",
+          "budgetMax": 50000
+        },
+        {
+          "tier": "中腰部",
+          "fans": "100w+",
+          "plays": ">30w",
+          "budgetMax": 8000
+        },
+        {
+          "tier": "尾部",
+          "fans": "30w+",
+          "plays": ">5w",
+          "budgetMax": 4000
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "头部≤50000",
+          "min": 0,
+          "max": 50000
+        },
+        {
+          "scope": "中腰部≤8000",
+          "min": 0,
+          "max": 8000
+        },
+        {
+          "scope": "尾部≤4000",
+          "min": 0,
+          "max": 4000
+        }
+      ],
+      "notes": []
+    },
+    {
+      "id": "case-4-dji",
+      "caseName": "大疆",
+      "platform": "douyin",
+      "category": "追星号",
+      "accountTypes": [
+        "追星账号",
+        "垂类粉丝号(张凌赫)"
+      ],
+      "dataRequirements": [
+        "CPV:抖音0.072、小红书0.144"
+      ],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [
+        {
+          "tier": "追星号",
+          "fans": "千粉以上"
+        },
+        {
+          "tier": "垂类粉丝号",
+          "fans": "百粉-千粉不限"
+        }
+      ],
+      "budgetLayers": [],
+      "notes": [
+        "提供素材二创二剪",
+        "提报时间紧迫"
+      ]
+    },
+    {
+      "id": "case-5-carlsberg",
+      "caseName": "嘉士伯西夏啤酒",
+      "platform": "xiaohongshu",
+      "category": "啤酒",
+      "accountTypes": [
+        "啤酒分享",
+        "好物种草"
+      ],
+      "dataRequirements": [
+        "主页点赞100左右"
+      ],
+      "audienceConstraints": [],
+      "regionConstraints": [
+        "达人IP必须为宁夏"
+      ],
+      "tierRules": [
+        {
+          "tier": "KOC",
+          "budget": 800,
+          "note": "不含平台费"
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "KOC",
+          "min": 0,
+          "max": 800
+        }
+      ],
+      "notes": [
+        "需要20个",
+        "档期5.30-6.30"
+      ]
+    },
+    {
+      "id": "case-6-zelimei",
+      "caseName": "泽立美医药",
+      "platform": "douyin",
+      "category": "医药/湿疹",
+      "accountTypes": [
+        "医生(皮肤科/儿科中腰部+)",
+        "科普达人(宝妈/健康)",
+        "宝妈/科普/生活(小红书)"
+      ],
+      "dataRequirements": [
+        "医生板块CPM≤90、CPE≤5",
+        "非医生科普CPM≤150、CPE≤5",
+        "小红书CPM<100、CPC<0.8、CPE<15",
+        "需自报爆款率、完播率、收藏率"
+      ],
+      "audienceConstraints": [
+        "2岁+特应性皮炎患儿宝妈、成人湿疹患者"
+      ],
+      "regionConstraints": [],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "医生板块15万/月"
+        },
+        {
+          "scope": "非医生科普5万/月"
+        },
+        {
+          "scope": "小红书3万/月"
+        }
+      ],
+      "notes": []
+    },
+    {
+      "id": "case-7-olebao",
+      "caseName": "山东蓬莱欧乐堡",
+      "platform": "douyin",
+      "category": "亲子游",
+      "accountTypes": [
+        "亲子生活"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [
+        "IP要求山东/江苏/河南/河北/浙江/山西/辽宁/北京/天津/大连"
+      ],
+      "tierRules": [
+        {
+          "tier": "粉丝20w+"
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 0,
+          "max": 35000
+        }
+      ],
+      "notes": [
+        "性价比高一些"
+      ]
+    },
+    {
+      "id": "case-8-qika",
+      "caseName": "奇卡",
+      "platform": "xiaohongshu",
+      "category": "粤语/港剧/拆卡",
+      "accountTypes": [
+        "拆卡类",
+        "粤语vlog类",
+        "港剧解说类",
+        "TVB解说类",
+        "本地旅游/跨界类",
+        "粤语教学类"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 3000,
+          "max": 5000
+        }
+      ],
+      "notes": [
+        "小红书/抖音分账号类型给数量"
+      ]
+    },
+    {
+      "id": "case-9-keda-charging",
+      "caseName": "科大智能充电桩",
+      "platform": "xiaohongshu",
+      "category": "汽车/新能源",
+      "accountTypes": [
+        "汽车",
+        "新能源"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [
+        {
+          "tier": "千粉以上"
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 0,
+          "max": 3000
+        }
+      ],
+      "notes": []
+    },
+    {
+      "id": "case-10-washing-fridge",
+      "caseName": "洗烘套装/冰箱",
+      "platform": "xiaohongshu",
+      "category": "家电",
+      "accountTypes": [
+        "情侣+养宠双重人设达人",
+        "健身/减肥/减脂(饮食管理方向)"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 10000,
+          "max": 30000
+        }
+      ],
+      "notes": [
+        "洗烘套装5位(情侣+养宠双人设)",
+        "冰箱8系5位(饮食管理,不要纯健身秀身材)"
+      ]
+    },
+    {
+      "id": "case-11-tuborg",
+      "caseName": "乐堡啤酒",
+      "platform": "xiaohongshu",
+      "category": "啤酒",
+      "accountTypes": [
+        "MBTI垂类号(KOL)",
+        "职场/生活/普通个人(KOC)"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [
+        {
+          "tier": "KOL",
+          "budgetMax": 8000
+        },
+        {
+          "tier": "KOC",
+          "budgetMax": 1000
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "KOL",
+          "min": 0,
+          "max": 8000
+        },
+        {
+          "scope": "KOC",
+          "min": 0,
+          "max": 1000
+        }
+      ],
+      "notes": [
+        "近3个月不能有嘉士伯相关合作(竞品排他)",
+        "档期5.13-5.17"
+      ]
+    },
+    {
+      "id": "case-12-gree",
+      "caseName": "格力哈工程",
+      "platform": "xiaohongshu",
+      "category": "家电/空调",
+      "accountTypes": [
+        "生活类(哈工程学生身份)",
+        "新闻资讯号",
+        "营销号"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [],
+      "regionConstraints": [],
+      "tierRules": [],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 1000,
+          "max": 6000
+        }
+      ],
+      "notes": [
+        "提报数量20个",
+        "弱化商业营销痕迹"
+      ]
+    },
+    {
+      "id": "case-13-elephant",
+      "caseName": "大象胶原蛋白",
+      "platform": "xiaohongshu",
+      "category": "胶原蛋白/护肤",
+      "accountTypes": [
+        "测评类",
+        "好物种草",
+        "情侣"
+      ],
+      "dataRequirements": [],
+      "audienceConstraints": [
+        "粉丝中18岁以下占比不超过10%"
+      ],
+      "regionConstraints": [],
+      "tierRules": [
+        {
+          "tier": "10W以下"
+        }
+      ],
+      "budgetLayers": [
+        {
+          "scope": "单个预算",
+          "min": 0,
+          "max": 4000
+        }
+      ],
+      "notes": [
+        "测评对标(大象胶原蛋白 vs 修丽可胶原蛋白精华)"
+      ]
+    }
+  ],
+  "patternLibrary": [
+    {
+      "key": "cpc",
+      "label": "CPC",
+      "note": "单次点击成本",
+      "regex": "CPC\\s*[≤<=<]\\s*(\\d+(?:\\.\\d+)?)"
+    },
+    {
+      "key": "cpv",
+      "label": "CPV",
+      "note": "单次播放成本",
+      "regex": "CPV\\s*[≤<=<::]\\s*(\\d+(?:\\.\\d+)?)"
+    },
+    {
+      "key": "recentPlays",
+      "label": "近3条平均播放",
+      "note": "近期内容爆款线",
+      "regex": "近\\s*3\\s*条[^\\n]{0,10}(?:平均)?播放\\s*[>>≥]\\s*(\\d+(?:\\.\\d+)?)\\s*万"
+    },
+    {
+      "key": "ageRatio",
+      "label": "年龄占比",
+      "note": "粉丝年龄结构",
+      "regex": "(\\d{2})-(\\d{2})岁[^\\n]{0,8}占比\\s*(\\d+)%\\s*以上"
+    },
+    {
+      "key": "underAgeRatio",
+      "label": "低龄占比上限",
+      "note": "低龄粉丝比例红线",
+      "regex": "(\\d+)岁以下[^\\n]{0,6}(?:的|占比)?[^\\n]{0,6}(?:不能超过|不超过|≤|<)\\s*(\\d+)%"
+    },
+    {
+      "key": "interestTags",
+      "label": "兴趣标签",
+      "note": "兴趣前三标签命中数",
+      "regex": "兴趣[^\\n]{0,6}(?:前三)?标签[^\\n]{0,6}(?:含|包含)\\s*(\\d+)\\s*个"
+    },
+    {
+      "key": "ipMustBe",
+      "label": "达人IP硬约束",
+      "note": "达人地域/IP 硬性要求",
+      "regex": "(?:达人)?\\s*ip\\s*(?:必须|要求|需|为|在)\\s*([^\\s,,。;\\n]+)"
+    },
+    {
+      "key": "sameProvince",
+      "label": "同省不跨区",
+      "note": "地域同省硬约束",
+      "regex": "(?:同|同一)个?省(?:份)?[^\\n]{0,12}(?:不要|不|禁止)?\\s*跨区域"
+    }
+  ]
+}

+ 49 - 0
claude-code-tihao-sourcing/docs/case-library/fewshot-cases.json

@@ -0,0 +1,49 @@
+{
+  "version": 1,
+  "source": "E:\\workspace\\tihao-ai\\docs\\20260813 - AI提号规则及喂养资料\\历史选号案例\\case_template.xlsx",
+  "generatedAt": "2026-08-14T03:03:13.048Z",
+  "purpose": "案例喂养 few-shot 库:品类+投放目标+人群+调性+平台 -> 选中账号特征 -> 结果标签。用于下次选号时做正/负样本参照。",
+  "cases": [
+    {
+      "id": "CASE-2026-001",
+      "category": "美妆护肤",
+      "goal": "种草转化",
+      "audience": "20-28岁油痘肌女性",
+      "brandTone": "硬核测评",
+      "platform": "xiaohongshu",
+      "platformLabel": "小红书",
+      "selectedAccountProfile": "18w粉/互动率3.5%/成分党/报价8k",
+      "outcomeLabel": "爆文",
+      "outcome": "positive",
+      "keyReason": "赞藏比高+垂直度92%,与油皮人群高度契合",
+      "customerFeedback": "客户确认续投",
+      "executionData": "单篇赞评藏1200,搜索『油皮护肤』占位TOP3",
+      "signalTags": [
+        "互动率",
+        "垂直度",
+        "人群契合"
+      ]
+    },
+    {
+      "id": "CASE-2026-002",
+      "category": "美妆护肤",
+      "goal": "种草转化",
+      "audience": "20-28岁油痘肌女性",
+      "brandTone": "硬核测评",
+      "platform": "douyin",
+      "platformLabel": "抖音",
+      "selectedAccountProfile": "500w粉/互动率0.8%/搞笑/报价20w",
+      "outcomeLabel": "翻车",
+      "outcome": "negative",
+      "keyReason": "粉丝画像匹配仅35%,互动率虚胖,内容疲劳",
+      "customerFeedback": "客户否决",
+      "executionData": "无转化,评论区负面",
+      "signalTags": [
+        "互动率",
+        "画像匹配",
+        "内容质量",
+        "数据虚胖"
+      ]
+    }
+  ]
+}

+ 5 - 5
claude-code-tihao-sourcing/docs/live-provider-integration-runbook.md

@@ -1,6 +1,6 @@
 # Tihao SOP 真实 Provider 联调 Runbook
 # Tihao SOP 真实 Provider 联调 Runbook
 
 
-本文档用于拿到真实 TikHub、VOC social、视频分析或多模态 evidence provider 权限后的联调验收。它不替代默认发布门禁;默认发布门禁仍然是:
+本文档用于拿到真实参考账号补证、VOC social、视频分析或多模态 evidence provider 权限后的联调验收。它不替代默认发布门禁;默认发布门禁仍然是:
 
 
 ```powershell
 ```powershell
 npm run acceptance
 npm run acceptance
@@ -8,7 +8,7 @@ npm run acceptance
 
 
 真实 provider gate 只有在具备安全测试账号、余额和样本时才运行。
 真实 provider gate 只有在具备安全测试账号、余额和样本时才运行。
 
 
-发布红线:不能宣称未验证的 provider 已完成真实接入。没有跑通 `acceptance:providers`,不能说 TikHub 已接通;没有跑通 `acceptance:video-ab`,不能说参考视频分析已经证明能提升提号结果。
+发布红线:不能宣称未验证的 provider 已完成真实接入。没有跑通 `acceptance:providers`,不能说参考补证 provider 已接通;没有跑通 `acceptance:video-ab`,不能说参考视频分析已经证明能提升提号结果。
 
 
 ## 1. 联调目标
 ## 1. 联调目标
 
 
@@ -26,7 +26,7 @@ npm run acceptance
 
 
 - 一份安全测试 Brief,推荐使用 DHA、敏感肌、清洁、零食等已有 fixture;
 - 一份安全测试 Brief,推荐使用 DHA、敏感肌、清洁、零食等已有 fixture;
 - 至少 1 条可访问的参考博主链接,优先选择带视频资源的链接;
 - 至少 1 条可访问的参考博主链接,优先选择带视频资源的链接;
-- TikHub 兼容参考补证 provider URL;
+- 参考账号补证 provider URL;
 - 多模态 evidence provider URL,或 OpenAI 兼容豆包视频分析 URL;
 - 多模态 evidence provider URL,或 OpenAI 兼容豆包视频分析 URL;
 - 如 provider 需要鉴权,准备运行时 token;
 - 如 provider 需要鉴权,准备运行时 token;
 - 如要跑 live 候选检索,准备 Tihao `sessionToken` 和 `Company objectId`。
 - 如要跑 live 候选检索,准备 Tihao `sessionToken` 和 `Company objectId`。
@@ -38,8 +38,8 @@ npm run acceptance
 PowerShell 示例:
 PowerShell 示例:
 
 
 ```powershell
 ```powershell
-$env:TIKHUB_BASE_URL="<reference-enrichment-provider-url>"
-$env:TIKHUB_TOKEN="<optional-tikhub-token>"
+$env:SOCIAL_ANALYSIS_BASE_URL="<reference-enrichment-provider-url>"
+$env:VOC_SOCIAL_TOKEN="<optional-reference-provider-token>"
 $env:TIHAO_EVIDENCE_BASE_URL="<multimodal-evidence-provider-url>"
 $env:TIHAO_EVIDENCE_BASE_URL="<multimodal-evidence-provider-url>"
 $env:TIHAO_EVIDENCE_TOKEN="<optional-evidence-provider-token>"
 $env:TIHAO_EVIDENCE_TOKEN="<optional-evidence-provider-token>"
 $env:DOUBAO_VISION_BASE_URL="<user-filled-video-analysis-url>"
 $env:DOUBAO_VISION_BASE_URL="<user-filled-video-analysis-url>"

+ 13 - 0
claude-code-tihao-sourcing/docs/media-library-collection-ddl.txt

@@ -0,0 +1,13 @@
+// 公司媒体资源库 collection(MongoDB)—— 由 media-library-schema 生成,未来建库直接执行
+db.createCollection('media_library');
+
+db.media_library.createIndex({"platform":1,"blogger_id":1}, {"name":"uk_blogger_id","unique":true});
+db.media_library.createIndex({"platform":1,"fans_count":-1}, {"name":"idx_platform_fans_desc"});
+db.media_library.createIndex({"platform":1,"tier":1,"fans_count":-1}, {"name":"idx_platform_tier"});
+db.media_library.createIndex({"platform":1,"tax_inclusive_price":1}, {"name":"idx_platform_price_asc"});
+db.media_library.createIndex({"platform":1,"interaction_rate":-1}, {"name":"idx_platform_interaction_desc"});
+db.media_library.createIndex({"platform":1,"verticality":-1}, {"name":"idx_platform_verticality"});
+db.media_library.createIndex({"platform":1,"cpe":1,"cpv":1}, {"name":"idx_platform_cpe_asc"});
+
+// 必填字段(缺一不可,对应模板「*」标记):
+// platform, content_type, nickname, fans_count, homepage_url, quote_url, fans_profile, tax_inclusive_price

+ 304 - 0
claude-code-tihao-sourcing/docs/media-library-field-schema.json

@@ -0,0 +1,304 @@
+{
+  "collection": "media_library",
+  "generatedAt": "2026-08-14T02:28:43.587Z",
+  "coreFields": {
+    "platform": {
+      "zh": "平台",
+      "type": "string",
+      "required": true,
+      "dimension": "platform",
+      "note": "小红书 / 抖音 / B站 等"
+    },
+    "content_type": {
+      "zh": "内容类型",
+      "type": "string",
+      "required": true,
+      "dimension": "contentType",
+      "note": "图文 / 视频 / 直播"
+    },
+    "nickname": {
+      "zh": "达人昵称",
+      "type": "string",
+      "required": true,
+      "dimension": "id"
+    },
+    "blogger_id": {
+      "zh": "达人ID",
+      "type": "string",
+      "dimension": "id",
+      "note": "平台侧唯一 ID,用于与蒲公英库对账"
+    },
+    "fans_count": {
+      "zh": "粉丝量",
+      "type": "int",
+      "required": true,
+      "dimension": "fanRange"
+    },
+    "homepage_url": {
+      "zh": "主页链接",
+      "type": "string",
+      "required": true,
+      "dimension": "profileUrl"
+    },
+    "quote_url": {
+      "zh": "报价链接",
+      "type": "string",
+      "required": true,
+      "dimension": "price"
+    },
+    "avg_play_30d": {
+      "zh": "近30天均播",
+      "type": "int",
+      "dimension": "interaction"
+    },
+    "fans_profile": {
+      "zh": "粉丝画像",
+      "type": "string",
+      "required": true,
+      "dimension": "fansProfile",
+      "note": "年龄段 / 性别 / 城市 描述"
+    },
+    "tax_inclusive_price": {
+      "zh": "含税报价",
+      "type": "number",
+      "required": true,
+      "dimension": "price",
+      "unit": "元"
+    },
+    "schedule": {
+      "zh": "档期",
+      "type": "string",
+      "dimension": "availability",
+      "note": "可合作档期"
+    },
+    "case_url": {
+      "zh": "代表案例链接",
+      "type": "string",
+      "dimension": "evidence"
+    },
+    "report_form": {
+      "zh": "报备形式",
+      "type": "string",
+      "dimension": "commercial",
+      "note": "报备 / 非报备"
+    },
+    "picture_price": {
+      "zh": "图文价格",
+      "type": "number",
+      "dimension": "price",
+      "unit": "元"
+    },
+    "video_price": {
+      "zh": "视频价格",
+      "type": "number",
+      "dimension": "price",
+      "unit": "元"
+    },
+    "cpv": {
+      "zh": "CPV(单次播放成本)",
+      "type": "number",
+      "dimension": "cpm",
+      "unit": "元"
+    },
+    "cpe": {
+      "zh": "CPE(单次互动成本)",
+      "type": "number",
+      "dimension": "cpm",
+      "unit": "元"
+    },
+    "read_median": {
+      "zh": "阅读中位数",
+      "type": "number",
+      "dimension": "cpm"
+    },
+    "interaction_median": {
+      "zh": "互动中位数",
+      "type": "number",
+      "dimension": "interaction"
+    },
+    "style": {
+      "zh": "风格",
+      "type": "string",
+      "dimension": "verticality"
+    },
+    "recent_status": {
+      "zh": "近况",
+      "type": "string",
+      "dimension": "activity"
+    },
+    "tier": {
+      "zh": "tier层级",
+      "type": "string",
+      "dimension": "tier",
+      "note": "头部/腰部/尾部 KOL / KOC"
+    },
+    "verticality": {
+      "zh": "垂直度",
+      "type": "number",
+      "dimension": "verticality",
+      "unit": "%"
+    },
+    "commercial_ratio": {
+      "zh": "商业内容占比",
+      "type": "number",
+      "dimension": "commercial",
+      "unit": "%"
+    },
+    "interaction_rate": {
+      "zh": "互动率",
+      "type": "number",
+      "dimension": "interaction",
+      "unit": "%"
+    },
+    "completion_rate": {
+      "zh": "完播率",
+      "type": "number",
+      "dimension": "completionRate",
+      "unit": "%"
+    },
+    "platform_score": {
+      "zh": "平台评分",
+      "type": "number",
+      "dimension": "score",
+      "unit": "分"
+    },
+    "growth_trend": {
+      "zh": "增长趋势",
+      "type": "string",
+      "dimension": "growth"
+    },
+    "fan_club_count": {
+      "zh": "粉丝团人数",
+      "type": "int",
+      "dimension": "fansProfile"
+    },
+    "live_avg_online": {
+      "zh": "直播场均在线",
+      "type": "int",
+      "dimension": "live"
+    },
+    "charging_count": {
+      "zh": "充电人数",
+      "type": "int",
+      "dimension": "live"
+    },
+    "avg_like_count": {
+      "zh": "平均赞同数",
+      "type": "int",
+      "dimension": "interaction"
+    },
+    "verified": {
+      "zh": "认证",
+      "type": "string",
+      "dimension": "verified"
+    },
+    "update_frequency": {
+      "zh": "更新频率",
+      "type": "string",
+      "dimension": "activity"
+    },
+    "remark": {
+      "zh": "备注",
+      "type": "string",
+      "dimension": "remark"
+    }
+  },
+  "requiredFields": [
+    "platform",
+    "content_type",
+    "nickname",
+    "fans_count",
+    "homepage_url",
+    "quote_url",
+    "fans_profile",
+    "tax_inclusive_price"
+  ],
+  "manualOnlyFields": [
+    "schedule",
+    "case_url",
+    "report_form",
+    "tax_inclusive_price",
+    "style",
+    "recent_status",
+    "tier",
+    "verified",
+    "update_frequency",
+    "remark"
+  ],
+  "pgyAlias": {
+    "blogger_id": "blogger_id",
+    "nickname": "nickname",
+    "fans_count": "follower_count",
+    "picture_price": "picture_price",
+    "video_price": "video_price",
+    "interaction_rate": "interaction_rate",
+    "completion_rate": "video_full_view_rate",
+    "platform_score": "credit_level",
+    "verticality": "content_tags",
+    "commercial_ratio": "co_note_count",
+    "read_median": "read_midnum",
+    "interaction_median": "interact_midnum"
+  },
+  "indexes": [
+    {
+      "name": "uk_blogger_id",
+      "spec": {
+        "platform": 1,
+        "blogger_id": 1
+      },
+      "options": {
+        "unique": true
+      }
+    },
+    {
+      "name": "idx_platform_fans_desc",
+      "spec": {
+        "platform": 1,
+        "fans_count": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_platform_tier",
+      "spec": {
+        "platform": 1,
+        "tier": 1,
+        "fans_count": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_platform_price_asc",
+      "spec": {
+        "platform": 1,
+        "tax_inclusive_price": 1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_platform_interaction_desc",
+      "spec": {
+        "platform": 1,
+        "interaction_rate": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_platform_verticality",
+      "spec": {
+        "platform": 1,
+        "verticality": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_platform_cpe_asc",
+      "spec": {
+        "platform": 1,
+        "cpe": 1,
+        "cpv": 1
+      },
+      "options": {}
+    }
+  ]
+}

+ 39 - 0
claude-code-tihao-sourcing/docs/media-library-query-map.md

@@ -0,0 +1,39 @@
+# 公司媒体资源库字段映射
+
+> 由 media-library-schema 生成。公司侧资源库(人工维护 + 商单交付)字段说明。
+
+- 字段总数:35,必填 8 个。
+
+## 必填字段(模板「*」标记)
+
+| 字段 | 中文 | 类型 | 选号维度 |
+| --- | --- | --- | --- |
+| `platform` | 平台 | string | platform |
+| `content_type` | 内容类型 | string | contentType |
+| `nickname` | 达人昵称 | string | id |
+| `fans_count` | 粉丝量 | int | fanRange |
+| `homepage_url` | 主页链接 | string | profileUrl |
+| `quote_url` | 报价链接 | string | price |
+| `fans_profile` | 粉丝画像 | string | fansProfile |
+| `tax_inclusive_price` | 含税报价 | number(元) | price |
+
+## 商单交付侧字段(需人工补录,蒲公英爬不到)
+
+`schedule`、`case_url`、`report_form`、`tax_inclusive_price`、`style`、`recent_status`、`tier`、`verified`、`update_frequency`、`remark`
+
+## 与蒲公英采集库字段对账
+
+| 公司资源库字段 | 蒲公英字段 | 说明 |
+| --- | --- | --- |
+| `blogger_id` | `blogger_id` | 达人ID |
+| `nickname` | `nickname` | 达人昵称 |
+| `fans_count` | `follower_count` | 粉丝量 |
+| `picture_price` | `picture_price` | 图文价格 |
+| `video_price` | `video_price` | 视频价格 |
+| `interaction_rate` | `interaction_rate` | 互动率 |
+| `completion_rate` | `video_full_view_rate` | 完播率 |
+| `platform_score` | `credit_level` | 平台评分 |
+| `verticality` | `content_tags` | 垂直度 |
+| `commercial_ratio` | `co_note_count` | 商业内容占比 |
+| `read_median` | `read_midnum` | 阅读中位数 |
+| `interaction_median` | `interact_midnum` | 互动中位数 |

+ 1 - 1
claude-code-tihao-sourcing/docs/payment-package-links.md

@@ -38,7 +38,7 @@ Title: 社媒分析服务
 Service: Fmode
 Service: Fmode
 ```
 ```
 
 
-对外只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名(如 TikHub 等)。社媒分析服务未开通或余额不足时,返回 `needs_recharge` 并展示上面的「社媒分析服务充值」链接,保持 `errors=[]`。
+对外只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名。社媒分析服务未开通或余额不足时,返回 `needs_recharge` 并展示上面的「社媒分析服务充值」链接,保持 `errors=[]`。
 
 
 ## 友好状态
 ## 友好状态
 
 

+ 15 - 0
claude-code-tihao-sourcing/docs/pgy-collection-ddl.txt

@@ -0,0 +1,15 @@
+// 蒲公英博主 collection(MongoDB)—— 由 pgy-schema 生成,未来建库直接执行
+db.createCollection('pgy_blogger');
+
+db.pgy_blogger.createIndex({"blogger_id":1}, {"name":"uk_blogger_id","unique":true});
+db.pgy_blogger.createIndex({"follower_count":-1}, {"name":"idx_fans_desc"});
+db.pgy_blogger.createIndex({"trade_type":1,"follower_count":-1}, {"name":"idx_trade_fans"});
+db.pgy_blogger.createIndex({"interaction_rate":-1}, {"name":"idx_interaction_desc"});
+db.pgy_blogger.createIndex({"lower_price":1}, {"name":"idx_price_asc"});
+db.pgy_blogger.createIndex({"content_tags":1,"feature_tags":1}, {"name":"idx_tags"});
+db.pgy_blogger.createIndex({"location":1}, {"name":"idx_location"});
+db.pgy_blogger.createIndex({"is_active":1,"active_days_in_last7":-1}, {"name":"idx_active"});
+db.pgy_blogger.createIndex({"estimate_video_cpm":1,"estimate_picture_cpm":1}, {"name":"idx_cpm_asc"});
+
+// 必采报价字段(列表接口)示例文档:
+// { blogger_id, nickname, follower_count, picture_price, video_price, lower_price, trade_type, content_tags }

+ 743 - 0
claude-code-tihao-sourcing/docs/pgy-field-schema.json

@@ -0,0 +1,743 @@
+{
+  "collection": "pgy_blogger",
+  "generatedAt": "2026-08-14T02:21:53.994Z",
+  "sources": {
+    "list": {
+      "name": "列表 blogger/v2",
+      "method": "POST",
+      "path": "xiaohongshu-pgy/api/solar/cooperator/blogger/v2",
+      "priority": 3
+    },
+    "profile": {
+      "name": "个人简介",
+      "method": "GET",
+      "path": "cooperator/user/blogger/{id}",
+      "priority": 2
+    },
+    "notesRate": {
+      "name": "数据概括 notesRate?noteType=3",
+      "method": "GET",
+      "path": "kol/dataV3/notesRate",
+      "priority": 2
+    },
+    "noteData": {
+      "name": "笔记数据 notesRate×3",
+      "method": "GET",
+      "path": "kol/dataV3/notesRate",
+      "priority": 1
+    },
+    "notesDetail": {
+      "name": "笔记案例 notesDetail×3",
+      "method": "GET",
+      "path": "kol/dataV2/notesDetail",
+      "priority": 1
+    },
+    "fansSummary": {
+      "name": "粉丝概览 fansSummary",
+      "method": "GET",
+      "path": "kol/dataV3/fansSummary",
+      "priority": 2
+    },
+    "fansProfile": {
+      "name": "粉丝画像 fans_profile",
+      "method": "GET",
+      "path": "kol/data/{id}/fans_profile",
+      "priority": 1
+    },
+    "dataSummary": {
+      "name": "数据表现总览 data_summary",
+      "method": "GET",
+      "path": "kol/data_v3/data_summary",
+      "priority": 1
+    },
+    "costEffective": {
+      "name": "流量性价比 costEffective",
+      "method": "GET",
+      "path": "kol/dataV2/costEffective",
+      "priority": 0
+    }
+  },
+  "dimensions": {
+    "fanRange": {
+      "zh": "粉丝范围",
+      "fields": [
+        "follower_count"
+      ],
+      "type": "number",
+      "unit": "人",
+      "query": {
+        "follower_count": {
+          "$gte": 50000,
+          "$lte": 500000
+        }
+      },
+      "note": "腰部 KOL 5万-50万;对应 ranker.fanRange 与 platform-quality-rules 分级"
+    },
+    "interaction": {
+      "zh": "互动率/互动中位数",
+      "fields": [
+        "interaction_rate",
+        "interact_midnum",
+        "inter_midnum_30d_total"
+      ],
+      "type": "number",
+      "unit": "% / 次",
+      "query": {
+        "interaction_rate": {
+          "$gte": 3
+        }
+      },
+      "note": "小红书互动率 ≥3% 优秀、≥1% 合格;抖音 ≥3% 优秀、≥1.5% 合格"
+    },
+    "price": {
+      "zh": "报价(必采)",
+      "fields": [
+        "picture_price",
+        "video_price",
+        "lower_price"
+      ],
+      "type": "number",
+      "unit": "元",
+      "query": {
+        "lower_price": {
+          "$lte": 3000
+        }
+      },
+      "note": "⭐ 列表接口只采这 3 个报价字段"
+    },
+    "verticality": {
+      "zh": "垂直度/类目",
+      "fields": [
+        "trade_type",
+        "content_tags",
+        "feature_tags",
+        "note_type_share"
+      ],
+      "type": "string/array",
+      "query": {
+        "trade_type": "美妆护肤",
+        "content_tags": {
+          "$all": [
+            "成分党"
+          ]
+        }
+      },
+      "note": "小红书垂直度 ≥70% 合格、≥90% 优秀"
+    },
+    "score": {
+      "zh": "评分/信用/等级",
+      "fields": [
+        "credit_level",
+        "goodness_level",
+        "level_name"
+      ],
+      "type": "string",
+      "query": {
+        "credit_level": {
+          "$in": [
+            "Lv5",
+            "Lv6",
+            "Lv7"
+          ]
+        }
+      },
+      "note": "蒲公英评分 ≥4.0 合格、≥4.5 优秀、<3.5 暂不考虑"
+    },
+    "location": {
+      "zh": "所在地/城市",
+      "fields": [
+        "location"
+      ],
+      "type": "string",
+      "query": {
+        "location": {
+          "$regex": "上海"
+        }
+      },
+      "note": "地域定向"
+    },
+    "gender": {
+      "zh": "性别",
+      "fields": [
+        "gender"
+      ],
+      "type": "int",
+      "query": {
+        "gender": 1
+      },
+      "note": "1=女 2=男"
+    },
+    "fansProfile": {
+      "zh": "粉丝画像(年龄/性别/城市)",
+      "fields": [
+        "fans_age_dist",
+        "fans_gender_dist",
+        "fans_city_top"
+      ],
+      "type": "array",
+      "query": {
+        "fans_age_dist": {
+          "$elemMatch": {
+            "age": "18-24",
+            "rate": {
+              "$gte": 40
+            }
+          }
+        }
+      },
+      "note": "粉丝画像匹配度 ≥60% 合格、≥80% 优秀"
+    },
+    "completionRate": {
+      "zh": "视频完播率",
+      "fields": [
+        "video_full_view_rate",
+        "video_finish_rate"
+      ],
+      "type": "number",
+      "unit": "%",
+      "query": {
+        "video_full_view_rate": {
+          "$gte": 20
+        }
+      },
+      "note": "抖音完播率 ≥20% 合格、≥40% 优秀、<10% 危险"
+    },
+    "likeCollectRatio": {
+      "zh": "赞藏比(收藏/点赞)",
+      "fields": [
+        "like_median",
+        "collect_median"
+      ],
+      "type": "number",
+      "query": {
+        "$expr": {
+          "$gte": [
+            {
+              "$divide": [
+                "$collect_median",
+                "$like_median"
+              ]
+            },
+            0.5
+          ]
+        }
+      },
+      "note": "小红书赞藏比 ≥0.5 合格、≥1.0 优秀"
+    },
+    "cpm": {
+      "zh": "CPM/CPE/阅读成本",
+      "fields": [
+        "estimate_picture_cpm",
+        "estimate_video_cpm",
+        "estimate_picture_engage_cost",
+        "estimate_video_engage_cost"
+      ],
+      "type": "number",
+      "unit": "元",
+      "query": {
+        "estimate_video_cpm": {
+          "$lte": 80
+        }
+      },
+      "note": "抖音腰部 CPM 40-80、CPE 5-10"
+    },
+    "growth": {
+      "zh": "粉丝增长",
+      "fields": [
+        "fans_growth_rate",
+        "fans_30d_growth_rate",
+        "fans_30d_growth_num"
+      ],
+      "type": "number",
+      "unit": "%",
+      "query": {
+        "fans_30d_growth_rate": {
+          "$gte": 0
+        }
+      },
+      "note": "负增长 = 账号健康度风险(platform-quality-rules 已接入)"
+    },
+    "activity": {
+      "zh": "活跃度",
+      "fields": [
+        "active_days_in_last7",
+        "is_active",
+        "last_active_time"
+      ],
+      "type": "mixed",
+      "query": {
+        "is_active": true,
+        "active_days_in_last7": {
+          "$gte": 3
+        }
+      },
+      "note": "近30天无更新 = 数据真实性风险"
+    },
+    "verified": {
+      "zh": "认证/合作权限",
+      "fields": [
+        "verified",
+        "is_pgy_creator",
+        "has_buyer_auth",
+        "cooperate_state"
+      ],
+      "type": "bool",
+      "query": {
+        "is_pgy_creator": true,
+        "has_buyer_auth": true
+      },
+      "note": "蒲公英创作者 + 品牌合作权限是下单前提"
+    }
+  },
+  "coreFields": {
+    "blogger_id": {
+      "zh": "博主ID",
+      "type": "string",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "id",
+      "primary": true
+    },
+    "nickname": {
+      "zh": "昵称",
+      "type": "string",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "id"
+    },
+    "follower_count": {
+      "zh": "粉丝数",
+      "type": "int",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "fanRange"
+    },
+    "like_count": {
+      "zh": "获赞数",
+      "type": "int",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "interaction"
+    },
+    "post_count": {
+      "zh": "笔记数",
+      "type": "int",
+      "sources": [
+        "list"
+      ],
+      "dimension": "activity"
+    },
+    "co_note_count": {
+      "zh": "合作笔记数",
+      "type": "int",
+      "sources": [
+        "list"
+      ],
+      "dimension": "activity"
+    },
+    "verified": {
+      "zh": "是否认证",
+      "type": "bool",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "verified"
+    },
+    "tags": {
+      "zh": "个人标签",
+      "type": "array",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "verticality"
+    },
+    "content_tags": {
+      "zh": "内容标签",
+      "type": "array",
+      "sources": [
+        "list"
+      ],
+      "dimension": "verticality"
+    },
+    "feature_tags": {
+      "zh": "特色标签",
+      "type": "array",
+      "sources": [
+        "list"
+      ],
+      "dimension": "verticality"
+    },
+    "trade_type": {
+      "zh": "行业",
+      "type": "string",
+      "sources": [
+        "list"
+      ],
+      "dimension": "verticality"
+    },
+    "gender": {
+      "zh": "性别",
+      "type": "int",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "gender"
+    },
+    "location": {
+      "zh": "所在地",
+      "type": "string",
+      "sources": [
+        "list",
+        "profile"
+      ],
+      "dimension": "location"
+    },
+    "picture_price": {
+      "zh": "图文报价",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "price",
+      "priority": 3
+    },
+    "video_price": {
+      "zh": "视频报价",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "price",
+      "priority": 3
+    },
+    "lower_price": {
+      "zh": "报价下限",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "price",
+      "priority": 3
+    },
+    "interaction_rate": {
+      "zh": "互动率",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "interaction",
+      "priority": 2
+    },
+    "interact_midnum": {
+      "zh": "互动中位数",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "interaction"
+    },
+    "read_midnum": {
+      "zh": "阅读中位数",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "cpm"
+    },
+    "like_median": {
+      "zh": "中位点赞量",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "likeCollectRatio"
+    },
+    "collect_median": {
+      "zh": "中位收藏量",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "likeCollectRatio"
+    },
+    "video_full_view_rate": {
+      "zh": "视频完播率",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "completionRate"
+    },
+    "hundred_like_rate": {
+      "zh": "百赞笔记比例",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "interaction"
+    },
+    "kilo_like_rate": {
+      "zh": "千赞笔记比例",
+      "type": "number",
+      "sources": [
+        "notesRate"
+      ],
+      "dimension": "interaction"
+    },
+    "fans_growth_rate": {
+      "zh": "30天增粉率",
+      "type": "number",
+      "sources": [
+        "fansSummary"
+      ],
+      "dimension": "growth"
+    },
+    "fans_30d_growth_rate": {
+      "zh": "30天粉丝增长率",
+      "type": "number",
+      "sources": [
+        "list",
+        "dataSummary"
+      ],
+      "dimension": "growth"
+    },
+    "fans_active_l28_rate": {
+      "zh": "28天活跃粉丝占比",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "activity"
+    },
+    "fans_engage_30d_rate": {
+      "zh": "30天互动率",
+      "type": "number",
+      "sources": [
+        "list"
+      ],
+      "dimension": "interaction"
+    },
+    "fans_age_dist": {
+      "zh": "粉丝年龄分布",
+      "type": "array",
+      "sources": [
+        "fansProfile"
+      ],
+      "dimension": "fansProfile"
+    },
+    "fans_gender_dist": {
+      "zh": "粉丝性别分布",
+      "type": "array",
+      "sources": [
+        "fansProfile"
+      ],
+      "dimension": "fansProfile"
+    },
+    "fans_city_top": {
+      "zh": "粉丝城市TOP",
+      "type": "array",
+      "sources": [
+        "fansProfile"
+      ],
+      "dimension": "fansProfile"
+    },
+    "estimate_picture_cpm": {
+      "zh": "估算图文CPM",
+      "type": "number",
+      "sources": [
+        "list",
+        "costEffective"
+      ],
+      "dimension": "cpm"
+    },
+    "estimate_video_cpm": {
+      "zh": "估算视频CPM",
+      "type": "number",
+      "sources": [
+        "list",
+        "costEffective"
+      ],
+      "dimension": "cpm"
+    },
+    "estimate_picture_engage_cost": {
+      "zh": "估算图文互动成本",
+      "type": "number",
+      "sources": [
+        "list",
+        "costEffective"
+      ],
+      "dimension": "cpm"
+    },
+    "estimate_video_engage_cost": {
+      "zh": "估算视频互动成本",
+      "type": "number",
+      "sources": [
+        "list",
+        "costEffective"
+      ],
+      "dimension": "cpm"
+    },
+    "credit_level": {
+      "zh": "信用等级",
+      "type": "string",
+      "sources": [
+        "profile"
+      ],
+      "dimension": "score"
+    },
+    "goodness_level": {
+      "zh": "诚信等级",
+      "type": "string",
+      "sources": [
+        "profile"
+      ],
+      "dimension": "score"
+    },
+    "level_name": {
+      "zh": "等级名称",
+      "type": "string",
+      "sources": [
+        "profile"
+      ],
+      "dimension": "score"
+    },
+    "is_pgy_creator": {
+      "zh": "蒲公英创作者",
+      "type": "bool",
+      "sources": [
+        "profile"
+      ],
+      "dimension": "verified"
+    },
+    "has_buyer_auth": {
+      "zh": "品牌合作权限",
+      "type": "bool",
+      "sources": [
+        "list"
+      ],
+      "dimension": "verified"
+    },
+    "cooperate_state": {
+      "zh": "合作状态",
+      "type": "string",
+      "sources": [
+        "list"
+      ],
+      "dimension": "verified"
+    },
+    "active_days_in_last7": {
+      "zh": "近7天活跃天数",
+      "type": "int",
+      "sources": [
+        "dataSummary"
+      ],
+      "dimension": "activity"
+    },
+    "is_active": {
+      "zh": "是否活跃",
+      "type": "bool",
+      "sources": [
+        "dataSummary"
+      ],
+      "dimension": "activity"
+    },
+    "last_active_time": {
+      "zh": "最后活跃时间",
+      "type": "string",
+      "sources": [
+        "profile"
+      ],
+      "dimension": "activity"
+    },
+    "response_rate": {
+      "zh": "回复率",
+      "type": "number",
+      "sources": [
+        "dataSummary"
+      ],
+      "dimension": "activity"
+    }
+  },
+  "indexes": [
+    {
+      "name": "uk_blogger_id",
+      "spec": {
+        "blogger_id": 1
+      },
+      "options": {
+        "unique": true
+      }
+    },
+    {
+      "name": "idx_fans_desc",
+      "spec": {
+        "follower_count": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_trade_fans",
+      "spec": {
+        "trade_type": 1,
+        "follower_count": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_interaction_desc",
+      "spec": {
+        "interaction_rate": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_price_asc",
+      "spec": {
+        "lower_price": 1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_tags",
+      "spec": {
+        "content_tags": 1,
+        "feature_tags": 1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_location",
+      "spec": {
+        "location": 1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_active",
+      "spec": {
+        "is_active": 1,
+        "active_days_in_last7": -1
+      },
+      "options": {}
+    },
+    {
+      "name": "idx_cpm_asc",
+      "spec": {
+        "estimate_video_cpm": 1,
+        "estimate_picture_cpm": 1
+      },
+      "options": {}
+    }
+  ]
+}

+ 36 - 0
claude-code-tihao-sourcing/docs/pgy-sourcing-query-map.md

@@ -0,0 +1,36 @@
+# 蒲公英选号查询映射
+
+> 由 pgy-schema 生成。选号 SOP 的每个筛选维度 → 蒲公英字段 → MongoDB 查询。
+
+| 选号维度 | 蒲公英字段 | 查询示例 |
+| --- | --- | --- |
+| 粉丝范围 | follower_count | `{"follower_count":{"$gte":50000,"$lte":500000}}` |
+| 互动率/互动中位数 | interaction_rate、interact_midnum、inter_midnum_30d_total | `{"interaction_rate":{"$gte":3}}` |
+| 报价(必采) | picture_price、video_price、lower_price | `{"lower_price":{"$lte":3000}}` |
+| 垂直度/类目 | trade_type、content_tags、feature_tags、note_type_share | `{"trade_type":"美妆护肤","content_tags":{"$all":["成分党"]}}` |
+| 评分/信用/等级 | credit_level、goodness_level、level_name | `{"credit_level":{"$in":["Lv5","Lv6","Lv7"]}}` |
+| 所在地/城市 | location | `{"location":{"$regex":"上海"}}` |
+| 性别 | gender | `{"gender":1}` |
+| 粉丝画像(年龄/性别/城市) | fans_age_dist、fans_gender_dist、fans_city_top | `{"fans_age_dist":{"$elemMatch":{"age":"18-24","rate":{"$gte":40}}}}` |
+| 视频完播率 | video_full_view_rate、video_finish_rate | `{"video_full_view_rate":{"$gte":20}}` |
+| 赞藏比(收藏/点赞) | like_median、collect_median | `{"$expr":{"$gte":[{"$divide":["$collect_median","$like_median"]},0.5]}}` |
+| CPM/CPE/阅读成本 | estimate_picture_cpm、estimate_video_cpm、estimate_picture_engage_cost、estimate_video_engage_cost | `{"estimate_video_cpm":{"$lte":80}}` |
+| 粉丝增长 | fans_growth_rate、fans_30d_growth_rate、fans_30d_growth_num | `{"fans_30d_growth_rate":{"$gte":0}}` |
+| 活跃度 | active_days_in_last7、is_active、last_active_time | `{"is_active":true,"active_days_in_last7":{"$gte":3}}` |
+| 认证/合作权限 | verified、is_pgy_creator、has_buyer_auth、cooperate_state | `{"is_pgy_creator":true,"has_buyer_auth":true}` |
+
+## 采集接口优先级
+
+| 优先级 | 接口 | 核心字段 |
+| --- | --- | --- |
+| ⭐⭐⭐ 必采 | 列表 blogger/v2(`POST xiaohongshu-pgy/api/solar/cooperator/blogger/v2`) | blogger_id、nickname、follower_count、like_count、post_count、co_note_count、verified、tags、content_tags、feature_tags、trade_type、gender |
+| ⭐⭐ 高优先 | 个人简介(`GET cooperator/user/blogger/{id}`) | blogger_id、nickname、follower_count、like_count、verified、tags、gender、location、credit_level、goodness_level、level_name、is_pgy_creator |
+| ⭐⭐ 高优先 | 数据概括 notesRate?noteType=3(`GET kol/dataV3/notesRate`) | interaction_rate、like_median、collect_median、video_full_view_rate、hundred_like_rate、kilo_like_rate |
+| ⭐ 中优先 | 笔记数据 notesRate×3(`GET kol/dataV3/notesRate`) | - |
+| ⭐ 中优先 | 笔记案例 notesDetail×3(`GET kol/dataV2/notesDetail`) | - |
+| ⭐⭐ 高优先 | 粉丝概览 fansSummary(`GET kol/dataV3/fansSummary`) | fans_growth_rate |
+| ⭐ 中优先 | 粉丝画像 fans_profile(`GET kol/data/{id}/fans_profile`) | fans_age_dist、fans_gender_dist、fans_city_top |
+| ⭐ 中优先 | 数据表现总览 data_summary(`GET kol/data_v3/data_summary`) | fans_30d_growth_rate、active_days_in_last7、is_active、response_rate |
+| 低优先 | 流量性价比 costEffective(`GET kol/dataV2/costEffective`) | estimate_picture_cpm、estimate_video_cpm、estimate_picture_engage_cost、estimate_video_engage_cost |
+
+> 接口总数:9 个详情 + 1 个列表 = 10 个接口。

+ 2 - 2
claude-code-tihao-sourcing/docs/reference-evidence-roadmap.md

@@ -44,7 +44,7 @@
 - `blockedCreators`;
 - `blockedCreators`;
 - 客户反馈中的“像某某账号”“不要某类账号”可以沉淀为下一轮提号偏好。
 - 客户反馈中的“像某某账号”“不要某类账号”可以沉淀为下一轮提号偏好。
 
 
-## 2. TikHub / VOC social 参考账号补证
+## 2. VOC social 参考账号补证
 
 
 目标:把 Brief 中的参考链接升级为参考账号基线,而不是只看链接文本。
 目标:把 Brief 中的参考链接升级为参考账号基线,而不是只看链接文本。
 
 
@@ -64,7 +64,7 @@ Provider 输入建议:
 {
 {
   "referenceEnrichmentBaseUrl": "https://<provider>",
   "referenceEnrichmentBaseUrl": "https://<provider>",
   "referenceEnrichmentPath": "reference-baselines",
   "referenceEnrichmentPath": "reference-baselines",
-  "tikhubToken": "<runtime optional token>",
+  "socialAnalysisToken": "<runtime optional token>",
   "referencePostsLimit": 20
   "referencePostsLimit": 20
 }
 }
 ```
 ```

+ 5 - 5
claude-code-tihao-sourcing/docs/release-notes.md

@@ -189,7 +189,7 @@ Validation:
 - 新增 `npm run real-gap:materials`,输出 `outputs/real-gap-material-audit-latest/real-gap-material-audit-summary.json`、Markdown 和 CSV。
 - 新增 `npm run real-gap:materials`,输出 `outputs/real-gap-material-audit-latest/real-gap-material-audit-summary.json`、Markdown 和 CSV。
 - 新增 `real-gap:materials:smoke` 并纳入 `npm run acceptance`,确保三个缺口都能找到项目材料,但 `canCloseNow=false` 不被误改。
 - 新增 `real-gap:materials:smoke` 并纳入 `npm run acceptance`,确保三个缺口都能找到项目材料,但 `canCloseNow=false` 不被误改。
 - 审计明确三个缺口的当前证据:
 - 审计明确三个缺口的当前证据:
-  - `historical-dataset`:DHA Brief/PoC/TikHub 工作簿已找到,`historyDraftRows=12`,逐字稿记录了 DHA 已提报但客户未反馈/未选新号;仍缺至少 5 个真实历史 Brief、客户决策、拒绝原因和历史人工补号量。
+  - `historical-dataset`:DHA Brief/PoC/参考分析工作簿已找到,`historyDraftRows=12`,逐字稿记录了 DHA 已提报但客户未反馈/未选新号;仍缺至少 5 个真实历史 Brief、客户决策、拒绝原因和历史人工补号量。
   - `video-ab-live-proof`:`videoResourceReady=true`、`realCandidateRows=1`、`realReferenceRows=1`、候选视频 note 线索 15 条;仍缺 `acceptance:video-ab` 的 live `proofContext` 和 runtime/company/provider。
   - `video-ab-live-proof`:`videoResourceReady=true`、`realCandidateRows=1`、`realReferenceRows=1`、候选视频 note 线索 15 条;仍缺 `acceptance:video-ab` 的 live `proofContext` 和 runtime/company/provider。
   - `manual-review-and-customer-effect`:`manualReviewDraftRows=12`,逐字稿覆盖客户选中率目标和人工复核逻辑;仍缺真实人工复核标签、客户选中/拒绝、反馈原因、本轮人工补号量,并且必须绑定通过的 history audit。
   - `manual-review-and-customer-effect`:`manualReviewDraftRows=12`,逐字稿覆盖客户选中率目标和人工复核逻辑;仍缺真实人工复核标签、客户选中/拒绝、反馈原因、本轮人工补号量,并且必须绑定通过的 history audit。
 - 审计报告把候选名单、逐字稿经验、视频资源 readiness 和旧 failure artifacts 的边界写清楚:它们是补证入口和失败证据,不是业务效果证明。
 - 审计报告把候选名单、逐字稿经验、视频资源 readiness 和旧 failure artifacts 的边界写清楚:它们是补证入口和失败证据,不是业务效果证明。
@@ -224,7 +224,7 @@ Validation:
 - 强推荐必须同时通过 Brief 和参考风格门槛;超预算、硬排除或高风险账号会被降级或剔除。
 - 强推荐必须同时通过 Brief 和参考风格门槛;超预算、硬排除或高风险账号会被降级或剔除。
 - Markdown 和 CSV 输出面向商务验收增强,包含 `Brief 解析摘要`、`参考视频风格指纹`、`候选召回记录`、`多模态证据卡`、`商务可用名单`、`剔除/降级原因`、`下一轮校准问题`。
 - Markdown 和 CSV 输出面向商务验收增强,包含 `Brief 解析摘要`、`参考视频风格指纹`、`候选召回记录`、`多模态证据卡`、`商务可用名单`、`剔除/降级原因`、`下一轮校准问题`。
 - PM 验收和 smoke 测试升级,强制检查高标准字段、真实性 gate 和 token 泄露扫描。
 - PM 验收和 smoke 测试升级,强制检查高标准字段、真实性 gate 和 token 泄露扫描。
-- 增加真实 VOC social 参考视频补证指引:使用 `https://server.fmode.cn/api/voc-social` 获取小红书视频详情,再把真实视频、封面、字幕或帧图资源传给豆包;不能拿 Parse sessionToken 直接调用公开 TikHub
+- 增加真实 VOC social 参考视频补证指引:使用 `https://server.fmode.cn/api/voc-social` 获取小红书视频详情,再把真实视频、封面、字幕或帧图资源传给豆包;不能拿 Parse sessionToken 直接调用公开第三方采集接口
 - 增加 `npm run acceptance:video-ab`,用于同一 Brief 和参考视频的 live A/B 验收。
 - 增加 `npm run acceptance:video-ab`,用于同一 Brief 和参考视频的 live A/B 验收。
 - 证据卡现在会影响候选参考风格命中点、分数和重排,不再只是报告里的被动展示。
 - 证据卡现在会影响候选参考风格命中点、分数和重排,不再只是报告里的被动展示。
 - 增加防跑偏的参考感知召回:允许生成 `DHA 专业科普` 这类和 Brief 绑定的召回词;禁止婚礼、宴会、布景、满月宴、仪式感等跑偏场景词污染召回。
 - 增加防跑偏的参考感知召回:允许生成 `DHA 专业科普` 这类和 Brief 绑定的召回词;禁止婚礼、宴会、布景、满月宴、仪式感等跑偏场景词污染召回。
@@ -269,7 +269,7 @@ Validation:
 已知边界:
 已知边界:
 
 
 - 首次组合 live 运行出现过一次短暂 `live_network_error`,重试后通过。后续 runbook 保留 retry/backoff,并建议对单关键词失败做软失败处理。
 - 首次组合 live 运行出现过一次短暂 `live_network_error`,重试后通过。后续 runbook 保留 retry/backoff,并建议对单关键词失败做软失败处理。
-- 未经过真实 provider gate 的能力只能称为合同验证或 mock 验证,不能宣称真实 TikHub、ffmpeg、讯飞 ASR 或豆包视觉已完成上线。
+- 未经过真实 provider gate 的能力只能称为合同验证或 mock 验证,不能宣称真实参考补证、ffmpeg、讯飞 ASR 或豆包视觉已完成上线。
 - 没有真实历史客户选择数据前,不能宣称客户选中率达到 30%/50%,只能使用 `review:metrics` 统计已标注样本。
 - 没有真实历史客户选择数据前,不能宣称客户选中率达到 30%/50%,只能使用 `review:metrics` 统计已标注样本。
 - npm 发布状态必须以 `npm publish` 成功和 registry 验证为准;仅 `npm pack` 或 dry-run 通过时只能称为本地包已重建。
 - npm 发布状态必须以 `npm publish` 成功和 registry 验证为准;仅 `npm pack` 或 dry-run 通过时只能称为本地包已重建。
 
 
@@ -407,7 +407,7 @@ Validation:
 
 
 主要变化:
 主要变化:
 
 
-- TikHub 兼容参考补证和多模态 evidence provider 请求增加 `contractVersion: "tihao-provider-v1"`。
+- 参考补证和多模态 evidence provider 请求增加 `contractVersion: "tihao-provider-v1"`。
 - reference provider 请求带上品牌、品类、平台、关键词和参考信号。
 - reference provider 请求带上品牌、品类、平台、关键词和参考信号。
 - 多模态 evidence provider 请求带上文本、ASR、视觉、帧图证据和风险复核能力。
 - 多模态 evidence provider 请求带上文本、ASR、视觉、帧图证据和风险复核能力。
 - 支持通过运行时 token 为 evidence provider 鉴权。
 - 支持通过运行时 token 为 evidence provider 鉴权。
@@ -427,7 +427,7 @@ Validation:
 - CLI 反馈和剔除项报告:支持保存商务反馈,报告中说明剔除/降级原因。
 - CLI 反馈和剔除项报告:支持保存商务反馈,报告中说明剔除/降级原因。
 - 提号规则与记忆循环加固:更明确地区分首轮假设、硬排除规则和偏好记忆。
 - 提号规则与记忆循环加固:更明确地区分首轮假设、硬排除规则和偏好记忆。
 - 文档和 PM gate 加固:把真实 provider runbook、roadmap、README 链接和“未跑真实 gate 不得宣称 live 完成”的措辞纳入验收。
 - 文档和 PM gate 加固:把真实 provider runbook、roadmap、README 链接和“未跑真实 gate 不得宣称 live 完成”的措辞纳入验收。
-- 参考证据路线:增加 `referenceEnrichmentBaseUrl` / `tikhubToken` provider 接口、`evidenceCards` / `evidenceCardsPath`、`multimodalEvidenceBaseUrl` 和对应验收。
+- 参考证据路线:增加 `referenceEnrichmentBaseUrl` / `socialAnalysisToken` provider 接口、`evidenceCards` / `evidenceCardsPath`、`multimodalEvidenceBaseUrl` 和对应验收。
 - workspace 安装:支持将 Claude Code plugin、skill 和 `.mcp.json` 写入客户工作区。
 - workspace 安装:支持将 Claude Code plugin、skill 和 `.mcp.json` 写入客户工作区。
 - 首个 npm 发布版本曾使用 `@gangvy/tihao-sop`,后续迁移到 `@vocmarket/tihao-sop`。
 - 首个 npm 发布版本曾使用 `@gangvy/tihao-sop`,后续迁移到 `@vocmarket/tihao-sop`。
 
 

+ 4 - 4
claude-code-tihao-sourcing/docs/skill-database-record.json

@@ -1,15 +1,15 @@
 {
 {
   "table": "skills",
   "table": "skills",
-  "updatedAt": "2026-06-17",
+  "updatedAt": "2026-08-14",
   "records": [
   "records": [
     {
     {
       "skillName": "tihao",
       "skillName": "tihao",
       "displayName": "提号",
       "displayName": "提号",
       "status": "published",
       "status": "published",
       "npmPackage": "@vocmarket/tihao",
       "npmPackage": "@vocmarket/tihao",
-      "npmVersion": "1.0.10",
+      "npmVersion": "1.0.19",
       "npmUrl": "https://www.npmjs.com/package/@vocmarket/tihao",
       "npmUrl": "https://www.npmjs.com/package/@vocmarket/tihao",
-      "tarballUrl": "https://registry.npmjs.org/@vocmarket/tihao/-/tihao-1.0.10.tgz",
+      "tarballUrl": "https://registry.npmjs.org/@vocmarket/tihao/-/tihao-1.0.19.tgz",
       "installCommand": "npx --yes @vocmarket/tihao@latest workspace --smoke",
       "installCommand": "npx --yes @vocmarket/tihao@latest workspace --smoke",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "workspaceSkillPath": ".claude/skills/tihao/SKILL.md",
       "workspaceSkillPath": ".claude/skills/tihao/SKILL.md",
@@ -49,7 +49,7 @@
       "displayName": "Fmode 图片分析",
       "displayName": "Fmode 图片分析",
       "status": "local",
       "status": "local",
       "npmPackage": "@vocmarket/tihao",
       "npmPackage": "@vocmarket/tihao",
-      "npmVersion": "1.0.10",
+      "npmVersion": "1.0.19",
       "installCommand": "npx --yes @vocmarket/tihao@latest workspace --smoke",
       "installCommand": "npx --yes @vocmarket/tihao@latest workspace --smoke",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "workspaceSkillPath": ".claude/skills/fmode-image-analysis/SKILL.md",
       "workspaceSkillPath": ".claude/skills/fmode-image-analysis/SKILL.md",

+ 7 - 7
claude-code-tihao-sourcing/docs/tihao-experience-implementation-log.md

@@ -262,7 +262,7 @@
 
 
 ### 本轮推进
 ### 本轮推进
 
 
-- 继续按提号需求主线推进:真实历史 Brief、真实视频 A/B live proof、商务复核和客户效果证明仍是完成条件;本轮没有把 `dha_brief.xlsx`、DHA PoC 推荐表、TikHub 参考分析版、旧 pipeline 或本地 worklist 升格为业务证明。
+- 继续按提号需求主线推进:真实历史 Brief、真实视频 A/B live proof、商务复核和客户效果证明仍是完成条件;本轮没有把 `dha_brief.xlsx`、DHA PoC 推荐表、社媒参考分析版、旧 pipeline 或本地 worklist 升格为业务证明。
 - 复查本地可用材料后确认:`dha_brief.xlsx` 只有 1 个 DHA Brief、参考账号链接和通用规则;缺至少 5 个真实历史 Brief、客户选中/拒绝、拒绝原因、历史人工补号基线和本轮人工补号量,因此仍只能作为 seed,不能关闭 `historical-dataset` 或 `manual-review-and-customer-effect`。
 - 复查本地可用材料后确认:`dha_brief.xlsx` 只有 1 个 DHA Brief、参考账号链接和通用规则;缺至少 5 个真实历史 Brief、客户选中/拒绝、拒绝原因、历史人工补号基线和本轮人工补号量,因此仍只能作为 seed,不能关闭 `historical-dataset` 或 `manual-review-and-customer-effect`。
 - 新增 `evidence:index` 防误判:当 `customer-effect-summary.json` 位于同一个输出目录的 `optimization-pipeline-summary.json` 下时,只有 pipeline 自身 `readyForClaim=true` 且 `proofGapClosure.complete=true/openCount=0`,该 customer-effect 才允许进入 `real_evidence`;否则即使 customer-effect 字段看似通过,也保持 `proofLevel=not_business_proof`,并在 summary 里写出 `pipelineBlocked` 原因。
 - 新增 `evidence:index` 防误判:当 `customer-effect-summary.json` 位于同一个输出目录的 `optimization-pipeline-summary.json` 下时,只有 pipeline 自身 `readyForClaim=true` 且 `proofGapClosure.complete=true/openCount=0`,该 customer-effect 才允许进入 `real_evidence`;否则即使 customer-effect 字段看似通过,也保持 `proofLevel=not_business_proof`,并在 summary 里写出 `pipelineBlocked` 原因。
 - `scripts/evidence-index-smoke.js` 新增回归用例:构造 `pipeline-claim-with-open-closure/customer-effect-audit/customer-effect-summary.json`,其中 customer-effect 自身门槛全为 true,但 pipeline `proofGapClosure.openCount=1`,断言该条仍为 `not_business_proof`,同时 standalone passing customer-effect 仍保持 `real_evidence`。
 - `scripts/evidence-index-smoke.js` 新增回归用例:构造 `pipeline-claim-with-open-closure/customer-effect-audit/customer-effect-summary.json`,其中 customer-effect 自身门槛全为 true,但 pipeline `proofGapClosure.openCount=1`,断言该条仍为 `not_business_proof`,同时 standalone passing customer-effect 仍保持 `real_evidence`。
@@ -316,7 +316,7 @@
 - 当前优化没有偏离提号需求:优化重心仍是把真实历史 Brief、真实视频 A/B、商务复核和客户效果证明放进可审计闭环,而不是把候选名单、worklist、操作包、smoke 或旧 pipeline 结果包装成业务效果。
 - 当前优化没有偏离提号需求:优化重心仍是把真实历史 Brief、真实视频 A/B、商务复核和客户效果证明放进可审计闭环,而不是把候选名单、worklist、操作包、smoke 或旧 pipeline 结果包装成业务效果。
 - 当前到位的是证明链和交接链路的准确性:`proof-gap-closure.openCount=3`、`closedCount=2`,`video-real-candidate` 与 `live-provider-overnight-proof` 已关闭,`video-resource-readiness.readyForVideoAbPreflight=true`、`realCandidateRows=1`。
 - 当前到位的是证明链和交接链路的准确性:`proof-gap-closure.openCount=3`、`closedCount=2`,`video-real-candidate` 与 `live-provider-overnight-proof` 已关闭,`video-resource-readiness.readyForVideoAbPreflight=true`、`realCandidateRows=1`。
 - 当前未到位的是业务效果证明本身:`optimization-completion.complete=false`、`readyForClaim=false`、`proofGapOpenCount=3`、`missingProofRequirementCount=61`,仍不能宣称提号率提升、客户效果达标或人工补号量下降。
 - 当前未到位的是业务效果证明本身:`optimization-completion.complete=false`、`readyForClaim=false`、`proofGapOpenCount=3`、`missingProofRequirementCount=61`,仍不能宣称提号率提升、客户效果达标或人工补号量下降。
-- 本轮追加搜证确认:`dha_brief.xlsx`、DHA PoC 推荐表和 TikHub 参考分析版仍只能作为种子材料;当前统一 intake 模板仍有示例/占位,历史数据不足 5 个真实 Brief,也缺客户选择/拒绝记录、拒绝原因、历史人工补号基线和本轮人工补号量。
+- 本轮追加搜证确认:`dha_brief.xlsx`、DHA PoC 推荐表和社媒参考分析版仍只能作为种子材料;当前统一 intake 模板仍有示例/占位,历史数据不足 5 个真实 Brief,也缺客户选择/拒绝记录、拒绝原因、历史人工补号基线和本轮人工补号量。
 - 本轮追加验证确认:旧 video A/B 产物虽出现 `acceptance.passed=true`,但 `proofContext.mode`、`proofContext.collectionMode` 和 `proofContext.generatedBy` 仍为 `unknown`;本地环境只发现 `ANTHROPIC_AUTH_TOKEN`,直接运行 `npm.cmd run acceptance:video-ab` 因缺 `TIHAO_SESSION_TOKEN` 等运行凭证失败,不能关闭真实视频 A/B live proof。
 - 本轮追加验证确认:旧 video A/B 产物虽出现 `acceptance.passed=true`,但 `proofContext.mode`、`proofContext.collectionMode` 和 `proofContext.generatedBy` 仍为 `unknown`;本地环境只发现 `ANTHROPIC_AUTH_TOKEN`,直接运行 `npm.cmd run acceptance:video-ab` 因缺 `TIHAO_SESSION_TOKEN` 等运行凭证失败,不能关闭真实视频 A/B live proof。
 
 
 ### 本轮修正
 ### 本轮修正
@@ -557,7 +557,7 @@
 
 
 ### 本轮修正
 ### 本轮修正
 
 
-- 新增 `local-seed-material:index`,把 `../dha_brief.xlsx`、`../output/dha-tihao-poc/AI提号-DHA小红书推荐名单-PoC.xlsx` 和 `../output/dha-tihao-poc/AI提号-DHA小红书推荐名单-TikHub参考分析版.xlsx` 做成本地种子材料索引。
+- 新增 `local-seed-material:index`,把 `../dha_brief.xlsx`、`../output/dha-tihao-poc/AI提号-DHA小红书推荐名单-PoC.xlsx` 和 `../output/dha-tihao-poc/AI提号-DHA小红书推荐名单-社媒参考分析版.xlsx` 做成本地种子材料索引。
 - 索引输出 `outputs/local-seed-material-index-latest/local-seed-material-index-summary.json`、Markdown 和 CSV,当前识别 `existingMaterialCount=3`、`workbookCount=3`、`promisingLiveAggregateCount=1`,并强制保留 `proofLevel=not_business_proof`、`directCustomerProof=false`、`canCloseProofGap=false`。
 - 索引输出 `outputs/local-seed-material-index-latest/local-seed-material-index-summary.json`、Markdown 和 CSV,当前识别 `existingMaterialCount=3`、`workbookCount=3`、`promisingLiveAggregateCount=1`,并强制保留 `proofLevel=not_business_proof`、`directCustomerProof=false`、`canCloseProofGap=false`。
 - XLSX 解析兼容带命名空间前缀和绝对 workbook relationship 的工作簿,当前能读出 DHA Brief 的 `brief/通用提号规则/参考媒体信息`,以及 PoC 表的 `结论/主推账号/备选复核/参考账号解析/Brief与方法` 等 sheet、行数和表头。
 - XLSX 解析兼容带命名空间前缀和绝对 workbook relationship 的工作簿,当前能读出 DHA Brief 的 `brief/通用提号规则/参考媒体信息`,以及 PoC 表的 `结论/主推账号/备选复核/参考账号解析/Brief与方法` 等 sheet、行数和表头。
 - `evidence:index` 新增 `local-seed-material-index` 分类,固定为 `not_business_proof`;`round:refresh` 新增 `local-seed-material-index` 步骤,位于 `optimization-handoff` 和 `evidence-index` 之间,因此每轮证据台账会读到最新种子材料索引。
 - `evidence:index` 新增 `local-seed-material-index` 分类,固定为 `not_business_proof`;`round:refresh` 新增 `local-seed-material-index` 步骤,位于 `optimization-handoff` 和 `evidence-index` 之间,因此每轮证据台账会读到最新种子材料索引。
@@ -572,7 +572,7 @@
 
 
 ### 当前边界
 ### 当前边界
 
 
-- 本轮只让本地 DHA Brief、PoC 推荐表、TikHub 参考分析版和旧 pipeline/live 自动化候选可发现,方便商务/投放转写真实模板和补真实候选视频;不补真实客户反馈,也不关闭 proof-gap。
+- 本轮只让本地 DHA Brief、PoC 推荐表、社媒参考分析版和旧 pipeline/live 自动化候选可发现,方便商务/投放转写真实模板和补真实候选视频;不补真实客户反馈,也不关闭 proof-gap。
 - 当前仍为 `proof-gap-closure.openCount=5`、`optimization-completion.readyForClaim=false`、`longrun:readiness.ready=false`。
 - 当前仍为 `proof-gap-closure.openCount=5`、`optimization-completion.readyForClaim=false`、`longrun:readiness.ready=false`。
 - 可以说“本地种子材料已纳入可复验索引并进入证据台账”;不能说提号率已提升、客户效果已达标或人工补号量已下降。
 - 可以说“本地种子材料已纳入可复验索引并进入证据台账”;不能说提号率已提升、客户效果已达标或人工补号量已下降。
 
 
@@ -1660,7 +1660,7 @@ npm run overnight:quality
 - `runs=2`
 - `runs=2`
 - `failures=0`
 - `failures=0`
 - `gatePass=true`
 - `gatePass=true`
-- 参考补证状态显示为 `needs_tikhub_enrichment`,不会误报真实补证完成。
+- 参考补证状态显示为 `needs_social_analysis_enrichment`,不会误报真实补证完成。
 
 
 完整验收:
 完整验收:
 
 
@@ -1691,7 +1691,7 @@ npm run acceptance
 
 
 - `TIHAO_GATE_REQUIRE_REFERENCE_PROVIDER=true`
 - `TIHAO_GATE_REQUIRE_REFERENCE_PROVIDER=true`
   - `reference-account` 策略的参考补证状态必须为 `ok`。
   - `reference-account` 策略的参考补证状态必须为 `ok`。
-  - 没有真实参考补证 provider、VOC social 视频详情或 TikHub 补证时会失败。
+  - 没有真实参考补证 provider 或 VOC social 视频详情时会失败。
 - `TIHAO_GATE_REQUIRE_HOMEPAGE_PROVIDER=true`
 - `TIHAO_GATE_REQUIRE_HOMEPAGE_PROVIDER=true`
   - `homepage-evidence` 策略的主页证据状态必须为 `ok`。
   - `homepage-evidence` 策略的主页证据状态必须为 `ok`。
   - 只使用 fallback/标签轻量判断时会失败。
   - 只使用 fallback/标签轻量判断时会失败。
@@ -1730,7 +1730,7 @@ npm run overnight:quality
 - 输出目录:`outputs/overnight-quality-1780734793371`
 - 输出目录:`outputs/overnight-quality-1780734793371`
 - `gatePass=false`
 - `gatePass=false`
 - 失败门禁:`reference-provider-ok`
 - 失败门禁:`reference-provider-ok`
-- 当前值:`needs_tikhub_enrichment`
+- 当前值:`needs_social_analysis_enrichment`
 - 目标:`ok`
 - 目标:`ok`
 
 
 严格主页 provider 门禁:
 严格主页 provider 门禁:

+ 1 - 1
claude-code-tihao-sourcing/docs/tihao-experience-optimization-plan.md

@@ -140,7 +140,7 @@ npm run history:audit -- --input <history-dataset目录> --output <审计输出
 
 
 ### 视频和多模态
 ### 视频和多模态
 
 
-- 有参考视频时,必须尝试 VOC social/TikHub 视频详情和豆包视频分析。
+- 有参考视频时,必须尝试 VOC social 视频详情和豆包视频分析。
 - 拿到视频 URL、封面、字幕、ASR、帧图或正文后,才能声明视频分析完成。
 - 拿到视频 URL、封面、字幕、ASR、帧图或正文后,才能声明视频分析完成。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - A/B 测试中,`video-enhanced` 不得低于 baseline:
 - A/B 测试中,`video-enhanced` 不得低于 baseline:

+ 1 - 1
claude-code-tihao-sourcing/docs/tihao-experience-optimization-plan.zh-CN.md

@@ -140,7 +140,7 @@ npm run history:audit -- --input <history-dataset目录> --output <审计输出
 
 
 ### 视频和多模态
 ### 视频和多模态
 
 
-- 有参考视频时,必须尝试 VOC social/TikHub 视频详情和豆包视频分析。
+- 有参考视频时,必须尝试 VOC social 视频详情和豆包视频分析。
 - 拿到视频 URL、封面、字幕、ASR、帧图或正文后,才能声明视频分析完成。
 - 拿到视频 URL、封面、字幕、ASR、帧图或正文后,才能声明视频分析完成。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - A/B 测试中,`video-enhanced` 不得低于 baseline:
 - A/B 测试中,`video-enhanced` 不得低于 baseline:

+ 2 - 2
claude-code-tihao-sourcing/docs/tihao-handoff-index.md

@@ -9,7 +9,7 @@
 - 2026-06-13 1.0.2 边界确认:三缺口材料审计当前 `gapCount=3`、`materialFoundCount=3`、`openGapCount=3`、`canCloseProofGap=false`。这只说明项目材料已找到并接入审计链路,不关闭业务证明缺口。
 - 2026-06-13 1.0.2 边界确认:三缺口材料审计当前 `gapCount=3`、`materialFoundCount=3`、`openGapCount=3`、`canCloseProofGap=false`。这只说明项目材料已找到并接入审计链路,不关闭业务证明缺口。
 - 2026-06-13 1.0.1 补丁发布完成:`@vocmarket/tihao-sop@1.0.1` 已发布到 npm,发布时 registry 已验证 `dist-tags.latest=1.0.1`,fresh `npx --yes @vocmarket/tihao-sop@latest workspace --smoke` 已通过;当前 latest 已由 1.0.2 接管。
 - 2026-06-13 1.0.1 补丁发布完成:`@vocmarket/tihao-sop@1.0.1` 已发布到 npm,发布时 registry 已验证 `dist-tags.latest=1.0.1`,fresh `npx --yes @vocmarket/tihao-sop@latest workspace --smoke` 已通过;当前 latest 已由 1.0.2 接管。
 - 2026-06-13 1.0.1 补丁推进:新增 `real-gap:materials` 三缺口材料审计,当前输出 `gapCount=3`、`materialFoundCount=3`、`openGapCount=3`、`canCloseProofGap=false`。这证明项目资料已找到并接入审计底稿,但三个真实业务证明缺口仍不能关闭。
 - 2026-06-13 1.0.1 补丁推进:新增 `real-gap:materials` 三缺口材料审计,当前输出 `gapCount=3`、`materialFoundCount=3`、`openGapCount=3`、`canCloseProofGap=false`。这证明项目资料已找到并接入审计底稿,但三个真实业务证明缺口仍不能关闭。
-- 2026-06-13 三缺口真实资料核对:`historical-dataset` 已连接 DHA Brief/PoC/TikHub 工作簿、`historyDraftRows=12` 和逐字稿中的 DHA 提报/未反馈事实;`video-ab-live-proof` 已连接 `videoResourceReady=true`、`realCandidateRows=1`、`realReferenceRows=1` 和 15 条候选视频 note 线索;`manual-review-and-customer-effect` 已连接 `manualReviewDraftRows=12`、客户选中率目标和人工复核经验规则。
+- 2026-06-13 三缺口真实资料核对:`historical-dataset` 已连接 DHA Brief/PoC/参考分析工作簿、`historyDraftRows=12` 和逐字稿中的 DHA 提报/未反馈事实;`video-ab-live-proof` 已连接 `videoResourceReady=true`、`realCandidateRows=1`、`realReferenceRows=1` 和 15 条候选视频 note 线索;`manual-review-and-customer-effect` 已连接 `manualReviewDraftRows=12`、客户选中率目标和人工复核经验规则。
 - 2026-06-13 仍未关闭的真实字段:历史缺口仍缺 `briefCount>=5`、客户选中/拒绝、拒绝原因、历史人工补号量;video A/B 仍缺 live `proofContext.generatedBy=acceptance:video-ab`、runtime credential、company、VOC social provider、video analysis provider;客户效果仍缺真实人工复核标签、客户选择、反馈原因、本轮人工补号量和通过的 history audit。
 - 2026-06-13 仍未关闭的真实字段:历史缺口仍缺 `briefCount>=5`、客户选中/拒绝、拒绝原因、历史人工补号量;video A/B 仍缺 live `proofContext.generatedBy=acceptance:video-ab`、runtime credential、company、VOC social provider、video analysis provider;客户效果仍缺真实人工复核标签、客户选择、反馈原因、本轮人工补号量和通过的 history audit。
 - 2026-06-13 1.0 正式发布完成:`@vocmarket/tihao-sop@1.0.0` 已发布到 npm,发布时 registry 已验证 `dist-tags.latest=1.0.0`,`npx --yes @vocmarket/tihao-sop@latest workspace --smoke` 已通过;当前 latest 已由 1.0.2 接管。
 - 2026-06-13 1.0 正式发布完成:`@vocmarket/tihao-sop@1.0.0` 已发布到 npm,发布时 registry 已验证 `dist-tags.latest=1.0.0`,`npx --yes @vocmarket/tihao-sop@latest workspace --smoke` 已通过;当前 latest 已由 1.0.2 接管。
 - 2026-06-13 资料桥接补强:项目资料已被找到并接入 `local-seed-to-intake-worklist`,最新 `proof-gap:closure` open 行带有 `materialBridge`。`historical-dataset` 连接 `history-intake-draft.csv` 12 行,`video-ab-live-proof` 连接 `video-resource-worklist.csv` 且保留 4 条参考种子与 3 条候选视频种子,`manual-review-and-customer-effect` 连接 `manual-review-draft.csv` 12 行。
 - 2026-06-13 资料桥接补强:项目资料已被找到并接入 `local-seed-to-intake-worklist`,最新 `proof-gap:closure` open 行带有 `materialBridge`。`historical-dataset` 连接 `history-intake-draft.csv` 12 行,`video-ab-live-proof` 连接 `video-resource-worklist.csv` 且保留 4 条参考种子与 3 条候选视频种子,`manual-review-and-customer-effect` 连接 `manual-review-draft.csv` 12 行。
@@ -59,7 +59,7 @@
 | 3 | 最新表单索引 | `outputs/latest-form-index-latest/latest-form-index.md` | 从一个入口定位补证表单、候选名单、字段级核对表、负责人行动文件、下一步行动队列和复验命令。 |
 | 3 | 最新表单索引 | `outputs/latest-form-index-latest/latest-form-index.md` | 从一个入口定位补证表单、候选名单、字段级核对表、负责人行动文件、下一步行动队列和复验命令。 |
 | 4 | 真实验收材料总包 | `outputs/real-proof-intake-bundle-latest/real-proof-intake-bundle.md` | 给商务和技术集中补齐真实材料,包含模板、证明缺口、当前证据、下一步动作、负责人附件和复验命令。 |
 | 4 | 真实验收材料总包 | `outputs/real-proof-intake-bundle-latest/real-proof-intake-bundle.md` | 给商务和技术集中补齐真实材料,包含模板、证明缺口、当前证据、下一步动作、负责人附件和复验命令。 |
 | 5 | 真实证明闭环工单 | `outputs/real-proof-closure-work-order-latest/real-proof-closure-work-order.md` | 把 intake、proof-gap、completion 未关闭项拆成 13 条字段级补证工单和 2 份负责人附件;展示当前证据和缺失证明要求,不写入正式模板,不关闭 proof-gap。 |
 | 5 | 真实证明闭环工单 | `outputs/real-proof-closure-work-order-latest/real-proof-closure-work-order.md` | 把 intake、proof-gap、completion 未关闭项拆成 13 条字段级补证工单和 2 份负责人附件;展示当前证据和缺失证明要求,不写入正式模板,不关闭 proof-gap。 |
-| 6 | 本地种子材料索引 | `outputs/local-seed-material-index-latest/local-seed-material-index.md` | 查看 `dha_brief.xlsx`、DHA PoC 推荐表、TikHub 参考分析版和旧 pipeline/live 自动化候选;只作为补真实模板和候选池种子。 |
+| 6 | 本地种子材料索引 | `outputs/local-seed-material-index-latest/local-seed-material-index.md` | 查看 `dha_brief.xlsx`、DHA PoC 推荐表、社媒参考分析版和旧 pipeline/live 自动化候选;只作为补真实模板和候选池种子。 |
 | 7 | 本地种子转 intake 补表 worklist | `outputs/local-seed-to-intake-worklist-latest/local-seed-to-intake-worklist.md` | 把 DHA Brief、PoC 候选和参考账号解析转成商务可补正式 intake 的草稿行。 |
 | 7 | 本地种子转 intake 补表 worklist | `outputs/local-seed-to-intake-worklist-latest/local-seed-to-intake-worklist.md` | 把 DHA Brief、PoC 候选和参考账号解析转成商务可补正式 intake 的草稿行。 |
 | 8 | 经验逐字稿索引 | `outputs/experience-transcript-index-latest/experience-transcript-index.md` | 查看提号经验逐字稿覆盖的 8 条经验规则;只作为经验来源追踪和规则校准种子。 |
 | 8 | 经验逐字稿索引 | `outputs/experience-transcript-index-latest/experience-transcript-index.md` | 查看提号经验逐字稿覆盖的 8 条经验规则;只作为经验来源追踪和规则校准种子。 |
 | 9 | 三缺口材料审计 | `outputs/real-gap-material-audit-latest/real-gap-material-audit.md` | 查看三个真实缺口对应的项目资料、已接产物、仍缺字段和不能关闭原因。 |
 | 9 | 三缺口材料审计 | `outputs/real-gap-material-audit-latest/real-gap-material-audit.md` | 查看三个真实缺口对应的项目资料、已接产物、仍缺字段和不能关闭原因。 |

+ 78 - 0
claude-code-tihao-sourcing/docs/tihao-material-driven-upgrade.md

@@ -0,0 +1,78 @@
+# 提号技能包资料驱动升级交接(2026-08-14)
+
+本文记录本轮「根据《20260813 - AI提号规则及喂养资料》对技能包做升级迭代」的完整交付。命令名、字段名、包名保留英文,因为它们是实际运行契约。
+
+## 边界(本轮红线)
+
+只开发技能包本身,不建数据库/接口。字段类资料只「结构化沉淀字段定义 + 建表 DDL + 选号查询映射」,供未来建库/开发查询接口时直接消费;不在此建库、不写爬虫、不开发查询接口。
+
+## 资料来源 → 产出 → 接入 对照
+
+| # | 资料来源 | 产出模块 | 接入点 | 报告展示 |
+| --- | --- | --- | --- | --- |
+| 1 | 《历史选号案例/case_template.xlsx》 | `scripts/case-template-import.js` | fewshot-cases.json + case-insights.md + history-data-template.csv | 历史相似案例参照 |
+| 2 | 《01-资源筛选标准(更新版).docx》 | `platform-quality-rules.js` | `ranker.js` scoreDataQuality | 数据质量分/层级/待补证 |
+| 3 | 《field_dictionary.md》 | `pgy-schema.js` | 独立 schema 模块(供未来建库) | docs 产物 |
+| 4 | 《media_library_template.xlsx》表头 | `media-library-schema.js` | 独立 schema 模块(供未来建库) | docs 产物 |
+| 5 | 《01-资源寻找方法(更新版).docx》STEP06 | `goal-strategy.js` | `experience-rules.js` | 投放目标与提号策略 |
+| 6 | 《02-项目执行SOP流程(优化版).xlsx》合规白名单 | `compliance-rules.js` | `experience-rules.js` | 内容合规白名单 |
+| 7 | 《AI提号需求列表.xlsx》13 个真实 brief | `brief-rule-patterns.js` | `brief-parser.js` | 真实案例规则模式命中 + 数据要求 |
+
+## 新增模块清单
+
+### 规则系统(喂 AI 提升选中率)
+
+- `mcp/src/features/tihao-sourcing/platform-quality-rules.js` — 分平台量化阈值:抖音 6 档 / 小红书 4 档达人层级 + 互动率/完播率/赞藏比/评分/垂直度/商业占比的合格线、优秀线、危险线;`evaluateCreatorDataQuality` 返回 tier/score/checks/missingMetrics。已接入 ranker 的 `scoreDataQuality`。
+- `mcp/src/features/tihao-sourcing/goal-strategy.js` — 4 类投放目标(种草转化/品牌曝光/搜索引流/活动冲量)→ 账号类型映射;3 条提号规则(默认提号数量 = 需求数量 3 倍以上、前 10 篇 80% 风格匹配、优先响应明确数量)。
+- `mcp/src/features/tihao-sourcing/compliance-rules.js` — 内容合规白名单:8 组高危词替换(美白→提亮肤色、祛痘→舒缓痘痘、平替→高性价比、特效/神器→体验感、第一/最好→领先/专研、根治→有助于)+ 3 平台特规(小红书/抖音/B站)+ 9 个安全词 + 审核流程。
+- `mcp/src/features/tihao-sourcing/brief-rule-patterns.js` — 13 个真实 brief 案例的结构化规则 + 8 个可复用正则模式(CPC/CPV/近3条播放/年龄占比/低龄占比/兴趣标签/达人IP/同省不跨区)。已增强 `brief-parser.js` 的 `resolvePerformanceRequirements`。
+
+### 案例系统(案例 → 可复用知识)
+
+- `scripts/case-template-import.js` — 零依赖 xlsx 读取 + 案例映射 + 归因洞察 + 规则建议(信号 ≥2 次才建议固化)。
+- `mcp/src/features/tihao-sourcing/few-shot-cases.js` — 运行时加载 `fewshot-cases.json`,按品类/平台/投放目标匹配历史正/负样本,附加到 criteria。
+
+### 数据库字段沉淀(供未来建库,不建库)
+
+- `mcp/src/features/tihao-sourcing/pgy-schema.js` — 蒲公英博主字段:9 采集接口 + 14 选号维度 + 45 核心字段 + 9 索引 + DDL + 查询映射。
+- `mcp/src/features/tihao-sourcing/media-library-schema.js` — 公司媒体资源库字段:35 字段 + 8 必填 + 10 商单交付侧人工补录字段 + 12 个与蒲公英对账别名 + 7 索引 + DDL + 查询映射。
+
+## 新增 npm scripts
+
+| 命令 | 作用 |
+| --- | --- |
+| `npm run case:import` | 案例模板 → few-shot + 归因 + 历史数据模板 |
+| `npm run case:import:smoke` | 案例导入器 smoke |
+| `npm run platform:quality-rules:smoke` | 分平台阈值 smoke |
+| `npm run goal:compliance:smoke` | 投放目标策略 + 合规白名单 smoke |
+| `npm run brief:patterns` | 生成 brief 规则模式 JSON |
+| `npm run brief:patterns:smoke` | 规则模式库 smoke |
+| `npm run fewshot:smoke` | few-shot 运行时接入 smoke |
+| `npm run pgy:schema` / `pgy:schema:smoke` | 蒲公英 schema 生成 / smoke |
+| `npm run media:schema` / `media:schema:smoke` | 媒体资源库 schema 生成 / smoke |
+
+## 生成的 docs 产物
+
+- `docs/pgy-field-schema.json` / `docs/pgy-collection-ddl.txt` / `docs/pgy-sourcing-query-map.md`
+- `docs/media-library-field-schema.json` / `docs/media-library-collection-ddl.txt` / `docs/media-library-query-map.md`
+- `docs/brief-rule-patterns.json`(13 案例 + 8 模式)
+- `docs/20260813 - AI提号规则及喂养资料/历史选号案例/case-import/fewshot-cases.json`(当前 2 条真实案例,随 `case:import` 每次刷新)
+
+## 验证结果
+
+- 8 个 smoke 全部通过:`media-library-schema`、`goal-compliance-rules`、`brief-rule-patterns`、`few-shot-cases`、`platform-quality-rules`、`pgy-schema`、`case-template-import`、`candidate-pool`。
+- `npm run mcp:smoke` 通过。
+- sample 端到端报告新增 4 个 section 正常渲染:`投放目标与提号策略`、`历史相似案例参照`、`内容合规白名单`、`真实案例规则模式命中`(命中条件渲染)。
+
+## Review 后修复(2026-08-14 二轮)
+
+1. **提号数量口径统一为 1.5 倍**:`report.js` 的 `getReviewPoolTarget` 维持 `ceil(targetCount*1.5)`(无目标默认 10);`goal-strategy.js` 的提号规则从「3 倍」调整为「1.5 倍」,报告「提号数量」提示与候选池口径一致(暂不启用 3 倍)。
+2. **达人 IP 正则收窄**:`brief-rule-patterns.js` 的 `ipMustBe` 加负向前瞻排除「授权/联名/形象/周边/运营」,避免「品牌 IP 授权/联名」误报;「达人 IP 必须为宁夏」「IP 要求山东」仍正常命中。
+3. **few-shot 案例库打包闭环**:案例库固化到技能包内 `docs/case-library/fewshot-cases.json`(`docs/` 已在 package.json `files` 里,随 npm 包分发);`case:import` 双写(资料目录留档 + 包内打包位置);`few-shot-cases.js` 的 `DEFAULT_PATHS` 优先读包内路径。npx 安装后运行时能稳定加载历史案例,不再依赖开发者本地工作区路径。
+
+## 后续可做(仍在技能包边界内)
+
+1. 按模板持续补充《历史选号案例》,每次补完跑 `npm run case:import`,few-shot 正/负样本会同步增长并提升相似案例参照命中率。
+2. 规则模式库 `brief-rule-patterns.js` 的 `PATTERN_LIBRARY` 可持续追加新表达;新案例里的数据要求表达式人工复核后可回填。
+3. `goal-strategy.js` 的投放目标 signals、`compliance-rules.js` 的高危词表可随客户反馈迭代。
+4. 未来要建库时,直接消费 `pgy-schema.js` / `media-library-schema.js` 的 `CORE_FIELDS` / `INDEXES` / `buildDdl()` / `buildQueryMap()`。

+ 22 - 0
claude-code-tihao-sourcing/mcp/catalog/params/douyin.json

@@ -0,0 +1,22 @@
+{
+  "douyin.search_author_square": [
+    { "name": "keyword", "in": "query", "type": "string", "required": true, "desc": "搜索关键词,如「母婴 测评」「DHA」。" },
+    { "name": "page", "in": "query", "type": "number", "required": false, "default": 1, "desc": "页码,从 1 开始。" },
+    { "name": "searchType", "in": "query", "type": "string", "required": false, "default": "CONTENT", "desc": "搜索类型,达人广场内容检索用 CONTENT。" },
+    { "name": "followerRange", "in": "query", "type": "string", "required": false, "desc": "粉丝量级区间(单位:万),如「5-50」。" },
+    { "name": "company", "in": "query", "type": "string", "required": false, "desc": "公司/企业号 id,由 token 对应账号自动解析时可不传。" },
+    { "name": "isRefresh", "in": "query", "type": "boolean", "required": false, "desc": "是否强制刷新缓存结果。" }
+  ],
+  "douyin.author_cp_info": [
+    { "name": "oAuthorId", "in": "query", "type": "string", "required": true, "desc": "达人 id,取自搜索结果项的 star_id。" }
+  ],
+  "douyin.author_convert_ability": [
+    { "name": "oAuthorId", "in": "query", "type": "string", "required": true, "desc": "达人 id,取自搜索结果项的 star_id。" }
+  ],
+  "douyin.author_marketing_info": [
+    { "name": "oAuthorId", "in": "query", "type": "string", "required": true, "desc": "达人 id,取自搜索结果项的 star_id。" }
+  ],
+  "douyin.author_order_experience": [
+    { "name": "oAuthorId", "in": "query", "type": "string", "required": true, "desc": "达人 id,取自搜索结果项的 star_id。" }
+  ]
+}

+ 16 - 0
claude-code-tihao-sourcing/mcp/catalog/params/xiaohongshu.json

@@ -0,0 +1,16 @@
+{
+  "xiaohongshu.search_blogger": [
+    { "name": "keyword", "in": "query", "type": "string", "required": true, "desc": "搜索关键词,如「母婴 育儿」「敏感肌」。" },
+    { "name": "page", "in": "query", "type": "number", "required": false, "default": 1, "desc": "页码,从 1 开始。" },
+    { "name": "searchType", "in": "query", "type": "string", "required": false, "default": "NOTE", "desc": "搜索类型,笔记检索用 NOTE。" },
+    { "name": "fansNumberLower", "in": "query", "type": "number", "required": false, "desc": "粉丝数下限(绝对值,非万)。" },
+    { "name": "fansNumberUpper", "in": "query", "type": "number", "required": false, "desc": "粉丝数上限(绝对值,非万)。" },
+    { "name": "gender", "in": "query", "type": "string", "required": false, "desc": "性别筛选:FEMALE / MALE / ALL。" }
+  ],
+  "xiaohongshu.kol_cost_effective": [
+    { "name": "userId", "in": "query", "type": "string", "required": true, "desc": "博主 id,取自搜索结果项的 userId。" }
+  ],
+  "xiaohongshu.kol_core_data": [
+    { "name": "userId", "in": "query", "type": "string", "required": true, "desc": "博主 id,取自搜索结果项的 userId。" }
+  ]
+}

+ 1452 - 0
claude-code-tihao-sourcing/mcp/catalog/voc-e-commerce-endpoints.json

@@ -0,0 +1,1452 @@
+{
+  "version": "1.2.0",
+  "updatedAt": "2026-07-03",
+  "gateway": {
+    "ecommerce": {
+      "baseUrl": "https://server.fmode.cn/api/voc-e-commerce",
+      "auth": "Authorization: Bearer <NewAPI fmode-api token(sk- 开头,优先)或平台 sessionToken(回退)>",
+      "upstream": "公司 voc-e-commerce 代理,按 proxyPath 透传到电商达人数据上游,对外不暴露任何上游供应商名称。",
+      "billing": "每次成功调用按平台规则计费一次;计费优先走 NewAPI(fmode-api)额度,sk- token 鉴权失败时自动回退平台 sessionToken。402=余额不足(引导 fmode-api 充值),401=token 缺失/失效,403=账号禁用/无权限。",
+      "note": "这是一个通用转发网关:清单内已登记抖音/小红书达人搜索与详情接口;清单里没有的接口可直接用 tihao_api_call 传 rawPath + method + params 调用。",
+      "rawPathHint": "调用未登记接口时,把 proxyPath 传给 tihao_api_call 的 rawPath,例如 douyin-xingtu/gw/api/data_sp/author_cp_info/v1,并按对应平台参数规则拼 query/body。"
+    },
+    "social": {
+      "baseUrl": "https://server.fmode.cn/api/voc-social",
+      "auth": "Authorization: Bearer <NewAPI fmode-api token(sk- 开头,优先)或平台 sessionToken(回退)>",
+      "upstream": "公司 voc-social 中转网关(全量代理,按 proxyPath 透传到社媒数据上游),对外不暴露任何上游供应商名称。",
+      "billing": "每次成功调用计费一次;计费与错误处理同电商网关(优先 fmode-api,401 回退 sessionToken)。",
+      "note": "小红书素人/非蒲公英(笔记/用户/评论/话题搜索与详情)和抖音素人/开放端(用户/视频搜索、主页/作品/视频/评论详情、分享链接与主页链接解析、星图 kolId 转换)走这个网关。endpoint 带 channel=\"social\" 时 tihao_api_call 会自动选用本网关;未登记接口可传 rawPath + channel=\"social\" 调用。",
+      "rawPathHint": "调用未登记的社媒接口时,把 proxyPath 传给 tihao_api_call 的 rawPath 并加 channel=\"social\",例如 xiaohongshu/app/search_notes。"
+    }
+  },
+  "platforms": [
+    {
+      "key": "douyin",
+      "label": "抖音星图",
+      "channel": "ecommerce",
+      "status": "verified",
+      "idField": "star_id",
+      "detailParam": "oAuthorId"
+    },
+    {
+      "key": "xiaohongshu",
+      "label": "小红书蒲公英(PGY 商单博主)",
+      "channel": "ecommerce",
+      "status": "verified",
+      "idField": "userId",
+      "detailParam": "userId"
+    },
+    {
+      "key": "xiaohongshu",
+      "label": "小红书素人/非蒲公英(笔记·用户·评论)",
+      "channel": "social",
+      "status": "verified",
+      "idField": "user_id",
+      "detailParam": "user_id"
+    },
+    {
+      "key": "douyin",
+      "label": "抖音素人/开放端(搜索·主页·视频·评论·链接解析)",
+      "channel": "social",
+      "status": "verified",
+      "idField": "sec_user_id",
+      "detailParam": "sec_user_id"
+    }
+  ],
+  "workflow": "电商达人(蒲公英/星图,channel=ecommerce):第一波搜索拿达人 id(抖音 star_id、小红书 userId),第二波按需调详情接口补 CPM/CPE/商单/中位数。小红书素人/非蒲公英(channel=social,走 voc-social 全量代理):用 xiaohongshu.search_notes 搜笔记、search/web_v3 搜用户,再用 note_detail/user_info/user_notes/note_comments 取详情。抖音素人/开放端(channel=social,同走 voc-social):用 douyin.search_user / douyin.search_video 按关键词搜账号与视频;参考账号/参考视频解析用 douyin.get_sec_user_id(主页链接→sec_user_id)、douyin.fetch_one_video_by_share_url(分享链接→视频+作者),再用 douyin.user_profile / douyin.user_posts / douyin.video_detail / douyin.video_comments 补主页与近作证据;需要接回星图详情线时用 douyin.xingtu.get_xingtu_kolid_by_sec_user_id 或 get_xingtu_kolid_by_unique_id 把抖音账号换成星图 kolId。tihao_api_call 按 endpoint 的 channel 自动选网关;两条线计费都优先 NewAPI(fmode-api)sk- token,鉴权失败回退平台 sessionToken。",
+  "endpoints": [
+    {
+      "id": "douyin.search_author_square",
+      "platform": "douyin",
+      "wave": "search",
+      "title": "抖音星图达人广场搜索",
+      "summary": "按关键词搜索抖音星图达人,返回达人列表(含 star_id、昵称、粉丝数、报价、标签等基础信息)。搜索结果不含 CPM/CPE/商单等绩效指标,需用详情接口补。返回项中的 star_id 即详情接口所需的 oAuthorId。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin-xingtu/gw/api/gsearch/search_for_author_square/v1",
+      "requiredParams": [
+        "keyword"
+      ],
+      "responseHint": "data.model.itemList[],每项含 star_id(→ oAuthorId)、name、follower、报价、标签。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "星图",
+        "搜索",
+        "达人",
+        "search",
+        "first-wave"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "douyin.author_cp_info",
+      "platform": "douyin",
+      "wave": "detail",
+      "title": "抖音星图达人 CPM/CPE 预期",
+      "summary": "返回单个达人的图文/视频预期 CPM、CPE、预期播放等投放性价比指标。入参 oAuthorId 取自搜索结果的 star_id。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin-xingtu/gw/api/data_sp/author_cp_info/v1",
+      "requiredParams": [
+        "oAuthorId"
+      ],
+      "responseHint": "expect_cpm / expect_cpe / expect_vv / hot_item 等预期投放指标。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "星图",
+        "cpm",
+        "cpe",
+        "详情",
+        "detail",
+        "second-wave",
+        "绩效"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "douyin.author_convert_ability",
+      "platform": "douyin",
+      "wave": "detail",
+      "title": "抖音星图达人转化能力",
+      "summary": "返回单个达人的 GPM 区间、视频播放中位数、关联视频数、推荐商品数等转化能力指标。入参 oAuthorId 取自搜索结果的 star_id。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin-xingtu/gw/api/data_sp/get_author_convert_ability/v1",
+      "requiredParams": [
+        "oAuthorId"
+      ],
+      "responseHint": "gpm_range / video_vv_median(播放中位数)/ related_video_cnt / rec_product_cnt 等。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "星图",
+        "转化",
+        "中位数",
+        "gpm",
+        "详情",
+        "detail",
+        "second-wave",
+        "绩效"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "douyin.author_marketing_info",
+      "platform": "douyin",
+      "wave": "detail",
+      "title": "抖音星图达人营销/报价信息",
+      "summary": "返回单个达人的分时长报价、行业标签、活动信息、榜单排名等营销信息。入参 oAuthorId 取自搜索结果的 star_id。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin-xingtu/gw/api/author/get_author_marketing_info/v1",
+      "requiredParams": [
+        "oAuthorId"
+      ],
+      "responseHint": "price_info[](分时长报价)/ industry_tags / activity_info / hot_list_ranks。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "星图",
+        "报价",
+        "营销",
+        "标签",
+        "详情",
+        "detail",
+        "second-wave"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "douyin.author_order_experience",
+      "platform": "douyin",
+      "wave": "detail",
+      "title": "抖音星图达人商单/合作经验",
+      "summary": "返回单个达人的商单合作行业经验等指标,可用于推断商单数/合作活跃度。入参 oAuthorId 取自搜索结果的 star_id。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin-xingtu/gw/api/aggregator/get_author_order_experience/v1",
+      "requiredParams": [
+        "oAuthorId"
+      ],
+      "responseHint": "order_experience_industries[](商单合作行业经验)。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "星图",
+        "商单",
+        "合作",
+        "经验",
+        "详情",
+        "detail",
+        "second-wave"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "douyin.search_user",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "search",
+      "title": "抖音用户搜索",
+      "summary": "按关键词搜索抖音用户/账号(素人/开放端,非星图)。用于按行业词/账号名找抖音达人或对照参考账号。返回的 sec_user_id 可继续用于 user_profile / user_posts / 星图 kolId 转换。",
+      "method": "POST",
+      "paramsIn": "body",
+      "proxyPath": "douyin/search/fetch_user_search_v2",
+      "requiredParams": [
+        "keyword"
+      ],
+      "params": [
+        {
+          "name": "keyword",
+          "in": "body",
+          "type": "string",
+          "required": true,
+          "desc": "搜索关键词(账号名/行业词),如「港风穿搭」「母婴测评」。"
+        },
+        {
+          "name": "cursor",
+          "in": "body",
+          "type": "integer",
+          "required": false,
+          "default": 0,
+          "desc": "分页偏移游标,首页为 0,翻页用上一次返回的 cursor。"
+        },
+        {
+          "name": "search_id",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 search_id。"
+        }
+      ],
+      "responseHint": "返回用户列表,含 sec_user_id、昵称、粉丝数、抖音号(unique_id);sec_user_id 用于主页/作品接口,unique_id 可换星图 kolId。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "用户",
+        "user",
+        "账号",
+        "搜索",
+        "search",
+        "达人",
+        "kol"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.search_video",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "search",
+      "title": "抖音视频搜索",
+      "summary": "按关键词只搜索抖音视频(素人/开放端)。用于按方向词采集相关短视频、找同类内容账号。",
+      "method": "POST",
+      "paramsIn": "body",
+      "proxyPath": "douyin/search/fetch_video_search_v2",
+      "requiredParams": [
+        "keyword"
+      ],
+      "params": [
+        {
+          "name": "keyword",
+          "in": "body",
+          "type": "string",
+          "required": true,
+          "desc": "搜索关键词,任意行业/方向均可。"
+        },
+        {
+          "name": "cursor",
+          "in": "body",
+          "type": "integer",
+          "required": false,
+          "default": 0,
+          "desc": "分页偏移游标,首页为 0,翻页用上一次返回的 cursor。"
+        },
+        {
+          "name": "sort_type",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "0",
+          "desc": "排序:0 综合,1 最多点赞,2 最新发布。"
+        },
+        {
+          "name": "publish_time",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "0",
+          "desc": "发布时间:0 不限,1 一天内,7 一周内,180 半年内。"
+        },
+        {
+          "name": "search_id",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 search_id。"
+        }
+      ],
+      "responseHint": "返回视频列表,含 aweme_id、作者(含 sec_user_id)、统计数据;翻页用 cursor/search_id。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "视频",
+        "video",
+        "搜索",
+        "search",
+        "关键词"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.search_general",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "search",
+      "title": "抖音综合搜索",
+      "summary": "按关键词做抖音综合搜索(视频/用户/话题混合,素人/开放端)。搜索意图不确定时的入口。",
+      "method": "POST",
+      "paramsIn": "body",
+      "proxyPath": "douyin/search/fetch_general_search_v2",
+      "requiredParams": [
+        "keyword"
+      ],
+      "params": [
+        {
+          "name": "keyword",
+          "in": "body",
+          "type": "string",
+          "required": true,
+          "desc": "搜索关键词,任意行业/方向均可。"
+        },
+        {
+          "name": "cursor",
+          "in": "body",
+          "type": "integer",
+          "required": false,
+          "default": 0,
+          "desc": "分页偏移游标,首页为 0,翻页用上一次返回的 cursor。"
+        },
+        {
+          "name": "sort_type",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "0",
+          "desc": "排序:0 综合,1 最多点赞,2 最新发布。"
+        },
+        {
+          "name": "publish_time",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "0",
+          "desc": "发布时间:0 不限,1 一天内,7 一周内,180 半年内。"
+        },
+        {
+          "name": "search_id",
+          "in": "body",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 search_id。"
+        }
+      ],
+      "responseHint": "返回混合结果(视频/用户/话题);翻页用 cursor/search_id。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "综合",
+        "搜索",
+        "search",
+        "视频",
+        "用户"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.user_profile",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "detail",
+      "title": "抖音用户主页信息",
+      "summary": "按 sec_user_id 获取抖音用户主页资料(昵称、签名、粉丝/获赞/作品数等)。参考账号解析与候选账号主页补证用。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/app/v3/handler_user_profile",
+      "requiredParams": [
+        "sec_user_id"
+      ],
+      "params": [
+        {
+          "name": "sec_user_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "用户 sec_user_id,来自 search_user、视频作者字段或 get_sec_user_id 链接解析。"
+        }
+      ],
+      "responseHint": "返回用户资料对象,含粉丝数、获赞数、作品数、签名、抖音号(unique_id)。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "用户",
+        "user",
+        "主页",
+        "profile",
+        "参考账号",
+        "达人"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.user_posts",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "detail",
+      "title": "抖音用户发布视频",
+      "summary": "按 sec_user_id 拉取某用户主页发布的视频列表。用于参考账号/候选账号近作盘点(近 10-20 条、更新频率、内容类目一致性)。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/app/v3/fetch_user_post_videos",
+      "requiredParams": [
+        "sec_user_id"
+      ],
+      "params": [
+        {
+          "name": "sec_user_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "用户 sec_user_id。"
+        },
+        {
+          "name": "max_cursor",
+          "in": "query",
+          "type": "integer",
+          "required": false,
+          "default": 0,
+          "desc": "分页游标,首页为 0,翻页用上一次返回的 max_cursor。"
+        },
+        {
+          "name": "count",
+          "in": "query",
+          "type": "integer",
+          "required": false,
+          "default": 20,
+          "desc": "每页数量,建议 20。"
+        }
+      ],
+      "responseHint": "返回该用户的视频列表(含 aweme_id、标题、封面、统计);翻页用返回的 max_cursor。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "用户",
+        "作品",
+        "posts",
+        "近作",
+        "参考账号",
+        "达人"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.video_detail",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "detail",
+      "title": "抖音视频详情",
+      "summary": "按视频 id(aweme_id)获取单个抖音视频完整数据(标题、作者、统计、视频地址、封面等)。参考视频补证用。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/app/v3/fetch_one_video",
+      "requiredParams": [
+        "aweme_id"
+      ],
+      "params": [
+        {
+          "name": "aweme_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "视频 id(aweme_id),来自搜索/作品列表/链接解析。"
+        }
+      ],
+      "responseHint": "返回单个视频对象,含统计、作者(sec_user_id)、话题、视频/封面地址。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "视频",
+        "video",
+        "详情",
+        "detail",
+        "参考视频",
+        "aweme"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.video_comments",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "detail",
+      "title": "抖音视频评论列表",
+      "summary": "按视频 id 拉取抖音视频一级评论。用于互动真实性核查(重复评论/刷评风险)与口碑证据。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/app/v3/fetch_video_comments",
+      "requiredParams": [
+        "aweme_id"
+      ],
+      "params": [
+        {
+          "name": "aweme_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "视频 id(aweme_id)。"
+        },
+        {
+          "name": "cursor",
+          "in": "query",
+          "type": "integer",
+          "required": false,
+          "default": 0,
+          "desc": "分页游标,首页为 0,翻页用上一次返回的 cursor。"
+        },
+        {
+          "name": "count",
+          "in": "query",
+          "type": "integer",
+          "required": false,
+          "default": 20,
+          "desc": "每页数量,建议 20。"
+        }
+      ],
+      "responseHint": "返回评论列表,含 comment_id、文本、点赞、子评论数;翻页用返回的 cursor。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "评论",
+        "comment",
+        "刷评",
+        "互动",
+        "风险"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.get_sec_user_id",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "resolve",
+      "title": "抖音主页链接提取 sec_user_id",
+      "summary": "把抖音用户主页链接(含短链)解析成 sec_user_id。参考账号链接解析第一步。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/web/get_sec_user_id",
+      "requiredParams": [
+        "url"
+      ],
+      "params": [
+        {
+          "name": "url",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "用户主页链接,支持 v.douyin.com 短链与 www.douyin.com/user/ 长链。"
+        }
+      ],
+      "responseHint": "返回 sec_user_id,可继续调 user_profile / user_posts / 星图 kolId 转换。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "链接解析",
+        "参考账号",
+        "sec_user_id",
+        "短链",
+        "主页"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.fetch_one_video_by_share_url",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "resolve",
+      "title": "抖音分享链接获取视频数据",
+      "summary": "按抖音分享链接(含 v.douyin.com 短链)直接获取该视频完整数据。参考视频链接解析一步到位(视频+作者 sec_user_id)。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/app/v3/fetch_one_video_by_share_url",
+      "requiredParams": [
+        "share_url"
+      ],
+      "params": [
+        {
+          "name": "share_url",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "视频分享链接,支持短链。"
+        }
+      ],
+      "responseHint": "返回单个视频对象(同 video_detail),含作者 sec_user_id,可继续做账号解析。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "链接解析",
+        "参考视频",
+        "分享链接",
+        "短链",
+        "video"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.xingtu.get_xingtu_kolid_by_sec_user_id",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "resolve",
+      "title": "sec_user_id 换星图 kolId",
+      "summary": "通过抖音 sec_user_id 获取星图 kolId。把素人线解析出的参考账号接回星图详情线(报价/CP/商单等)。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/xingtu/get_xingtu_kolid_by_sec_user_id",
+      "requiredParams": [
+        "sec_user_id"
+      ],
+      "params": [
+        {
+          "name": "sec_user_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "抖音用户 sec_user_id。"
+        }
+      ],
+      "responseHint": "返回星图 kolId;拿到后可用星图详情接口(oAuthorId=kolId)查报价/CP/商单数据。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "星图",
+        "xingtu",
+        "kolid",
+        "参考账号",
+        "桥接"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "douyin.xingtu.get_xingtu_kolid_by_unique_id",
+      "platform": "douyin",
+      "channel": "social",
+      "wave": "resolve",
+      "title": "抖音号换星图 kolId",
+      "summary": "通过抖音号(unique_id)获取星图 kolId。客户只给抖音号时直接接回星图详情线。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "douyin/xingtu/get_xingtu_kolid_by_unique_id",
+      "requiredParams": [
+        "unique_id"
+      ],
+      "params": [
+        {
+          "name": "unique_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "抖音号(unique_id)。"
+        }
+      ],
+      "responseHint": "返回星图 kolId;拿到后可用星图详情接口(oAuthorId=kolId)查报价/CP/商单数据。",
+      "tags": [
+        "抖音",
+        "douyin",
+        "素人",
+        "非星图",
+        "社媒",
+        "social",
+        "星图",
+        "xingtu",
+        "kolid",
+        "抖音号",
+        "桥接"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.search_blogger",
+      "platform": "xiaohongshu",
+      "wave": "search",
+      "title": "小红书蒲公英博主搜索",
+      "summary": "按关键词搜索小红书蒲公英博主,返回博主列表(含 userId、redId 短ID、昵称、粉丝数、报价及部分绩效字段)。返回项中的 userId 即详情接口所需入参。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu-pgy/api/solar/cooperator/blogger/v2/v1",
+      "requiredParams": [
+        "keyword"
+      ],
+      "responseHint": "博主列表,每项含 userId(→ 详情接口入参)、redId(短ID)、昵称、粉丝数、报价、部分 CPM/互动字段。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "蒲公英",
+        "pgy",
+        "搜索",
+        "博主",
+        "search",
+        "first-wave"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "xiaohongshu.kol_cost_effective",
+      "platform": "xiaohongshu",
+      "wave": "detail",
+      "title": "小红书蒲公英博主性价比 (CPM/CPE)",
+      "summary": "返回单个博主的图文/视频预期 CPM、阅读成本、互动成本等性价比指标。入参 userId 取自搜索结果。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu-pgy/api/solar/kol/dataV2/costEffective/v1",
+      "requiredParams": [
+        "userId"
+      ],
+      "responseHint": "estimatePictureCpm / estimateVideoCpm / pictureReadCost / videoReadCost / estimatePictureEngageCost 等。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "蒲公英",
+        "pgy",
+        "cpm",
+        "cpe",
+        "性价比",
+        "详情",
+        "detail",
+        "second-wave",
+        "绩效"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "xiaohongshu.kol_core_data",
+      "platform": "xiaohongshu",
+      "wave": "detail",
+      "title": "小红书蒲公英博主核心数据",
+      "summary": "返回单个博主的核心数据(阅读/互动中位数等汇总与每日数据)。入参 userId 取自搜索结果。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu-pgy/api/pgy/kol/data/core_data/v1",
+      "requiredParams": [
+        "userId"
+      ],
+      "responseHint": "sumData(阅读/互动中位数等汇总)/ dailyData(每日数据)。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "蒲公英",
+        "pgy",
+        "核心数据",
+        "中位数",
+        "详情",
+        "detail",
+        "second-wave",
+        "绩效"
+      ],
+      "billing": 1,
+      "channel": "ecommerce"
+    },
+    {
+      "id": "xiaohongshu.search_notes",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "search",
+      "title": "小红书笔记搜索",
+      "summary": "按关键词搜索小红书笔记。任意行业/方向关键词均可用于 VOC 选题与口碑采集。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/search_notes",
+      "requiredParams": [
+        "keyword",
+        "page"
+      ],
+      "params": [
+        {
+          "name": "keyword",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "搜索关键词,任意行业/方向均可。"
+        },
+        {
+          "name": "page",
+          "in": "query",
+          "type": "integer",
+          "required": true,
+          "default": 1,
+          "desc": "页码,从 1 开始,翻页递增。"
+        },
+        {
+          "name": "sort_type",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "general",
+          "desc": "排序:general 综合,time_descending 最新,popularity_descending 最热。"
+        },
+        {
+          "name": "filter_note_type",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "不限",
+          "desc": "笔记类型筛选:不限 / 视频 / 图文。"
+        },
+        {
+          "name": "filter_note_time",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "不限",
+          "desc": "发布时间筛选:不限 / 一天内 / 一周内 / 半年内。"
+        },
+        {
+          "name": "search_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 search_id。"
+        },
+        {
+          "name": "session_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 session_id。"
+        }
+      ],
+      "responseHint": "返回笔记列表,含 note_id、标题、作者、互动数;翻页递增 page 并透传 search_id/session_id。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "笔记",
+        "note",
+        "搜索",
+        "search",
+        "关键词",
+        "voc",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.search_products",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "search",
+      "title": "小红书商品搜索",
+      "summary": "按关键词搜索小红书商品(带价格/服务保障筛选),用于电商相关 VOC。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/search_products",
+      "requiredParams": [
+        "keyword",
+        "page"
+      ],
+      "params": [
+        {
+          "name": "keyword",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "搜索关键词。"
+        },
+        {
+          "name": "page",
+          "in": "query",
+          "type": "integer",
+          "required": true,
+          "default": 1,
+          "desc": "页码,从 1 开始。"
+        },
+        {
+          "name": "sort",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "排序方式,可留空用默认。"
+        },
+        {
+          "name": "scope",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "筛选范围,可留空。"
+        },
+        {
+          "name": "min_price",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "最低价筛选,可留空。"
+        },
+        {
+          "name": "max_price",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "最高价筛选,可留空。"
+        },
+        {
+          "name": "search_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 search_id。"
+        },
+        {
+          "name": "session_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "连续翻页时透传上一次返回的 session_id。"
+        }
+      ],
+      "responseHint": "返回商品列表,含标题、价格、销量、店铺。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "商品",
+        "product",
+        "电商",
+        "搜索",
+        "search",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.web_v3.fetch_search_notes",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "search",
+      "title": "搜索笔记",
+      "summary": "搜索笔记 接口优先级: - 小红书接口推荐优先级: `App V2` > `App` > `Web V3(本接口)` > `Web V2` > `Web` 参数: - keyword: 搜索关键词 - page: 页码 - sort: 排序方式 - general: 综合 - t",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/web_v3/fetch_search_notes",
+      "requiredParams": [],
+      "params": [],
+      "responseHint": "",
+      "tags": [
+        "xiaohongshu",
+        "小红书",
+        "web",
+        "v3",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.web_v3.fetch_search_users",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "search",
+      "title": "搜索用户",
+      "summary": "搜索用户 接口优先级: - 小红书接口推荐优先级: `App V2` > `App` > `Web V3(本接口)` > `Web V2` > `Web` 参数: - keyword: 搜索关键词 - page: 页码",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/web_v3/fetch_search_users",
+      "requiredParams": [],
+      "params": [],
+      "responseHint": "",
+      "tags": [
+        "xiaohongshu",
+        "小红书",
+        "web",
+        "v3",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.note_detail",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书笔记详情",
+      "summary": "按 note_id(或分享文案)获取小红书笔记完整内容。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/get_note_info",
+      "requiredParams": [
+        "note_id"
+      ],
+      "params": [
+        {
+          "name": "note_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "笔记 id(note_id),来自 search_notes 返回。"
+        },
+        {
+          "name": "share_text",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "可选:直接传小红书分享文案/链接,与 note_id 二选一。"
+        }
+      ],
+      "responseHint": "返回单条笔记对象,含正文、图片/视频、话题、互动数。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "笔记",
+        "note",
+        "详情",
+        "detail",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.note_comments",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书笔记评论",
+      "summary": "按 note_id 拉取小红书笔记的一级评论(App V2,VOC 口碑主力接口)。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app_v2/get_note_comments",
+      "requiredParams": [
+        "note_id"
+      ],
+      "params": [
+        {
+          "name": "note_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "笔记 id(note_id),来自 search_notes 返回的 note.id。"
+        },
+        {
+          "name": "share_text",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "可选:小红书分享文案/链接,与 note_id 二选一。"
+        },
+        {
+          "name": "xsec_token",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "可选安全令牌,来自 search_notes 返回的 note.xsec_token;部分笔记风控需要。"
+        }
+      ],
+      "responseHint": "返回评论列表,含 comment_id、文本、点赞、子评论数;翻页用返回的游标。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "评论",
+        "comment",
+        "voc",
+        "口碑",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.sub_comments",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书评论回复(楼中楼)",
+      "summary": "按 note_id + comment_id 拉取某条评论下的回复(App V2),用于深挖讨论。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app_v2/get_note_sub_comments",
+      "requiredParams": [
+        "note_id",
+        "comment_id"
+      ],
+      "params": [
+        {
+          "name": "note_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "笔记 id(note_id)。"
+        },
+        {
+          "name": "comment_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "一级评论 id,来自 note_comments 返回。"
+        },
+        {
+          "name": "start",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "分页游标,首页留空,翻页用上一次返回的 start。"
+        }
+      ],
+      "responseHint": "返回某条评论的回复列表;翻页用返回的 start。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "评论",
+        "回复",
+        "reply",
+        "sub",
+        "楼中楼",
+        "voc",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.user_info",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书用户信息",
+      "summary": "按 user_id 获取小红书用户资料(昵称、简介、粉丝/获赞等)。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/get_user_info",
+      "requiredParams": [
+        "user_id"
+      ],
+      "params": [
+        {
+          "name": "user_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "用户 id(24 位 hex),来自 search_notes 返回的 note.user.userid;注意不是数字 red_id。"
+        }
+      ],
+      "responseHint": "返回用户资料对象,含粉丝数、获赞收藏、简介。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "用户",
+        "user",
+        "信息",
+        "profile",
+        "达人",
+        "竞品",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.user_notes",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书用户发布笔记",
+      "summary": "按 user_id 拉取某用户发布的笔记列表,用于竞品/达人内容盘点。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/get_user_notes",
+      "requiredParams": [
+        "user_id"
+      ],
+      "params": [
+        {
+          "name": "user_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "用户 id(user_id)。"
+        },
+        {
+          "name": "cursor",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "分页游标,首页留空,翻页用上一次返回的 cursor。"
+        }
+      ],
+      "responseHint": "返回该用户的笔记列表;翻页用返回的 cursor。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "用户",
+        "笔记",
+        "notes",
+        "posts",
+        "竞品",
+        "达人",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.topic_notes",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "小红书话题笔记",
+      "summary": "按话题 page_id 拉取该话题下的笔记,用于话题/品类维度的 VOC 采集。",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/app/get_topic_notes",
+      "requiredParams": [
+        "page_id",
+        "first_load_time"
+      ],
+      "params": [
+        {
+          "name": "page_id",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "话题 page_id。"
+        },
+        {
+          "name": "first_load_time",
+          "in": "query",
+          "type": "string",
+          "required": true,
+          "desc": "首次加载时间戳(秒),首次请求传当前时间戳。"
+        },
+        {
+          "name": "sort",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "hot",
+          "desc": "排序:hot 最热 / time 最新。"
+        },
+        {
+          "name": "last_note_ct",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "翻页透传上一次返回的 last_note_ct。"
+        },
+        {
+          "name": "last_note_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "翻页透传上一次返回的 last_note_id。"
+        },
+        {
+          "name": "cursor_score",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "翻页透传上一次返回的 cursor_score。"
+        },
+        {
+          "name": "session_id",
+          "in": "query",
+          "type": "string",
+          "required": false,
+          "default": "",
+          "desc": "翻页透传上一次返回的 session_id。"
+        }
+      ],
+      "responseHint": "返回话题下笔记列表;翻页透传 last_note_ct/last_note_id/cursor_score/session_id。",
+      "tags": [
+        "小红书",
+        "xiaohongshu",
+        "xhs",
+        "话题",
+        "topic",
+        "笔记",
+        "品类",
+        "voc",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.web_v3.fetch_user_info",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "获取用户信息",
+      "summary": "获取用户公开资料 接口优先级: - 小红书接口推荐优先级: `App V2` > `App` > `Web V3(本接口)` > `Web V2` > `Web` 参数: - user_id: 用户ID",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/web_v3/fetch_user_info",
+      "requiredParams": [],
+      "params": [],
+      "responseHint": "",
+      "tags": [
+        "xiaohongshu",
+        "小红书",
+        "web",
+        "v3",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.web_v3.fetch_user_notes",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "获取用户笔记列表",
+      "summary": "获取用户发布的笔记列表 接口优先级: - 小红书接口推荐优先级: `App V2` > `App` > `Web V3(本接口)` > `Web V2` > `Web` 参数: - user_id: 用户ID - cursor: 翻页游标 - num: 返回数量 (最大 30)",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/web_v3/fetch_user_notes",
+      "requiredParams": [],
+      "params": [],
+      "responseHint": "",
+      "tags": [
+        "xiaohongshu",
+        "小红书",
+        "web",
+        "v3",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    },
+    {
+      "id": "xiaohongshu.web_v3.fetch_note_detail",
+      "platform": "xiaohongshu",
+      "channel": "social",
+      "wave": "detail",
+      "title": "获取笔记详情",
+      "summary": "获取笔记详情(图文/视频通用) 接口优先级: - 小红书接口推荐优先级: `App V2` > `App` > `Web V3(本接口)` > `Web V2` > `Web` 参数: - note_id: 笔记ID,可以从小红书的分享链接中获取 - xsec_token: 安全",
+      "method": "GET",
+      "paramsIn": "query",
+      "proxyPath": "xiaohongshu/web_v3/fetch_note_detail",
+      "requiredParams": [],
+      "params": [],
+      "responseHint": "",
+      "tags": [
+        "xiaohongshu",
+        "小红书",
+        "web",
+        "v3",
+        "素人",
+        "非蒲公英",
+        "社媒",
+        "social"
+      ],
+      "billing": 1
+    }
+  ]
+}

+ 121 - 0
claude-code-tihao-sourcing/mcp/src/core/api-catalog.js

@@ -0,0 +1,121 @@
+const fs = require('fs');
+const path = require('path');
+
+const CATALOG_PATH = path.resolve(__dirname, '..', '..', 'catalog', 'voc-e-commerce-endpoints.json');
+const PARAMS_DIR = path.resolve(__dirname, '..', '..', 'catalog', 'params');
+
+let cached;
+const paramCache = {};
+
+function loadCatalog() {
+  if (cached) return cached;
+  const raw = fs.readFileSync(CATALOG_PATH, 'utf8').replace(/^\uFEFF/, '');
+  cached = JSON.parse(raw);
+  return cached;
+}
+
+// Lazily load the per-platform parameter store; the index stays lightweight.
+function loadParams(platform) {
+  if (!platform) return {};
+  if (paramCache[platform]) return paramCache[platform];
+  try {
+    const raw = fs.readFileSync(path.join(PARAMS_DIR, `${platform}.json`), 'utf8').replace(/^\uFEFF/, '');
+    paramCache[platform] = JSON.parse(raw);
+  } catch {
+    paramCache[platform] = {};
+  }
+  return paramCache[platform];
+}
+
+// Attach the full parameter schema to an index entry, fetching from the param store on demand.
+function hydrateEndpoint(endpoint) {
+  if (!endpoint) return endpoint;
+  if (Array.isArray(endpoint.params) && endpoint.params.length) return endpoint;
+  const params = loadParams(endpoint.platform)[endpoint.id] || [];
+  return { ...endpoint, params };
+}
+
+function listEndpoints() {
+  return loadCatalog().endpoints || [];
+}
+
+function normalize(value) {
+  return String(value || '').trim().toLowerCase();
+}
+
+function findEndpoint(idOrPath) {
+  const needle = normalize(idOrPath);
+  if (!needle) return undefined;
+  const endpoints = listEndpoints();
+  return (
+    endpoints.find(item => normalize(item.id) === needle) ||
+    endpoints.find(item => normalize(item.proxyPath) === needle) ||
+    endpoints.find(item => normalize(item.proxyPath).replace(/^\//, '') === needle.replace(/^\//, ''))
+  );
+}
+
+function scoreEndpoint(endpoint, terms) {
+  if (!terms.length) return 1;
+  const haystack = normalize([
+    endpoint.id,
+    endpoint.platform,
+    endpoint.title,
+    endpoint.summary,
+    endpoint.proxyPath,
+    endpoint.wave,
+    (endpoint.tags || []).join(' ')
+  ].join(' '));
+  let score = 0;
+  for (const term of terms) {
+    if (!term) continue;
+    if (haystack.includes(term)) score += 1;
+  }
+  return score;
+}
+
+function searchEndpoints({ query, platform, tag, limit = 20 } = {}) {
+  const terms = normalize(query).split(/\s+/).filter(Boolean);
+  const platformKey = normalize(platform);
+  const tagKey = normalize(tag);
+  let results = listEndpoints().map(endpoint => ({
+    endpoint,
+    score: scoreEndpoint(endpoint, terms)
+  }));
+  if (platformKey) {
+    results = results.filter(item => normalize(item.endpoint.platform) === platformKey);
+  }
+  if (tagKey) {
+    results = results.filter(item => (item.endpoint.tags || []).some(t => normalize(t) === tagKey));
+  }
+  results = results.filter(item => item.score > 0);
+  results.sort((a, b) => b.score - a.score);
+  return results.slice(0, Math.max(1, limit)).map(item => item.endpoint);
+}
+
+function summarizeEndpoint(endpoint) {
+  const hydrated = hydrateEndpoint(endpoint);
+  return {
+    id: hydrated.id,
+    platform: hydrated.platform,
+    wave: hydrated.wave,
+    title: hydrated.title,
+    summary: hydrated.summary,
+    method: hydrated.method,
+    proxyPath: hydrated.proxyPath,
+    requiredParams: (hydrated.params && hydrated.params.length)
+      ? hydrated.params.filter(p => p.required).map(p => p.name)
+      : (hydrated.requiredParams || []),
+    tags: hydrated.tags || []
+  };
+}
+
+module.exports = {
+  CATALOG_PATH,
+  loadCatalog,
+  loadParams,
+  hydrateEndpoint,
+  listEndpoints,
+  findEndpoint,
+  searchEndpoints,
+  summarizeEndpoint
+};

+ 111 - 16
claude-code-tihao-sourcing/mcp/src/core/credentials.js

@@ -29,13 +29,73 @@ function readEnvFile(cwd = process.cwd()) {
   return values;
   return values;
 }
 }
 
 
+function readJsonMaybe(filePath) {
+  try {
+    if (!filePath || !fs.existsSync(filePath)) return {};
+    return JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, ''));
+  } catch {
+    return {};
+  }
+}
+
+// 合并读取 Claude Code 的 settings env(用户级 + 项目级,含 .local 覆盖文件)。
+// Claude Code 用户的 fmode newapi SK 默认就放在这里的 env.ANTHROPIC_AUTH_TOKEN。
+function readClaudeSettingsEnv() {
+  const files = [
+    path.join(os.homedir(), '.claude', 'settings.json'),
+    path.join(os.homedir(), '.claude', 'settings.local.json'),
+    path.join(process.cwd(), '.claude', 'settings.json'),
+    path.join(process.cwd(), '.claude', 'settings.local.json')
+  ];
+  const merged = {};
+  for (const filePath of files) {
+    const json = readJsonMaybe(filePath);
+    const env = json && typeof json.env === 'object' && json.env ? json.env : null;
+    if (!env) continue;
+    for (const [key, value] of Object.entries(env)) {
+      if (merged[key] === undefined && typeof value === 'string' && value.trim()) {
+        merged[key] = value;
+      }
+    }
+  }
+  return merged;
+}
+
+// 读取 ~/.fmode/config.json(及项目级 ./.fmode/config.json)里的 newapi token。
+function readFmodeConfig() {
+  const files = [
+    path.join(os.homedir(), '.fmode', 'config.json'),
+    path.join(process.cwd(), '.fmode', 'config.json')
+  ];
+  const merged = {};
+  for (const filePath of files) {
+    const json = readJsonMaybe(filePath);
+    for (const [key, value] of Object.entries(json || {})) {
+      if (merged[key] === undefined) merged[key] = value;
+    }
+  }
+  return merged;
+}
+
+// 仅当 ANTHROPIC_AUTH_TOKEN 看起来是 fmode 的 newapi SK 时才采纳:
+// - 必须 sk- 开头,且排除真 Anthropic 官方 key(sk-ant- 开头);
+// - 若 settings 里设了 ANTHROPIC_BASE_URL,必须指向 fmode(否则这把 token 是发往别处的)。
+function pickFmodeAnthropicToken(env) {
+  const token = env && typeof env.ANTHROPIC_AUTH_TOKEN === 'string' ? env.ANTHROPIC_AUTH_TOKEN.trim() : '';
+  if (!token || !/^sk-/i.test(token) || /^sk-ant-/i.test(token)) return '';
+  const base = String((env && (env.ANTHROPIC_BASE_URL || env.ANTHROPIC_API_BASE)) || '').toLowerCase();
+  if (base && !base.includes('fmode')) return '';
+  return token;
+}
+
 function readTihaoToken(input = {}) {
 function readTihaoToken(input = {}) {
+  const explicit = input.tihaoToken || input.sessionToken || input.vocToken || '';
+  if (explicit) return explicit;
+  if (input.allowEnvToken === false) return '';
+
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const env = { ...process.env, ...fileEnv };
   const env = { ...process.env, ...fileEnv };
   return (
   return (
-    input.tihaoToken ||
-    input.sessionToken ||
-    input.vocToken ||
     env.TIHAO_SESSION_TOKEN ||
     env.TIHAO_SESSION_TOKEN ||
     env.VOC_ECOMMERCE_TOKEN ||
     env.VOC_ECOMMERCE_TOKEN ||
     env.VOC_TOKEN ||
     env.VOC_TOKEN ||
@@ -45,8 +105,50 @@ function readTihaoToken(input = {}) {
   );
   );
 }
 }
 
 
+// NewAPI(fmode-api)计费 token,以 sk- 开头。
+// 计费已迁移到 NewAPI:优先用它发请求,鉴权失败再回退平台 sessionToken(apig,原路径保留)。
+// 关键:这把 sk- 就是 Claude Code 的 env.ANTHROPIC_AUTH_TOKEN(在 ~/.claude/settings.json),
+// 默认装完技能不必再手动配置——这里直接从 settings.json / 进程环境读取它,避免「明明配了 SK 却判缺 token、被甩进老付费框」的死循环。
+// 取值优先级:显式入参 → .env.local/环境变量 → voc-credentials.json/tihao-credentials.json 的 fmodeApiKey
+//   → ~/.fmode/config.json 的 newapiToken → Claude Code env(进程注入 + ~/.claude/settings.json)的 ANTHROPIC_AUTH_TOKEN(sk-、且 base 指向 fmode)。
+function readNewApiToken(input = {}) {
+  const explicit =
+    input.newapiToken ||
+    input.newApiToken ||
+    input.fmodeApiKey ||
+    input.fmodeApiToken ||
+    '';
+  if (explicit) return explicit;
+  if (input.allowEnvToken === false) return '';
+
+  const fileEnv = readEnvFile(input.cwd || process.cwd());
+  const env = { ...process.env, ...fileEnv };
+  const fmodeConfig = readFmodeConfig();
+  const claudeEnv = readClaudeSettingsEnv();
+  return (
+    env.NEWAPI_TOKEN ||
+    env.NEW_API_TOKEN ||
+    env.FMODE_API_KEY ||
+    env.FMODE_API_TOKEN ||
+    env.fmodeApiKey ||
+    env.fmodeApiToken ||
+    fmodeConfig.newapiToken ||
+    fmodeConfig.newApiToken ||
+    fmodeConfig.fmodeApiKey ||
+    fmodeConfig.fmodeApiToken ||
+    // Claude Code 显式写在 settings.json env 里的 fmode key(优先于自动推断的 ANTHROPIC_AUTH_TOKEN)
+    claudeEnv.FMODE_API_KEY ||
+    claudeEnv.NEWAPI_TOKEN ||
+    // Claude Code 默认入口:进程注入的 ANTHROPIC_AUTH_TOKEN(Claude Code 会把 settings.env 注入子进程)
+    pickFmodeAnthropicToken(process.env) ||
+    // 兜底:直接读 ~/.claude/settings.json 里的 env.ANTHROPIC_AUTH_TOKEN(独立运行、未被注入时)
+    pickFmodeAnthropicToken(claudeEnv) ||
+    ''
+  );
+}
+
 // 社媒分析服务 token(对外只叫「社媒分析服务」)。
 // 社媒分析服务 token(对外只叫「社媒分析服务」)。
-// 优先用平台 sessionToken / vocSocialToken;tikhub* 只作为历史兼容项保留,不对外暴露。
+// 优先用平台 sessionToken / vocSocialToken。
 function readSocialAnalysisToken(input = {}) {
 function readSocialAnalysisToken(input = {}) {
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const env = { ...process.env, ...fileEnv };
   const env = { ...process.env, ...fileEnv };
@@ -56,23 +158,13 @@ function readSocialAnalysisToken(input = {}) {
     input.sessionToken ||
     input.sessionToken ||
     input.tihaoToken ||
     input.tihaoToken ||
     input.vocToken ||
     input.vocToken ||
-    input.tikhubToken ||
-    input.tikHubToken ||
     env.VOC_SOCIAL_TOKEN ||
     env.VOC_SOCIAL_TOKEN ||
     env.TIHAO_SESSION_TOKEN ||
     env.TIHAO_SESSION_TOKEN ||
     env.VOC_TOKEN ||
     env.VOC_TOKEN ||
-    env.TIKHUB_TOKEN ||
-    env.TIKHUB_API_KEY ||
-    env.TIKHUB_KEY ||
     ''
     ''
   );
   );
 }
 }
 
 
-// Backward-compatible alias; prefer readSocialAnalysisToken in new code.
-function readTikhubToken(input = {}) {
-  return readSocialAnalysisToken(input);
-}
-
 function readEvidenceProviderToken(input = {}) {
 function readEvidenceProviderToken(input = {}) {
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const env = { ...process.env, ...fileEnv };
   const env = { ...process.env, ...fileEnv };
@@ -101,8 +193,11 @@ function readEvidenceProviderToken(input = {}) {
 
 
 module.exports = {
 module.exports = {
   readTihaoToken,
   readTihaoToken,
+  readNewApiToken,
   readSocialAnalysisToken,
   readSocialAnalysisToken,
-  readTikhubToken,
   readEvidenceProviderToken,
   readEvidenceProviderToken,
-  readEnvFile
+  readEnvFile,
+  readClaudeSettingsEnv,
+  readFmodeConfig,
+  pickFmodeAnthropicToken
 };
 };

+ 68 - 0
claude-code-tihao-sourcing/mcp/src/core/files.js

@@ -94,6 +94,73 @@ function parseSharedStrings(xmlBuffer) {
   return strings;
   return strings;
 }
 }
 
 
+function readXlsxSheetNames(buffer) {
+  const workbookXml = readZipEntry(buffer, 'xl/workbook.xml');
+  if (!workbookXml) return [];
+  const xml = workbookXml.toString('utf8');
+  const names = [];
+  const sheetRegex = /<sheet\b[^>]*\bname="([^"]*)"/g;
+  let match;
+  while ((match = sheetRegex.exec(xml)) !== null) names.push(decodeXml(match[1]));
+  return names;
+}
+
+function columnLabelToIndex(label) {
+  let index = 0;
+  for (const char of String(label || '').toUpperCase()) {
+    index = index * 26 + (char.charCodeAt(0) - 64);
+  }
+  return index - 1;
+}
+
+// 读取 xlsx 的结构化二维表:{ name, rows }[],rows[rowIdx][colIdx] 保留空单元格以对齐列。
+function readXlsxRows(fileOrBuffer) {
+  const buffer = Buffer.isBuffer(fileOrBuffer) ? fileOrBuffer : fs.readFileSync(fileOrBuffer);
+  const sharedStrings = parseSharedStrings(readZipEntry(buffer, 'xl/sharedStrings.xml'));
+  const sheetNames = readXlsxSheetNames(buffer);
+  const entries = listZipEntries(buffer)
+    .filter(entry => /^xl\/worksheets\/sheet\d+\.xml$/.test(entry.name))
+    .sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
+  const sheets = [];
+  entries.forEach((entry, entryIndex) => {
+    const xmlBuffer = readZipEntry(buffer, entry.name);
+    if (!xmlBuffer) return;
+    const xml = xmlBuffer.toString('utf8');
+    const rows = [];
+    const rowRegex = /<row\b[^>]*>([\s\S]*?)<\/row>/g;
+    let rowMatch;
+    while ((rowMatch = rowRegex.exec(xml)) !== null) {
+      const cells = [];
+      let colIndex = 0;
+      const cellRegex = /<c\b([^>]*?)(?:\/>|>([\s\S]*?)<\/c>)/g;
+      let cellMatch;
+      while ((cellMatch = cellRegex.exec(rowMatch[1])) !== null) {
+        const attrs = cellMatch[1];
+        const body = cellMatch[2] || '';
+        const ref = attrs.match(/\br="([A-Za-z]+)\d+"/);
+        if (ref) colIndex = columnLabelToIndex(ref[1]);
+        const type = attrs.match(/\bt="([^"]+)"/)?.[1];
+        let value = '';
+        if (/<is\b/.test(body)) {
+          const parts = [];
+          const textRegex = /<t\b[^>]*>([\s\S]*?)<\/t>/g;
+          let textMatch;
+          while ((textMatch = textRegex.exec(body)) !== null) parts.push(decodeXml(textMatch[1]));
+          value = parts.join('');
+        } else {
+          const rawValue = body.match(/<v[^>]*>([\s\S]*?)<\/v>/)?.[1] || '';
+          value = type === 's' ? (sharedStrings[Number(rawValue)] || '') : decodeXml(rawValue);
+        }
+        cells[colIndex] = value;
+        colIndex += 1;
+      }
+      if (cells.some(item => String(item || '').trim())) rows.push(cells);
+    }
+    sheets.push({ name: sheetNames[entryIndex] || entry.name, rows });
+  });
+  return sheets;
+}
+
 function extractXlsxText(buffer) {
 function extractXlsxText(buffer) {
   const sharedStrings = parseSharedStrings(readZipEntry(buffer, 'xl/sharedStrings.xml'));
   const sharedStrings = parseSharedStrings(readZipEntry(buffer, 'xl/sharedStrings.xml'));
   const entries = listZipEntries(buffer)
   const entries = listZipEntries(buffer)
@@ -178,6 +245,7 @@ function extractDocxText(buffer) {
 module.exports = {
 module.exports = {
   ensureDir,
   ensureDir,
   readTextMaybe,
   readTextMaybe,
+  readXlsxRows,
   writeJson,
   writeJson,
   writeText
   writeText
 };
 };

+ 44 - 4
claude-code-tihao-sourcing/mcp/src/core/payment-links.js

@@ -4,6 +4,9 @@ const TIHAO_ECOMMERCE_APIG_PATH = '/apig/voc-e-commerce';
 const RECHARGE_BASE_URL = 'https://app.fmode.cn/dev/apig-pay/';
 const RECHARGE_BASE_URL = 'https://app.fmode.cn/dev/apig-pay/';
 const PARSE_USERS_ME_URL = 'https://server.fmode.cn/parse/users/me';
 const PARSE_USERS_ME_URL = 'https://server.fmode.cn/parse/users/me';
 
 
+// NewAPI(fmode-api)余额充值入口。携带 ?balance=fmodeapi 会自动打开余额充值弹窗。
+const FMODE_API_RECHARGE_URL = 'https://app.fmode.cn/dev/studio/?balance=fmodeapi';
+
 // 社媒分析服务(参考博主补证 / 视频·视觉分析)对应的 APIG 充值服务。
 // 社媒分析服务(参考博主补证 / 视频·视觉分析)对应的 APIG 充值服务。
 // 对客户只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名。
 // 对客户只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名。
 const VOC_SOCIAL_APIG_ID = 'Vo3ROWEvDy';
 const VOC_SOCIAL_APIG_ID = 'Vo3ROWEvDy';
@@ -107,13 +110,23 @@ function buildSocialRechargeRequiredMessage(paymentUrl) {
   ].join('\n');
   ].join('\n');
 }
 }
 
 
-function buildMissingTokenMessage(paymentUrl) {
+// 没读到计费 token 时的提示。
+// 关键:计费优先走 NewAPI(fmode-api)的 sk- token,这是「没读到 token」的**可恢复**状态,
+// 不是余额不足、也不代表功能用不了——先引导 agent 从 Claude Code 的 settings.json 取 SK 重试,
+// 充值只作为最后兜底(且仅真 402 才需要),newapi 余额充值走 studio/?balance=fmodeapi。
+function buildMissingTokenMessage(paymentUrl, options = {}) {
+  const fmodeApiRechargeUrl = options.fmodeApiRechargeUrl || FMODE_API_RECHARGE_URL;
   return [
   return [
-    '还没有检测到提号电商数据服务 sessionToken,暂时不能进行真实 JustOne 代理检索。',
+    '还没读到提号电商数据服务的计费 token,暂时不能进行真实电商数据代理检索——但这通常是**配置/读取**问题,不是余额不足,也不代表这个功能用不了。',
+    '',
+    '计费优先走 NewAPI(fmode-api)的 `sk-` token,它通常就是 Claude Code 的 `ANTHROPIC_AUTH_TOKEN`(`sk-` 开头),配在用户级 `~/.claude/settings.json` 的 `env` 里。',
     '',
     '',
-    `请先打开开通/充值链接:${paymentUrl}`,
+    '请按这个顺序自救(优先,不要直接让用户去点充值):',
+    '1. 读取 `~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`(确认是 `sk-` 开头、`ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`)。',
+    '2. 用 `FMODE_API_KEY=sk-…`(或入参 `newapiToken`)把它传给本工具,重新调用一次。',
+    `3. 仍报缺 token / 鉴权失败时,才考虑回退 \`r:\` 开头的平台 sessionToken(配到 \`.env.local\` 或 \`~/.claude/voc-credentials.json\`,或传入 \`tihaoToken\` / \`sessionToken\`;其充值/开通入口:${paymentUrl}),或确认是否真的 \`402 余额不足\`。`,
     '',
     '',
-    '开通后,把平台 sessionToken 配置到当前工作区 `.env.local`,或传入 MCP 工具的 `tihaoToken` / `sessionToken` 字段。',
+    `(仅当确认是 fmode-api 余额不足时才充值:${fmodeApiRechargeUrl})`,
     '',
     '',
     '系统只会用 token 发起接口请求,不会在报告、日志或聊天输出里回显 token。'
     '系统只会用 token 发起接口请求,不会在报告、日志或聊天输出里回显 token。'
   ].join('\n');
   ].join('\n');
@@ -129,6 +142,30 @@ function buildRechargeRequiredMessage(paymentUrl) {
   ].join('\n');
   ].join('\n');
 }
 }
 
 
+function buildFmodeApiRechargeUrl() {
+  return FMODE_API_RECHARGE_URL;
+}
+
+// NewAPI(fmode-api)余额不足(HTTP 402)时的充值提示。
+function buildFmodeApiRechargeMessage(paymentUrl = FMODE_API_RECHARGE_URL) {
+  return [
+    '提号电商数据服务余额不足(fmode-api 计费额度已用尽),暂时不能继续真实数据检索。',
+    '',
+    `请打开充值链接补充余额:${paymentUrl}`,
+    '',
+    '链接会自动打开余额充值弹窗;充值后用相同参数重试即可。底层鉴权/上游响应不会展示给商务用户。'
+  ].join('\n');
+}
+
+// 用户被禁用或无该接口权限(HTTP 403,区别于余额不足)。
+function buildPermissionDeniedMessage() {
+  return [
+    '当前账号被禁用或没有该接口的访问权限,暂时不能调用(这不是余额不足、也不是关键词/类目问题)。',
+    '',
+    '请联系服务方确认账号状态或接口权限后重试。底层鉴权/上游响应不会展示给商务用户。'
+  ].join('\n');
+}
+
 module.exports = {
 module.exports = {
   TIHAO_ECOMMERCE_APIG_ID,
   TIHAO_ECOMMERCE_APIG_ID,
   TIHAO_ECOMMERCE_APIG_PATH,
   TIHAO_ECOMMERCE_APIG_PATH,
@@ -139,6 +176,9 @@ module.exports = {
   resolveTihaoUserInfo,
   resolveTihaoUserInfo,
   buildMissingTokenMessage,
   buildMissingTokenMessage,
   buildRechargeRequiredMessage,
   buildRechargeRequiredMessage,
+  buildFmodeApiRechargeUrl,
+  buildFmodeApiRechargeMessage,
+  buildPermissionDeniedMessage,
   buildSocialRechargeInfo,
   buildSocialRechargeInfo,
   buildSocialRechargeUrl,
   buildSocialRechargeUrl,
   buildSocialMissingTokenMessage,
   buildSocialMissingTokenMessage,

+ 17 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/brief-parser.js

@@ -1,5 +1,6 @@
 const path = require('path');
 const path = require('path');
 const { readTextMaybe } = require('../../core/files');
 const { readTextMaybe } = require('../../core/files');
+const { extractPatternHits } = require('./brief-rule-patterns');
 
 
 function parseBrief(input = {}) {
 function parseBrief(input = {}) {
   const productContextText = normalizeTextInput(input.productIntro || input.productDescription || input.productContext || input.product);
   const productContextText = normalizeTextInput(input.productIntro || input.productDescription || input.productContext || input.product);
@@ -65,6 +66,7 @@ function parseBrief(input = {}) {
     blockedCreators,
     blockedCreators,
     exclusions,
     exclusions,
     performanceRequirements: resolvePerformanceRequirements(text),
     performanceRequirements: resolvePerformanceRequirements(text),
+    briefRulePatterns: extractPatternHits(text),
     deliveryRequirements: ['账号名称', '平台', '粉丝数', '报价', '推荐理由', '风险提示', '分级']
     deliveryRequirements: ['账号名称', '平台', '粉丝数', '报价', '推荐理由', '风险提示', '分级']
   };
   };
 }
 }
@@ -77,6 +79,12 @@ function resolvePerformanceRequirements(text) {
   if (cp1) requirements.push(`CP1≤${cp1[1]}`);
   if (cp1) requirements.push(`CP1≤${cp1[1]}`);
   const cpm = text.match(/CPM\s*[≤<=]\s*(\d+(?:\.\d+)?)/i);
   const cpm = text.match(/CPM\s*[≤<=]\s*(\d+(?:\.\d+)?)/i);
   if (cpm) requirements.push(`CPM≤${cpm[1]}`);
   if (cpm) requirements.push(`CPM≤${cpm[1]}`);
+  const cpc = text.match(/CPC\s*[≤<=<]\s*(\d+(?:\.\d+)?)/i);
+  if (cpc) requirements.push(`CPC≤${cpc[1]}`);
+  const cpv = text.match(/CPV\s*[≤<=<::]\s*(\d+(?:\.\d+)?)/i);
+  if (cpv) requirements.push(`CPV≤${cpv[1]}`);
+  const recentPlays = text.match(/近\s*3\s*条[^\n]{0,10}(?:平均)?播放\s*[>>≥]\s*(\d+(?:\.\d+)?)\s*万/);
+  if (recentPlays) requirements.push(`近3条平均播放>${recentPlays[1]}万`);
   const readMedian = text.match(/阅读中位数\s*[≧≥>=]\s*(\d+(?:,\d{3})*)/);
   const readMedian = text.match(/阅读中位数\s*[≧≥>=]\s*(\d+(?:,\d{3})*)/);
   if (readMedian) requirements.push(`阅读中位数≥${readMedian[1]}`);
   if (readMedian) requirements.push(`阅读中位数≥${readMedian[1]}`);
   const averageLikes = text.match(/(?:平均点赞|月均点赞|近\s*30\s*天平均点赞|近一个月平均点赞)[^\d]{0,12}(\d+(?:,\d{3})*)/);
   const averageLikes = text.match(/(?:平均点赞|月均点赞|近\s*30\s*天平均点赞|近一个月平均点赞)[^\d]{0,12}(\d+(?:,\d{3})*)/);
@@ -95,6 +103,15 @@ function resolvePerformanceRequirements(text) {
   if (homepageInteraction) requirements.push(`主页互动数据≥${homepageInteraction[1]}`);
   if (homepageInteraction) requirements.push(`主页互动数据≥${homepageInteraction[1]}`);
   const audience = text.match(/(\d{2}-\d{2}岁占比\d+%以上)/);
   const audience = text.match(/(\d{2}-\d{2}岁占比\d+%以上)/);
   if (audience) requirements.push(audience[1]);
   if (audience) requirements.push(audience[1]);
+  const ageRatio = text.match(/(\d{2})-(\d{2})岁[^\n]{0,8}占比\s*(\d+)%\s*以上/);
+  if (ageRatio) requirements.push(`${ageRatio[1]}-${ageRatio[2]}岁占比${ageRatio[3]}%以上`);
+  const underAgeRatio = text.match(/(\d+)岁以下[^\n]{0,6}(?:的|占比)?[^\n]{0,6}(?:不能超过|不超过|≤|<)\s*(\d+)%/);
+  if (underAgeRatio) requirements.push(`${underAgeRatio[1]}岁以下占比≤${underAgeRatio[2]}%`);
+  const interestTags = text.match(/兴趣[^\n]{0,6}(?:前三)?标签[^\n]{0,6}(?:含|包含)\s*(\d+)\s*个/);
+  if (interestTags) requirements.push(`兴趣前三标签含${interestTags[1]}个`);
+  const ipMustBe = text.match(/(?:达人)?\s*ip\s*(?:必须|要求|需|为|在)\s*([^\s,,。;\n]+)/i);
+  if (ipMustBe) requirements.push(`达人IP必须${ipMustBe[1]}`);
+  if (/(?:同|同一)个?省(?:份)?[^\n]{0,12}(?:不要|不|禁止)?\s*跨区域/.test(text)) requirements.push('同省KOL不要跨区域');
   return unique(requirements);
   return unique(requirements);
 }
 }
 
 

+ 212 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/brief-rule-patterns.js

@@ -0,0 +1,212 @@
+// 真实 brief 案例规则模式库(结构化自《AI提号需求列表.xlsx》13 个真实 brief 案例)
+// 用途:
+// 1. 知识库沉淀:把真实案例里的定量/定性规则结构化,作为 brief-parser 的校准样本。
+// 2. 规则模式:把「数据要求/人群画像/地域硬约束/量级分层」的常见表达抽象成正则,供 brief-parser 复用,
+//    让新 brief 里出现的同类要求能被自动识别(CPC/CPV/近3条播放/年龄占比/低龄占比/兴趣标签/达人IP)。
+
+// 13 个真实案例的结构化规则(每个案例提炼出可复用的规则维度)
+const CASE_PATTERNS = [
+  {
+    id: 'case-1-nest-space',
+    caseName: 'Nest空间探店',
+    platform: 'xiaohongshu',
+    category: '本地生活/家居探店',
+    accountTypes: ['精致生活', '家居探店'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: ['同省份KOL,不要跨区域(6城市探店)'],
+    tierRules: [],
+    budgetLayers: [{ scope: '人均预算', min: 6000, max: 10000, note: '报备视频探店,图文价格也要给' }],
+    notes: ['2位/城市', '五一假期档期']
+  },
+  {
+    id: 'case-2-dha',
+    caseName: 'DHA',
+    platform: 'xiaohongshu',
+    category: '母婴',
+    accountTypes: ['干货科普(营养师/育婴师妈妈)', '618合集(6个月-7岁孩子妈妈)'],
+    dataRequirements: ['CPE≤3', '阅读中位数≥4000', '预估阅读单价≤1.0'],
+    audienceConstraints: ['女性为主,25-44岁占比70%以上', '兴趣前三标签含2个(母婴/萌娃/记录生活)'],
+    regionConstraints: ['地域广东山东河南占比多为正常'],
+    tierRules: [],
+    budgetLayers: [{ scope: '单个预算', min: 1000, max: 5000 }],
+    notes: []
+  },
+  {
+    id: 'case-3-music',
+    caseName: '音乐类达人',
+    platform: 'douyin',
+    category: '音乐',
+    accountTypes: ['音乐达人'],
+    dataRequirements: ['近3条视频平均播放:头部>100w、中腰部>30w、尾部>5w'],
+    audienceConstraints: ['年龄18-23岁为主(可接受18-30岁)', '男女比例1:1或女性略高'],
+    regionConstraints: [],
+    tierRules: [
+      { tier: '头部', fans: '200w+', plays: '>100w', budgetMax: 50000 },
+      { tier: '中腰部', fans: '100w+', plays: '>30w', budgetMax: 8000 },
+      { tier: '尾部', fans: '30w+', plays: '>5w', budgetMax: 4000 }
+    ],
+    budgetLayers: [{ scope: '头部≤50000', min: 0, max: 50000 }, { scope: '中腰部≤8000', min: 0, max: 8000 }, { scope: '尾部≤4000', min: 0, max: 4000 }],
+    notes: []
+  },
+  {
+    id: 'case-4-dji',
+    caseName: '大疆',
+    platform: 'douyin',
+    category: '追星号',
+    accountTypes: ['追星账号', '垂类粉丝号(张凌赫)'],
+    dataRequirements: ['CPV:抖音0.072、小红书0.144'],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [{ tier: '追星号', fans: '千粉以上' }, { tier: '垂类粉丝号', fans: '百粉-千粉不限' }],
+    budgetLayers: [],
+    notes: ['提供素材二创二剪', '提报时间紧迫']
+  },
+  {
+    id: 'case-5-carlsberg',
+    caseName: '嘉士伯西夏啤酒',
+    platform: 'xiaohongshu',
+    category: '啤酒',
+    accountTypes: ['啤酒分享', '好物种草'],
+    dataRequirements: ['主页点赞100左右'],
+    audienceConstraints: [],
+    regionConstraints: ['达人IP必须为宁夏'],
+    tierRules: [{ tier: 'KOC', budget: 800, note: '不含平台费' }],
+    budgetLayers: [{ scope: 'KOC', min: 0, max: 800 }],
+    notes: ['需要20个', '档期5.30-6.30']
+  },
+  {
+    id: 'case-6-zelimei',
+    caseName: '泽立美医药',
+    platform: 'douyin',
+    category: '医药/湿疹',
+    accountTypes: ['医生(皮肤科/儿科中腰部+)', '科普达人(宝妈/健康)', '宝妈/科普/生活(小红书)'],
+    dataRequirements: ['医生板块CPM≤90、CPE≤5', '非医生科普CPM≤150、CPE≤5', '小红书CPM<100、CPC<0.8、CPE<15', '需自报爆款率、完播率、收藏率'],
+    audienceConstraints: ['2岁+特应性皮炎患儿宝妈、成人湿疹患者'],
+    regionConstraints: [],
+    tierRules: [],
+    budgetLayers: [{ scope: '医生板块15万/月' }, { scope: '非医生科普5万/月' }, { scope: '小红书3万/月' }],
+    notes: []
+  },
+  {
+    id: 'case-7-olebao',
+    caseName: '山东蓬莱欧乐堡',
+    platform: 'douyin',
+    category: '亲子游',
+    accountTypes: ['亲子生活'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: ['IP要求山东/江苏/河南/河北/浙江/山西/辽宁/北京/天津/大连'],
+    tierRules: [{ tier: '粉丝20w+' }],
+    budgetLayers: [{ scope: '单个预算', min: 0, max: 35000 }],
+    notes: ['性价比高一些']
+  },
+  {
+    id: 'case-8-qika',
+    caseName: '奇卡',
+    platform: 'xiaohongshu',
+    category: '粤语/港剧/拆卡',
+    accountTypes: ['拆卡类', '粤语vlog类', '港剧解说类', 'TVB解说类', '本地旅游/跨界类', '粤语教学类'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [],
+    budgetLayers: [{ scope: '单个预算', min: 3000, max: 5000 }],
+    notes: ['小红书/抖音分账号类型给数量']
+  },
+  {
+    id: 'case-9-keda-charging',
+    caseName: '科大智能充电桩',
+    platform: 'xiaohongshu',
+    category: '汽车/新能源',
+    accountTypes: ['汽车', '新能源'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [{ tier: '千粉以上' }],
+    budgetLayers: [{ scope: '单个预算', min: 0, max: 3000 }],
+    notes: []
+  },
+  {
+    id: 'case-10-washing-fridge',
+    caseName: '洗烘套装/冰箱',
+    platform: 'xiaohongshu',
+    category: '家电',
+    accountTypes: ['情侣+养宠双重人设达人', '健身/减肥/减脂(饮食管理方向)'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [],
+    budgetLayers: [{ scope: '单个预算', min: 10000, max: 30000 }],
+    notes: ['洗烘套装5位(情侣+养宠双人设)', '冰箱8系5位(饮食管理,不要纯健身秀身材)']
+  },
+  {
+    id: 'case-11-tuborg',
+    caseName: '乐堡啤酒',
+    platform: 'xiaohongshu',
+    category: '啤酒',
+    accountTypes: ['MBTI垂类号(KOL)', '职场/生活/普通个人(KOC)'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [{ tier: 'KOL', budgetMax: 8000 }, { tier: 'KOC', budgetMax: 1000 }],
+    budgetLayers: [{ scope: 'KOL', min: 0, max: 8000 }, { scope: 'KOC', min: 0, max: 1000 }],
+    notes: ['近3个月不能有嘉士伯相关合作(竞品排他)', '档期5.13-5.17']
+  },
+  {
+    id: 'case-12-gree',
+    caseName: '格力哈工程',
+    platform: 'xiaohongshu',
+    category: '家电/空调',
+    accountTypes: ['生活类(哈工程学生身份)', '新闻资讯号', '营销号'],
+    dataRequirements: [],
+    audienceConstraints: [],
+    regionConstraints: [],
+    tierRules: [],
+    budgetLayers: [{ scope: '单个预算', min: 1000, max: 6000 }],
+    notes: ['提报数量20个', '弱化商业营销痕迹']
+  },
+  {
+    id: 'case-13-elephant',
+    caseName: '大象胶原蛋白',
+    platform: 'xiaohongshu',
+    category: '胶原蛋白/护肤',
+    accountTypes: ['测评类', '好物种草', '情侣'],
+    dataRequirements: [],
+    audienceConstraints: ['粉丝中18岁以下占比不超过10%'],
+    regionConstraints: [],
+    tierRules: [{ tier: '10W以下' }],
+    budgetLayers: [{ scope: '单个预算', min: 0, max: 4000 }],
+    notes: ['测评对标(大象胶原蛋白 vs 修丽可胶原蛋白精华)']
+  }
+];
+
+// 可复用规则模式:真实案例里出现的定量/定性表达 → 正则,供 brief-parser 识别同类新 brief。
+const PATTERN_LIBRARY = {
+  cpc: { regex: /CPC\s*[≤<=<]\s*(\d+(?:\.\d+)?)/i, label: 'CPC', note: '单次点击成本' },
+  cpv: { regex: /CPV\s*[≤<=<::]\s*(\d+(?:\.\d+)?)/i, label: 'CPV', note: '单次播放成本' },
+  recentPlays: { regex: /近\s*3\s*条[^\n]{0,10}(?:平均)?播放\s*[>>≥]\s*(\d+(?:\.\d+)?)\s*万/i, label: '近3条平均播放', note: '近期内容爆款线' },
+  ageRatio: { regex: /(\d{2})-(\d{2})岁[^\n]{0,8}占比\s*(\d+)%\s*以上/i, label: '年龄占比', note: '粉丝年龄结构' },
+  underAgeRatio: { regex: /(\d+)岁以下[^\n]{0,6}(?:的|占比)?[^\n]{0,6}(?:不能超过|不超过|≤|<)\s*(\d+)%/i, label: '低龄占比上限', note: '低龄粉丝比例红线' },
+  interestTags: { regex: /兴趣[^\n]{0,6}(?:前三)?标签[^\n]{0,6}(?:含|包含)\s*(\d+)\s*个/i, label: '兴趣标签', note: '兴趣前三标签命中数' },
+  ipMustBe: { regex: /(?:达人|博主|KOL)?\s*ip\s*(?!授权|联名|形象|周边|运营)(?:必须|要求|需|为|在)\s*([^\s,,。;\n]+)/i, label: '达人IP硬约束', note: '达人地域/IP 硬性要求(排除品牌 IP 授权/联名等误报)' },
+  sameProvince: { regex: /(?:同|同一)个?省(?:份)?[^\n]{0,12}(?:不要|不|禁止)?\s*跨区域/i, label: '同省不跨区', note: '地域同省硬约束' }
+};
+
+// 从文本中提取命中的规则模式,返回 [{ key, label, match, note }]
+function extractPatternHits(text = '') {
+  const hits = [];
+  for (const [key, pattern] of Object.entries(PATTERN_LIBRARY)) {
+    const match = String(text || '').match(pattern.regex);
+    if (match) {
+      hits.push({ key, label: pattern.label, note: pattern.note, raw: match[0] });
+    }
+  }
+  return hits;
+}
+
+module.exports = {
+  CASE_PATTERNS,
+  PATTERN_LIBRARY,
+  extractPatternHits
+};

+ 100 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/compliance-rules.js

@@ -0,0 +1,100 @@
+// 内容合规白名单(结构化自《02-项目执行SOP流程(优化版).xlsx》「内容合规白名单」sheet)
+// 用途:提号报告里对内容脚本做用词风险提示,把高危词替换为安全词,规避平台审核违规。
+
+// 高危词 → 安全替代词(左列 → 右列)
+const BANNED_WORD_REPLACEMENTS = [
+  { banned: '美白', safe: '提亮肤色 / 均匀肤色', note: '「美白」属功效宣称词,需严格规避' },
+  { banned: '祛痘', safe: '舒缓痘痘 / 改善痘痘肌', note: '「祛」字有治疗含义,改为「改善/舒缓」' },
+  { banned: '平替', safe: '高性价比选择', note: '「平替」暗示竞品对比,存在侵权风险' },
+  { banned: '特效', safe: '体验感 / 有助于', note: '绝对化用词,平台审核高度敏感' },
+  { banned: '神器', safe: '体验感 / 有助于', note: '绝对化用词,平台审核高度敏感' },
+  { banned: '第一', safe: '领先 / 专研', note: '禁止使用排名性/绝对化表述' },
+  { banned: '最好', safe: '领先 / 专研', note: '禁止使用排名性/绝对化表述' },
+  { banned: '根治', safe: '有助于 / 改善', note: '绝对化功效用词' }
+];
+
+// 通用规则(风险类型 → 禁止行为 → 安全替代)
+const GENERAL_RULES = [
+  {
+    type: '功效描述',
+    banned: '禁用「第一/最/特效/根治」等绝对化用词',
+    safe: '改用「有助于/体验感更好」;例:「皮肤细腻度提升」替代「美白」'
+  },
+  {
+    type: '促销引导',
+    banned: '小红书禁止「限时折扣/跳转购买」强引流',
+    safe: '改用「可前往主页了解」;抖音需挂「购物车」组件才可引流'
+  }
+];
+
+// 平台特规(平台 → 高危雷区 → 安全操作)
+const PLATFORM_RULES = {
+  xiaohongshu: {
+    banned: ['使用虚假对比图(前后P图)', '引导私信交易'],
+    safe: ['使用实验室仪器检测实拍视频', '标注「合作信息请邮件咨询」']
+  },
+  douyin: {
+    banned: ['未在视频开头标注「广告」', '抽奖活动无公示说明'],
+    safe: ['口播前3秒明示「广告合作」', '抽奖发布24小时后开奖并公示']
+  },
+  bilibili: {
+    banned: ['挂载未过审商品链接', '未说明利益关系'],
+    safe: ['商品链接发布前提交平台预审', '视频简介首行标注「感谢XX品牌赞助」']
+  }
+};
+
+// 安全词库(以护肤产品为例,可直接使用)
+const SAFE_WORDS = [
+  '控油力', '水油平衡', '肤感清爽', '温和配方', '精准滴管',
+  '持妆时长', '成分透明', '皮肤屏障', '焕新肌感'
+];
+
+// 使用流程(审核 SOP)
+const WORKFLOW = [
+  '① 客户确认词库(项目启动会后1日内)',
+  '② 同步达人创作规范(BD提报时附上)',
+  '③ 脚本审核时用词标红检测(推荐工具:零克查词)',
+  '④ 发布后存档备案(按项目ID归档)'
+];
+
+// 对 brief 文本/候选内容做合规词命中检测,返回需要替换的高危词。
+function detectComplianceRisks(text = '') {
+  const hits = [];
+  for (const item of BANNED_WORD_REPLACEMENTS) {
+    if (String(text || '').includes(item.banned)) {
+      hits.push({ banned: item.banned, safe: item.safe, note: item.note });
+    }
+  }
+  return hits;
+}
+
+// 生成合规检查结果,供 report 展示。
+function buildComplianceCheck(criteria = {}) {
+  const platforms = criteria.platforms || [];
+  const text = [criteria.rawText, criteria.brand, criteria.category].filter(Boolean).join(' ');
+  const bannedHits = detectComplianceRisks(text);
+  const platformRules = platforms
+    .map(platform => ({ platform, ...(PLATFORM_RULES[platform] || {}) }))
+    .filter(item => item.banned || item.safe);
+  return {
+    bannedWordReplacements: BANNED_WORD_REPLACEMENTS,
+    generalRules: GENERAL_RULES,
+    platformRules,
+    safeWords: SAFE_WORDS,
+    workflow: WORKFLOW,
+    bannedHits,
+    note: bannedHits.length
+      ? `检出 ${bannedHits.length} 处高危词,脚本审核时需替换为安全词后再发布。`
+      : '本轮 Brief 未命中高危词;达人脚本仍需按白名单逐条复核。'
+  };
+}
+
+module.exports = {
+  BANNED_WORD_REPLACEMENTS,
+  GENERAL_RULES,
+  PLATFORM_RULES,
+  SAFE_WORDS,
+  WORKFLOW,
+  detectComplianceRisks,
+  buildComplianceCheck
+};

+ 8 - 1
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/experience-rules.js

@@ -1,14 +1,21 @@
+const { buildGoalStrategy } = require('./goal-strategy');
+const { buildComplianceCheck } = require('./compliance-rules');
+
 function enrichCriteriaWithExperience(criteria = {}) {
 function enrichCriteriaWithExperience(criteria = {}) {
   const categoryRules = buildCategoryRules(criteria);
   const categoryRules = buildCategoryRules(criteria);
   const requirementLayers = buildRequirementLayers(criteria, categoryRules);
   const requirementLayers = buildRequirementLayers(criteria, categoryRules);
   const referenceUsability = buildReferenceUsability(criteria);
   const referenceUsability = buildReferenceUsability(criteria);
   const sourcingStrategy = buildPlatformSourcingStrategy(criteria);
   const sourcingStrategy = buildPlatformSourcingStrategy(criteria);
+  const goalStrategy = buildGoalStrategy(criteria);
+  const compliance = buildComplianceCheck(criteria);
   return {
   return {
     ...criteria,
     ...criteria,
     categoryRules,
     categoryRules,
     requirementLayers,
     requirementLayers,
     referenceUsability,
     referenceUsability,
-    sourcingStrategy
+    sourcingStrategy,
+    goalStrategy,
+    compliance
   };
   };
 }
 }
 
 

+ 78 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/few-shot-cases.js

@@ -0,0 +1,78 @@
+// few-shot 案例库运行时接入
+// 把案例导入器产出的 fewshot-cases.json 在提号时自动加载,按品类/平台/投放目标匹配历史相似案例,
+// 作为正/负样本参照附加到 criteria,供 report 展示「历史相似案例参照」。
+// 边界:只做「参照提示」,不静默替代评分/人工判断。
+
+const fs = require('fs');
+const path = require('path');
+
+// 默认候选路径(案例导入器 `npm run case:import` 的默认输出)
+const DEFAULT_PATHS = [
+  // 技能包内打包路径(case:import 会双写到这里,随 npm 包分发,优先读取)
+  path.join(__dirname, '..', '..', '..', '..', 'docs', 'case-library', 'fewshot-cases.json'),
+  // 工作区根目录 docs(资料目录,开发者本地留档)
+  path.join(__dirname, '..', '..', '..', '..', '..', 'docs', '20260813 - AI提号规则及喂养资料', '历史选号案例', 'case-import', 'fewshot-cases.json'),
+  // 技能包内 docs 的资料目录(兼容旧布局)
+  path.join(__dirname, '..', '..', '..', '..', 'docs', '20260813 - AI提号规则及喂养资料', '历史选号案例', 'case-import', 'fewshot-cases.json')
+];
+
+function loadFewshotCases(explicitPath) {
+  const candidates = [...(explicitPath ? [explicitPath] : []), ...DEFAULT_PATHS];
+  for (const file of candidates) {
+    try {
+      if (fs.existsSync(file)) {
+        const data = JSON.parse(fs.readFileSync(file, 'utf8'));
+        if (Array.isArray(data.cases)) return { ...data, file };
+      }
+    } catch (error) {
+      // 解析失败继续尝试下一个路径
+    }
+  }
+  return null;
+}
+
+// 按品类 / 平台 / 投放目标匹配历史相似案例,返回带 score 的排序结果。
+function matchSimilarCases(cases, criteria = {}) {
+  const category = String(criteria.category || '');
+  const goal = String(criteria.goalStrategy?.goalLabel || '');
+  const platform = (criteria.platforms || [])[0] || '';
+  const scored = (cases || []).map(item => {
+    let score = 0;
+    if (item.category && category && (item.category.includes(category) || category.includes(item.category))) score += 2;
+    if (item.platform && platform && item.platform === platform) score += 1;
+    if (item.goal && goal && (item.goal.includes(goal) || goal.includes(item.goal))) score += 1;
+    return { ...item, score };
+  });
+  return scored
+    .filter(item => item.score > 0)
+    .sort((a, b) => b.score - a.score)
+    .slice(0, 6);
+}
+
+function buildFewshotReference(criteria = {}, input = {}) {
+  const loaded = loadFewshotCases(input.fewshotCasesPath || input.caseLibraryPath);
+  if (!loaded) {
+    return {
+      status: 'no_case_library',
+      note: '未加载历史案例库(fewshot-cases.json);运行 `npm run case:import` 后即可在提号时自动参照历史正/负样本。',
+      totalCases: 0,
+      cases: []
+    };
+  }
+  const similar = matchSimilarCases(loaded.cases, criteria);
+  return {
+    status: 'loaded',
+    note: similar.length
+      ? `已从历史案例库匹配到 ${similar.length} 条相似案例(品类/平台/投放目标)。`
+      : '已加载历史案例库,但本轮未匹配到同品类/同目标的案例。',
+    totalCases: loaded.cases.length,
+    cases: similar
+  };
+}
+
+module.exports = {
+  DEFAULT_PATHS,
+  loadFewshotCases,
+  matchSimilarCases,
+  buildFewshotReference
+};

+ 104 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/goal-strategy.js

@@ -0,0 +1,104 @@
+// 投放目标 → 选号策略映射 + 提号 SOP 规则
+// 结构化自《01-资源寻找方法(更新版).docx》STEP06(没有参考账号时,按投放目标反推账号类型)
+// 以及《AI提号需求列表》SOP 提号规则(默认 1.5 倍提号、前 10 篇 80% 风格匹配、优先响应明确数量)。
+
+// 投放目标 → 账号类型映射。signals 用于从 brief 文本识别投放目标。
+const GOAL_STRATEGIES = {
+  seed_conversion: {
+    zh: '种草转化',
+    signals: ['种草', '转化', '带货', 'ROI', '卖货', '成交', '下单', '电商', '销售', '促转化', '转化率', '转化效果'],
+    accountTypes: ['真实测评账号', '垂类KOC', '素人真实体验'],
+    tierHint: '偏尾部KOC与素人,重真实体验与转化,不要只看粉丝量',
+    note: '重转化时优先真实测评、垂类KOC、素人真实体验;转化链路看 CPE/CPV 与评论区购买意图。'
+  },
+  brand_exposure: {
+    zh: '品牌曝光',
+    signals: ['曝光', '品牌', '声量', '认知', '知名度', '破圈', '泛流量', '打爆', '传播', '话题', '热度', '出圈'],
+    accountTypes: ['垂类头部腰部KOL', '泛流量账号'],
+    tierHint: '偏头部腰部KOL,重覆盖与声量,兼顾垂类可信度',
+    note: '重曝光时优先垂类头部腰部KOL与泛流量账号,兼顾专业背书与覆盖。'
+  },
+  search_drain: {
+    zh: '搜索引流',
+    signals: ['搜索', '引流', '关键词', '小蓝词', '沉淀', 'SEO', '搜索词', '蓝词', '长尾', '卡位', '收录'],
+    accountTypes: ['带关键词账号', '可沉淀内容账号', '小蓝词卡位账号'],
+    tierHint: '内容必须带目标搜索关键词、可长期沉淀,卡搜索词/小蓝词',
+    note: '搜索引流看重内容能否带目标关键词、能否长期沉淀,优先能卡搜索词/小蓝词的账号。'
+  },
+  campaign_volume: {
+    zh: '活动冲量',
+    signals: ['冲量', '活动', '铺量', '同城', '本地', '促销', '大促', '节点', '爆发', '批量', '拉新', '引流到店', '到店'],
+    accountTypes: ['铺量素人', '同城本地账号'],
+    tierHint: '铺量素人 + 同城本地,重数量与节点节奏',
+    note: '活动冲量偏铺量素人与同城本地账号,重数量和节点节奏。'
+  }
+};
+
+// 提号 SOP 规则(喂给 AI 提升选中率的硬规则)
+const SUGGESTION_RULES = [
+  {
+    id: 'suggest-1.5x',
+    title: '默认提号数量 = 需求数量的 1.5 倍以上',
+    multiplier: 1.5,
+    description: '默认提号数量按需求数量的 1.5 倍以上输出,给客户更充分的选择与替换空间。'
+  },
+  {
+    id: 'style-match-80',
+    title: '前 10 篇笔记至少 80% 风格匹配',
+    threshold: 0.8,
+    description: '候选账号最近 10 篇内容至少 80% 匹配 Brief 调性,否则降级为需复核。'
+  },
+  {
+    id: 'respond-explicit-count',
+    title: '优先响应明确数量与平台分布',
+    description: 'Brief 明确写了提号数量/平台分布时,严格按数量响应,不自行缩减。'
+  }
+];
+
+function detectGoal(text) {
+  const scores = {};
+  for (const [goal, strategy] of Object.entries(GOAL_STRATEGIES)) {
+    scores[goal] = strategy.signals.filter(signal => String(text || '').includes(signal)).length;
+  }
+  let best = 'brand_exposure';
+  let bestScore = -1;
+  for (const [goal, score] of Object.entries(scores)) {
+    if (score > bestScore) {
+      best = goal;
+      bestScore = score;
+    }
+  }
+  return best;
+}
+
+function buildGoalStrategy(criteria = {}) {
+  const text = [
+    criteria.rawText,
+    criteria.brand,
+    criteria.category,
+    ...(criteria.keywords || [])
+  ].filter(Boolean).join(' ');
+  const goal = detectGoal(text);
+  const strategy = GOAL_STRATEGIES[goal] || GOAL_STRATEGIES.brand_exposure;
+  const targetCount = Number(criteria.targetCount || 0);
+  const suggestionCount = targetCount ? Math.ceil(targetCount * 1.5) : 15;
+  return {
+    goal,
+    goalLabel: strategy.zh,
+    accountTypes: strategy.accountTypes,
+    tierHint: strategy.tierHint,
+    note: strategy.note,
+    suggestionCount,
+    suggestionRules: SUGGESTION_RULES,
+    suggestionNote: targetCount
+      ? `Brief 目标 ${targetCount} 位,按 1.5 倍规则建议默认提号 ${suggestionCount} 位以上。`
+      : 'Brief 未明确目标人数,建议默认提号 15 位以上供客户选择。'
+  };
+}
+
+module.exports = {
+  GOAL_STRATEGIES,
+  SUGGESTION_RULES,
+  detectGoal,
+  buildGoalStrategy
+};

+ 41 - 13
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/live-provider.js

@@ -1,8 +1,10 @@
-const { readTihaoToken } = require('../../core/credentials');
+const { readTihaoToken, readNewApiToken } = require('../../core/credentials');
 const {
 const {
   buildTihaoRechargeInfo,
   buildTihaoRechargeInfo,
   buildMissingTokenMessage,
   buildMissingTokenMessage,
-  buildRechargeRequiredMessage,
+  buildFmodeApiRechargeUrl,
+  buildFmodeApiRechargeMessage,
+  buildPermissionDeniedMessage,
   resolveTihaoUserInfo
   resolveTihaoUserInfo
 } = require('../../core/payment-links');
 } = require('../../core/payment-links');
 const { friendlyState } = require('../../core/result-envelope');
 const { friendlyState } = require('../../core/result-envelope');
@@ -10,8 +12,12 @@ const { friendlyState } = require('../../core/result-envelope');
 const DEFAULT_BASE_URL = 'https://server.fmode.cn/api/voc-e-commerce';
 const DEFAULT_BASE_URL = 'https://server.fmode.cn/api/voc-e-commerce';
 
 
 async function searchLiveCreators(criteria, input = {}) {
 async function searchLiveCreators(criteria, input = {}) {
-  const token = readTihaoToken(input);
-  const recharge = await buildTihaoRechargeInfo({ sessionToken: token });
+  // 计费优先走 NewAPI(fmode-api)的 sk- token;该 token 鉴权失败(401)时回退平台 sessionToken。
+  const newapiToken = readNewApiToken(input);
+  const sessionToken = (readTihaoToken(input));
+  const token = newapiToken || sessionToken;
+  const fallbackToken = newapiToken ? sessionToken : '';
+  const recharge = await buildTihaoRechargeInfo({ sessionToken: (sessionToken || token) });
   if (!token) {
   if (!token) {
     return friendlyState('needs_token', buildMissingTokenMessage(recharge.paymentUrl), {
     return friendlyState('needs_token', buildMissingTokenMessage(recharge.paymentUrl), {
       data: { recharge }
       data: { recharge }
@@ -19,7 +25,7 @@ async function searchLiveCreators(criteria, input = {}) {
   }
   }
 
 
   const baseUrl = input.baseUrl || process.env.TIHAO_ECOMMERCE_BASE_URL || DEFAULT_BASE_URL;
   const baseUrl = input.baseUrl || process.env.TIHAO_ECOMMERCE_BASE_URL || DEFAULT_BASE_URL;
-  const identity = await resolveTihaoUserInfo(token);
+  const identity = await resolveTihaoUserInfo(sessionToken || token);
   const companyId = input.company || process.env.TIHAO_COMPANY || process.env.VOC_ECOMMERCE_COMPANY || identity.companyId || '';
   const companyId = input.company || process.env.TIHAO_COMPANY || process.env.VOC_ECOMMERCE_COMPANY || identity.companyId || '';
   const platforms = criteria.platforms.filter(platform => platform === 'xiaohongshu' || platform === 'douyin');
   const platforms = criteria.platforms.filter(platform => platform === 'xiaohongshu' || platform === 'douyin');
   const recallKeywords = buildRecallKeywords(criteria, input);
   const recallKeywords = buildRecallKeywords(criteria, input);
@@ -46,12 +52,7 @@ async function searchLiveCreators(criteria, input = {}) {
         if (companyId) url.searchParams.set('company', String(companyId));
         if (companyId) url.searchParams.set('company', String(companyId));
         if (input.isRefresh) url.searchParams.set('isRefresh', 'true');
         if (input.isRefresh) url.searchParams.set('isRefresh', 'true');
 
 
-        const response = await fetchWithRetry(url, {
-          headers: {
-            Authorization: `Bearer ${token}`,
-            Accept: 'application/json'
-          }
-        });
+        const response = await fetchWithFallback(url, token, fallbackToken);
 
 
         if (!response) {
         if (!response) {
           return friendlyState(
           return friendlyState(
@@ -75,9 +76,17 @@ async function searchLiveCreators(criteria, input = {}) {
             data: { recharge }
             data: { recharge }
           });
           });
         }
         }
+        if (response.status === 402) {
+          // NewAPI(fmode-api)余额不足,引导到 fmode-api 余额充值弹窗。
+          const fmodeApiRechargeUrl = buildFmodeApiRechargeUrl();
+          return friendlyState('needs_recharge', buildFmodeApiRechargeMessage(fmodeApiRechargeUrl), {
+            data: { recharge: { paymentUrl: fmodeApiRechargeUrl, kind: 'fmode-api' } }
+          });
+        }
         if (response.status === 403) {
         if (response.status === 403) {
-          return friendlyState('needs_recharge', buildRechargeRequiredMessage(recharge.paymentUrl), {
-            data: { recharge }
+          // 用户被禁用/无权限,区别于余额不足。
+          return friendlyState('needs_permission', buildPermissionDeniedMessage(), {
+            data: {}
           });
           });
         }
         }
         if (!response.ok) {
         if (!response.ok) {
@@ -248,6 +257,23 @@ async function fetchWithRetry(url, options) {
   return null;
   return null;
 }
 }
 
 
+// 先用 newapi 的 sk- token 请求;若返回 401(鉴权失败,例如服务端未迁移 / 该号未入仓 /
+// token 未被接受)且提供了回退 token(平台 sessionToken),自动用回退 token 重发一次。
+// 402(余额不足)/403(无权限)不回退,直接交给上层按状态码处理。
+async function fetchWithFallback(url, token, fallbackToken) {
+  const buildOptions = bearer => ({
+    headers: {
+      Authorization: `Bearer ${bearer}`,
+      Accept: 'application/json'
+    }
+  });
+  const response = await fetchWithRetry(url, buildOptions(token));
+  if (response && response.status === 401 && fallbackToken && fallbackToken !== token) {
+    return fetchWithRetry(url, buildOptions(fallbackToken));
+  }
+  return response;
+}
+
 function resolveProxyPath(platform) {
 function resolveProxyPath(platform) {
   if (platform === 'douyin') return 'douyin-xingtu/gw/api/gsearch/search_for_author_square/v1';
   if (platform === 'douyin') return 'douyin-xingtu/gw/api/gsearch/search_for_author_square/v1';
   return 'xiaohongshu-pgy/api/solar/cooperator/blogger/v2/v1';
   return 'xiaohongshu-pgy/api/solar/cooperator/blogger/v2/v1';
@@ -296,6 +322,7 @@ function normalizeCreator(item, platform, index) {
   ].map(item => String(item).trim()).filter(Boolean))].slice(0, 12);
   ].map(item => String(item).trim()).filter(Boolean))].slice(0, 12);
   const secUid = String(item.secUid || item.sec_uid || item.secId || item.sec_id || item.authorSecUid || item.author_sec_uid || attr.secUid || attr.sec_uid || '');
   const secUid = String(item.secUid || item.sec_uid || item.secId || item.sec_id || item.authorSecUid || item.author_sec_uid || attr.secUid || attr.sec_uid || '');
   const userId = String(secUid || item.userId || item.user_id || item.star_id || attr.core_user_id || attr.id || item.id || `live-${index}`);
   const userId = String(secUid || item.userId || item.user_id || item.star_id || attr.core_user_id || attr.id || item.id || `live-${index}`);
+  const shortId = String(item.redId || item.red_id || item.uniqueId || item.unique_id || item.douyinId || item.douyin_id || item.shortId || item.short_id || attr.unique_id || attr.red_id || '');
   const imagePrice = pickNumberDeep(sources, [
   const imagePrice = pickNumberDeep(sources, [
     'picturePrice', 'picture_price', 'imagePrice', 'image_price', 'notePrice', 'note_price',
     'picturePrice', 'picture_price', 'imagePrice', 'image_price', 'notePrice', 'note_price',
     'graphicPrice', 'graphicsPrice', 'graphics_price', 'imageTextPrice', 'image_text_price',
     'graphicPrice', 'graphicsPrice', 'graphics_price', 'imageTextPrice', 'image_text_price',
@@ -316,6 +343,7 @@ function normalizeCreator(item, platform, index) {
   return {
   return {
     platform,
     platform,
     platformUserId: userId,
     platformUserId: userId,
+    platformShortId: shortId,
     displayName: String(item.name || item.nickname || item.nickName || attr.nick_name || attr.nickname || attr.name || ''),
     displayName: String(item.name || item.nickname || item.nickName || attr.nick_name || attr.nickname || attr.name || ''),
     profileUrl: resolveProfileUrl(item, attr, platform, userId, secUid),
     profileUrl: resolveProfileUrl(item, attr, platform, userId, secUid),
     fansCount: toNumber(item.fansNum || item.fansCount || item.fans_count || item.followerCount || attr.follower || attr.fans_count),
     fansCount: toNumber(item.fansNum || item.fansCount || item.fans_count || item.followerCount || attr.follower || attr.fans_count),

+ 137 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/media-library-schema.js

@@ -0,0 +1,137 @@
+// 公司媒体资源库字段 schema(结构化自《媒体资源库及来源》/ media_library_template.xlsx 表头)
+// 边界:仅沉淀字段定义 + 建表参考 + 选号查询映射,供未来建库/开发查询接口时直接消费,不在此建库。
+// 与 pgy-schema(蒲公英采集侧)互补:pgy 是「爬虫采集的原始博主数据」,这里是「人工维护 + 商单交付侧」资源库,
+// 两者通过 nickname / blogger_id / fans_count / 报价 / 互动率 等字段对账打通。
+
+// 字段表(字段名 → 中文/类型/是否必填/选号维度)。
+// 必填字段带 required: true,对应表头里「*」标记(平台/内容类型/达人昵称/粉丝量/主页链接/报价链接/粉丝画像/含税报价)。
+const CORE_FIELDS = {
+  platform: { zh: '平台', type: 'string', required: true, dimension: 'platform', note: '小红书 / 抖音 / B站 等' },
+  content_type: { zh: '内容类型', type: 'string', required: true, dimension: 'contentType', note: '图文 / 视频 / 直播' },
+  nickname: { zh: '达人昵称', type: 'string', required: true, dimension: 'id' },
+  blogger_id: { zh: '达人ID', type: 'string', dimension: 'id', note: '平台侧唯一 ID,用于与蒲公英库对账' },
+  fans_count: { zh: '粉丝量', type: 'int', required: true, dimension: 'fanRange' },
+  homepage_url: { zh: '主页链接', type: 'string', required: true, dimension: 'profileUrl' },
+  quote_url: { zh: '报价链接', type: 'string', required: true, dimension: 'price' },
+  avg_play_30d: { zh: '近30天均播', type: 'int', dimension: 'interaction' },
+  fans_profile: { zh: '粉丝画像', type: 'string', required: true, dimension: 'fansProfile', note: '年龄段 / 性别 / 城市 描述' },
+  tax_inclusive_price: { zh: '含税报价', type: 'number', required: true, dimension: 'price', unit: '元' },
+  schedule: { zh: '档期', type: 'string', dimension: 'availability', note: '可合作档期' },
+  case_url: { zh: '代表案例链接', type: 'string', dimension: 'evidence' },
+  report_form: { zh: '报备形式', type: 'string', dimension: 'commercial', note: '报备 / 非报备' },
+  picture_price: { zh: '图文价格', type: 'number', dimension: 'price', unit: '元' },
+  video_price: { zh: '视频价格', type: 'number', dimension: 'price', unit: '元' },
+  cpv: { zh: 'CPV(单次播放成本)', type: 'number', dimension: 'cpm', unit: '元' },
+  cpe: { zh: 'CPE(单次互动成本)', type: 'number', dimension: 'cpm', unit: '元' },
+  read_median: { zh: '阅读中位数', type: 'number', dimension: 'cpm' },
+  interaction_median: { zh: '互动中位数', type: 'number', dimension: 'interaction' },
+  style: { zh: '风格', type: 'string', dimension: 'verticality' },
+  recent_status: { zh: '近况', type: 'string', dimension: 'activity' },
+  tier: { zh: 'tier层级', type: 'string', dimension: 'tier', note: '头部/腰部/尾部 KOL / KOC' },
+  verticality: { zh: '垂直度', type: 'number', dimension: 'verticality', unit: '%' },
+  commercial_ratio: { zh: '商业内容占比', type: 'number', dimension: 'commercial', unit: '%' },
+  interaction_rate: { zh: '互动率', type: 'number', dimension: 'interaction', unit: '%' },
+  completion_rate: { zh: '完播率', type: 'number', dimension: 'completionRate', unit: '%' },
+  platform_score: { zh: '平台评分', type: 'number', dimension: 'score', unit: '分' },
+  growth_trend: { zh: '增长趋势', type: 'string', dimension: 'growth' },
+  fan_club_count: { zh: '粉丝团人数', type: 'int', dimension: 'fansProfile' },
+  live_avg_online: { zh: '直播场均在线', type: 'int', dimension: 'live' },
+  charging_count: { zh: '充电人数', type: 'int', dimension: 'live' },
+  avg_like_count: { zh: '平均赞同数', type: 'int', dimension: 'interaction' },
+  verified: { zh: '认证', type: 'string', dimension: 'verified' },
+  update_frequency: { zh: '更新频率', type: 'string', dimension: 'activity' },
+  remark: { zh: '备注', type: 'string', dimension: 'remark' }
+};
+
+// 必填字段(表头「*」标记,入库时缺一不可)
+const REQUIRED_FIELDS = Object.keys(CORE_FIELDS).filter(key => CORE_FIELDS[key].required);
+
+// 商单交付侧字段(蒲公英爬虫采不到、需人工补录的字段)
+const MANUAL_ONLY_FIELDS = [
+  'schedule', 'case_url', 'report_form', 'tax_inclusive_price',
+  'style', 'recent_status', 'tier', 'verified', 'update_frequency', 'remark'
+];
+
+// 与蒲公英采集库可对账的字段(公司库字段 → 蒲公英字段,用于两库打通去重)
+const PGY_ALIAS = {
+  blogger_id: 'blogger_id',
+  nickname: 'nickname',
+  fans_count: 'follower_count',
+  picture_price: 'picture_price',
+  video_price: 'video_price',
+  interaction_rate: 'interaction_rate',
+  completion_rate: 'video_full_view_rate',
+  platform_score: 'credit_level',
+  verticality: 'content_tags',
+  commercial_ratio: 'co_note_count',
+  read_median: 'read_midnum',
+  interaction_median: 'interact_midnum'
+};
+
+// 建议索引(选号查询热路径)
+const INDEXES = [
+  { name: 'uk_blogger_id', spec: { platform: 1, blogger_id: 1 }, options: { unique: true } },
+  { name: 'idx_platform_fans_desc', spec: { platform: 1, fans_count: -1 }, options: {} },
+  { name: 'idx_platform_tier', spec: { platform: 1, tier: 1, fans_count: -1 }, options: {} },
+  { name: 'idx_platform_price_asc', spec: { platform: 1, tax_inclusive_price: 1 }, options: {} },
+  { name: 'idx_platform_interaction_desc', spec: { platform: 1, interaction_rate: -1 }, options: {} },
+  { name: 'idx_platform_verticality', spec: { platform: 1, verticality: -1 }, options: {} },
+  { name: 'idx_platform_cpe_asc', spec: { platform: 1, cpe: 1, cpv: 1 }, options: {} }
+];
+
+// 生成 MongoDB 建表 + 索引 DDL 文本
+function buildDdl(collectionName = 'media_library') {
+  const lines = [];
+  lines.push('// 公司媒体资源库 collection(MongoDB)—— 由 media-library-schema 生成,未来建库直接执行');
+  lines.push(`db.createCollection('${collectionName}');`);
+  lines.push('');
+  for (const index of INDEXES) {
+    lines.push(`db.${collectionName}.createIndex(${JSON.stringify(index.spec)}, ${JSON.stringify({ name: index.name, ...index.options })});`);
+  }
+  lines.push('');
+  lines.push('// 必填字段(缺一不可,对应模板「*」标记):');
+  lines.push(`// ${REQUIRED_FIELDS.join(', ')}`);
+  return lines.join('\n');
+}
+
+// 生成选号查询映射 markdown
+function buildQueryMap() {
+  const lines = [];
+  lines.push('# 公司媒体资源库字段映射');
+  lines.push('');
+  lines.push('> 由 media-library-schema 生成。公司侧资源库(人工维护 + 商单交付)字段说明。');
+  lines.push('');
+  lines.push(`- 字段总数:${Object.keys(CORE_FIELDS).length},必填 ${REQUIRED_FIELDS.length} 个。`);
+  lines.push('');
+  lines.push('## 必填字段(模板「*」标记)');
+  lines.push('');
+  lines.push('| 字段 | 中文 | 类型 | 选号维度 |');
+  lines.push('| --- | --- | --- | --- |');
+  for (const key of REQUIRED_FIELDS) {
+    const field = CORE_FIELDS[key];
+    lines.push(`| \`${key}\` | ${field.zh} | ${field.type}${field.unit ? `(${field.unit})` : ''} | ${field.dimension} |`);
+  }
+  lines.push('');
+  lines.push('## 商单交付侧字段(需人工补录,蒲公英爬不到)');
+  lines.push('');
+  lines.push(`\`${MANUAL_ONLY_FIELDS.join('`、`')}\``);
+  lines.push('');
+  lines.push('## 与蒲公英采集库字段对账');
+  lines.push('');
+  lines.push('| 公司资源库字段 | 蒲公英字段 | 说明 |');
+  lines.push('| --- | --- | --- |');
+  for (const [local, pgy] of Object.entries(PGY_ALIAS)) {
+    lines.push(`| \`${local}\` | \`${pgy}\` | ${CORE_FIELDS[local].zh} |`);
+  }
+  return lines.join('\n');
+}
+
+module.exports = {
+  CORE_FIELDS,
+  REQUIRED_FIELDS,
+  MANUAL_ONLY_FIELDS,
+  PGY_ALIAS,
+  INDEXES,
+  buildDdl,
+  buildQueryMap
+};

+ 250 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/pgy-schema.js

@@ -0,0 +1,250 @@
+// 蒲公英博主字段 schema(结构化自《field_dictionary.md》)
+// 边界:仅沉淀字段定义 + 建表参考 + 选号查询映射,供未来建库/开发查询接口时直接消费,不在此建库。
+// 源码映射:hc_pgy_spider/pgy_field_map.py;采集接口共 9 个详情 + 1 个列表。
+
+// 采集接口(endpointId 供未来查询接口注册使用)
+const SOURCES = {
+  list: { name: '列表 blogger/v2', method: 'POST', path: 'xiaohongshu-pgy/api/solar/cooperator/blogger/v2', priority: 3 },
+  profile: { name: '个人简介', method: 'GET', path: 'cooperator/user/blogger/{id}', priority: 2 },
+  notesRate: { name: '数据概括 notesRate?noteType=3', method: 'GET', path: 'kol/dataV3/notesRate', priority: 2 },
+  noteData: { name: '笔记数据 notesRate×3', method: 'GET', path: 'kol/dataV3/notesRate', priority: 1 },
+  notesDetail: { name: '笔记案例 notesDetail×3', method: 'GET', path: 'kol/dataV2/notesDetail', priority: 1 },
+  fansSummary: { name: '粉丝概览 fansSummary', method: 'GET', path: 'kol/dataV3/fansSummary', priority: 2 },
+  fansProfile: { name: '粉丝画像 fans_profile', method: 'GET', path: 'kol/data/{id}/fans_profile', priority: 1 },
+  dataSummary: { name: '数据表现总览 data_summary', method: 'GET', path: 'kol/data_v3/data_summary', priority: 1 },
+  costEffective: { name: '流量性价比 costEffective', method: 'GET', path: 'kol/dataV2/costEffective', priority: 0 }
+};
+
+// 选号查询维度 → 蒲公英字段 + MongoDB 查询示例
+// 这是未来「选号时开发接口查询」的核心映射:rank/platform-quality-rules 的每个维度对应哪些字段、怎么查。
+const DIMENSIONS = {
+  fanRange: {
+    zh: '粉丝范围',
+    fields: ['follower_count'],
+    type: 'number',
+    unit: '人',
+    query: { follower_count: { $gte: 50000, $lte: 500000 } },
+    note: '腰部 KOL 5万-50万;对应 ranker.fanRange 与 platform-quality-rules 分级'
+  },
+  interaction: {
+    zh: '互动率/互动中位数',
+    fields: ['interaction_rate', 'interact_midnum', 'inter_midnum_30d_total'],
+    type: 'number',
+    unit: '% / 次',
+    query: { interaction_rate: { $gte: 3 } },
+    note: '小红书互动率 ≥3% 优秀、≥1% 合格;抖音 ≥3% 优秀、≥1.5% 合格'
+  },
+  price: {
+    zh: '报价(必采)',
+    fields: ['picture_price', 'video_price', 'lower_price'],
+    type: 'number',
+    unit: '元',
+    query: { lower_price: { $lte: 3000 } },
+    note: '⭐ 列表接口只采这 3 个报价字段'
+  },
+  verticality: {
+    zh: '垂直度/类目',
+    fields: ['trade_type', 'content_tags', 'feature_tags', 'note_type_share'],
+    type: 'string/array',
+    query: { trade_type: '美妆护肤', content_tags: { $all: ['成分党'] } },
+    note: '小红书垂直度 ≥70% 合格、≥90% 优秀'
+  },
+  score: {
+    zh: '评分/信用/等级',
+    fields: ['credit_level', 'goodness_level', 'level_name'],
+    type: 'string',
+    query: { credit_level: { $in: ['Lv5', 'Lv6', 'Lv7'] } },
+    note: '蒲公英评分 ≥4.0 合格、≥4.5 优秀、<3.5 暂不考虑'
+  },
+  location: {
+    zh: '所在地/城市',
+    fields: ['location'],
+    type: 'string',
+    query: { location: { $regex: '上海' } },
+    note: '地域定向'
+  },
+  gender: {
+    zh: '性别',
+    fields: ['gender'],
+    type: 'int',
+    query: { gender: 1 },
+    note: '1=女 2=男'
+  },
+  fansProfile: {
+    zh: '粉丝画像(年龄/性别/城市)',
+    fields: ['fans_age_dist', 'fans_gender_dist', 'fans_city_top'],
+    type: 'array',
+    query: { 'fans_age_dist': { $elemMatch: { age: '18-24', rate: { $gte: 40 } } } },
+    note: '粉丝画像匹配度 ≥60% 合格、≥80% 优秀'
+  },
+  completionRate: {
+    zh: '视频完播率',
+    fields: ['video_full_view_rate', 'video_finish_rate'],
+    type: 'number',
+    unit: '%',
+    query: { video_full_view_rate: { $gte: 20 } },
+    note: '抖音完播率 ≥20% 合格、≥40% 优秀、<10% 危险'
+  },
+  likeCollectRatio: {
+    zh: '赞藏比(收藏/点赞)',
+    fields: ['like_median', 'collect_median'],
+    type: 'number',
+    query: { $expr: { $gte: [{ $divide: ['$collect_median', '$like_median'] }, 0.5] } },
+    note: '小红书赞藏比 ≥0.5 合格、≥1.0 优秀'
+  },
+  cpm: {
+    zh: 'CPM/CPE/阅读成本',
+    fields: ['estimate_picture_cpm', 'estimate_video_cpm', 'estimate_picture_engage_cost', 'estimate_video_engage_cost'],
+    type: 'number',
+    unit: '元',
+    query: { estimate_video_cpm: { $lte: 80 } },
+    note: '抖音腰部 CPM 40-80、CPE 5-10'
+  },
+  growth: {
+    zh: '粉丝增长',
+    fields: ['fans_growth_rate', 'fans_30d_growth_rate', 'fans_30d_growth_num'],
+    type: 'number',
+    unit: '%',
+    query: { fans_30d_growth_rate: { $gte: 0 } },
+    note: '负增长 = 账号健康度风险(platform-quality-rules 已接入)'
+  },
+  activity: {
+    zh: '活跃度',
+    fields: ['active_days_in_last7', 'is_active', 'last_active_time'],
+    type: 'mixed',
+    query: { is_active: true, active_days_in_last7: { $gte: 3 } },
+    note: '近30天无更新 = 数据真实性风险'
+  },
+  verified: {
+    zh: '认证/合作权限',
+    fields: ['verified', 'is_pgy_creator', 'has_buyer_auth', 'cooperate_state'],
+    type: 'bool',
+    query: { is_pgy_creator: true, has_buyer_auth: true },
+    note: '蒲公英创作者 + 品牌合作权限是下单前提'
+  }
+};
+
+// 核心字段表(字段名 → 中文/类型/来源接口/选号维度)
+const CORE_FIELDS = {
+  // 顶级必采
+  blogger_id: { zh: '博主ID', type: 'string', sources: ['list', 'profile'], dimension: 'id', primary: true },
+  nickname: { zh: '昵称', type: 'string', sources: ['list', 'profile'], dimension: 'id' },
+  follower_count: { zh: '粉丝数', type: 'int', sources: ['list', 'profile'], dimension: 'fanRange' },
+  like_count: { zh: '获赞数', type: 'int', sources: ['list', 'profile'], dimension: 'interaction' },
+  post_count: { zh: '笔记数', type: 'int', sources: ['list'], dimension: 'activity' },
+  co_note_count: { zh: '合作笔记数', type: 'int', sources: ['list'], dimension: 'activity' },
+  verified: { zh: '是否认证', type: 'bool', sources: ['list', 'profile'], dimension: 'verified' },
+  tags: { zh: '个人标签', type: 'array', sources: ['list', 'profile'], dimension: 'verticality' },
+  content_tags: { zh: '内容标签', type: 'array', sources: ['list'], dimension: 'verticality' },
+  feature_tags: { zh: '特色标签', type: 'array', sources: ['list'], dimension: 'verticality' },
+  trade_type: { zh: '行业', type: 'string', sources: ['list'], dimension: 'verticality' },
+  gender: { zh: '性别', type: 'int', sources: ['list', 'profile'], dimension: 'gender' },
+  location: { zh: '所在地', type: 'string', sources: ['list', 'profile'], dimension: 'location' },
+  // 报价(必采)
+  picture_price: { zh: '图文报价', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
+  video_price: { zh: '视频报价', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
+  lower_price: { zh: '报价下限', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
+  // 互动/数据
+  interaction_rate: { zh: '互动率', type: 'number', sources: ['notesRate'], dimension: 'interaction', priority: 2 },
+  interact_midnum: { zh: '互动中位数', type: 'number', sources: ['list'], dimension: 'interaction' },
+  read_midnum: { zh: '阅读中位数', type: 'number', sources: ['list'], dimension: 'cpm' },
+  like_median: { zh: '中位点赞量', type: 'number', sources: ['notesRate'], dimension: 'likeCollectRatio' },
+  collect_median: { zh: '中位收藏量', type: 'number', sources: ['notesRate'], dimension: 'likeCollectRatio' },
+  video_full_view_rate: { zh: '视频完播率', type: 'number', sources: ['notesRate'], dimension: 'completionRate' },
+  hundred_like_rate: { zh: '百赞笔记比例', type: 'number', sources: ['notesRate'], dimension: 'interaction' },
+  kilo_like_rate: { zh: '千赞笔记比例', type: 'number', sources: ['notesRate'], dimension: 'interaction' },
+  // 粉丝增长/画像
+  fans_growth_rate: { zh: '30天增粉率', type: 'number', sources: ['fansSummary'], dimension: 'growth' },
+  fans_30d_growth_rate: { zh: '30天粉丝增长率', type: 'number', sources: ['list', 'dataSummary'], dimension: 'growth' },
+  fans_active_l28_rate: { zh: '28天活跃粉丝占比', type: 'number', sources: ['list'], dimension: 'activity' },
+  fans_engage_30d_rate: { zh: '30天互动率', type: 'number', sources: ['list'], dimension: 'interaction' },
+  fans_age_dist: { zh: '粉丝年龄分布', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
+  fans_gender_dist: { zh: '粉丝性别分布', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
+  fans_city_top: { zh: '粉丝城市TOP', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
+  // 成本/CPM
+  estimate_picture_cpm: { zh: '估算图文CPM', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
+  estimate_video_cpm: { zh: '估算视频CPM', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
+  estimate_picture_engage_cost: { zh: '估算图文互动成本', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
+  estimate_video_engage_cost: { zh: '估算视频互动成本', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
+  // 信用/合作
+  credit_level: { zh: '信用等级', type: 'string', sources: ['profile'], dimension: 'score' },
+  goodness_level: { zh: '诚信等级', type: 'string', sources: ['profile'], dimension: 'score' },
+  level_name: { zh: '等级名称', type: 'string', sources: ['profile'], dimension: 'score' },
+  is_pgy_creator: { zh: '蒲公英创作者', type: 'bool', sources: ['profile'], dimension: 'verified' },
+  has_buyer_auth: { zh: '品牌合作权限', type: 'bool', sources: ['list'], dimension: 'verified' },
+  cooperate_state: { zh: '合作状态', type: 'string', sources: ['list'], dimension: 'verified' },
+  active_days_in_last7: { zh: '近7天活跃天数', type: 'int', sources: ['dataSummary'], dimension: 'activity' },
+  is_active: { zh: '是否活跃', type: 'bool', sources: ['dataSummary'], dimension: 'activity' },
+  last_active_time: { zh: '最后活跃时间', type: 'string', sources: ['profile'], dimension: 'activity' },
+  response_rate: { zh: '回复率', type: 'number', sources: ['dataSummary'], dimension: 'activity' }
+};
+
+// 建议索引(选号查询热路径)
+const INDEXES = [
+  { name: 'uk_blogger_id', spec: { blogger_id: 1 }, options: { unique: true } },
+  { name: 'idx_fans_desc', spec: { follower_count: -1 }, options: {} },
+  { name: 'idx_trade_fans', spec: { trade_type: 1, follower_count: -1 }, options: {} },
+  { name: 'idx_interaction_desc', spec: { interaction_rate: -1 }, options: {} },
+  { name: 'idx_price_asc', spec: { lower_price: 1 }, options: {} },
+  { name: 'idx_tags', spec: { content_tags: 1, feature_tags: 1 }, options: {} },
+  { name: 'idx_location', spec: { location: 1 }, options: {} },
+  { name: 'idx_active', spec: { is_active: 1, active_days_in_last7: -1 }, options: {} },
+  { name: 'idx_cpm_asc', spec: { estimate_video_cpm: 1, estimate_picture_cpm: 1 }, options: {} }
+];
+
+// 生成 MongoDB 建表 + 索引 DDL 文本(未来建库直接抄)
+function buildDdl(collectionName = 'pgy_blogger') {
+  const lines = [];
+  lines.push('// 蒲公英博主 collection(MongoDB)—— 由 pgy-schema 生成,未来建库直接执行');
+  lines.push(`db.createCollection('${collectionName}');`);
+  lines.push('');
+  for (const index of INDEXES) {
+    lines.push(`db.${collectionName}.createIndex(${JSON.stringify(index.spec)}, ${JSON.stringify({ name: index.name, ...index.options })});`);
+  }
+  lines.push('');
+  lines.push(`// 必采报价字段(列表接口)示例文档:`);
+  lines.push('// { blogger_id, nickname, follower_count, picture_price, video_price, lower_price, trade_type, content_tags }');
+  return lines.join('\n');
+}
+
+// 生成选号查询映射 markdown(未来接口开发参考)
+function buildQueryMap() {
+  const lines = [];
+  lines.push('# 蒲公英选号查询映射');
+  lines.push('');
+  lines.push('> 由 pgy-schema 生成。选号 SOP 的每个筛选维度 → 蒲公英字段 → MongoDB 查询。');
+  lines.push('');
+  lines.push('| 选号维度 | 蒲公英字段 | 查询示例 |');
+  lines.push('| --- | --- | --- |');
+  for (const [key, dim] of Object.entries(DIMENSIONS)) {
+    lines.push(`| ${dim.zh} | ${dim.fields.join('、')} | \`${JSON.stringify(dim.query)}\` |`);
+  }
+  lines.push('');
+  lines.push('## 采集接口优先级');
+  lines.push('');
+  lines.push('| 优先级 | 接口 | 核心字段 |');
+  lines.push('| --- | --- | --- |');
+  const priorityMap = { 3: '⭐⭐⭐ 必采', 2: '⭐⭐ 高优先', 1: '⭐ 中优先', 0: '低优先' };
+  for (const [key, source] of Object.entries(SOURCES)) {
+    lines.push(`| ${priorityMap[source.priority] || '-'} | ${source.name}(\`${source.method} ${source.path}\`) | ${fieldsBySource(key).join('、') || '-'} |`);
+  }
+  lines.push('');
+  lines.push('> 接口总数:9 个详情 + 1 个列表 = 10 个接口。');
+  return lines.join('\n');
+}
+
+function fieldsBySource(sourceKey) {
+  return Object.entries(CORE_FIELDS)
+    .filter(([, field]) => field.sources.includes(sourceKey))
+    .map(([name]) => name)
+    .slice(0, 12);
+}
+
+module.exports = {
+  SOURCES,
+  DIMENSIONS,
+  CORE_FIELDS,
+  INDEXES,
+  buildDdl,
+  buildQueryMap
+};

+ 153 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/platform-quality-rules.js

@@ -0,0 +1,153 @@
+// 分平台达人筛选阈值(结构化自《01-资源筛选标准(更新版).docx》)
+// 用途:
+// 1. 作为知识库沉淀:各平台 KOL/KOC 分级、量化指标合格线/优秀线/危险线。
+// 2. 接入 ranker:对 live-provider 已采集的字段做增量判定;未采集的指标标「待补证」而非误扣分。
+
+// 达人层级(按粉丝量,单位:人)
+const TIERS = {
+  douyin: [
+    { name: '超头部KOL', minFans: 10000000, label: '超头部KOL(1000万+)' },
+    { name: '头部KOL', minFans: 5000000, maxFans: 10000000, label: '头部KOL(500万-1000万)' },
+    { name: '腰部KOL', minFans: 1000000, maxFans: 5000000, label: '腰部KOL(100万-500万)' },
+    { name: '尾部KOC', minFans: 100000, maxFans: 1000000, label: '尾部KOC(10万-100万)' },
+    { name: '素人KOC', minFans: 10000, maxFans: 100000, label: '素人KOC(1万-10万)' },
+    { name: '素人以下', maxFans: 10000, label: '素人以下(<1万)' }
+  ],
+  xiaohongshu: [
+    { name: '头部KOL', minFans: 500000, label: '头部KOL(50万+)' },
+    { name: '腰部KOL', minFans: 50000, maxFans: 500000, label: '腰部KOL(5万-50万)' },
+    { name: '尾部KOC', minFans: 5000, maxFans: 50000, label: '尾部KOC(5000-5万)' },
+    { name: '素人', maxFans: 5000, label: '素人(<5000)' }
+  ]
+};
+
+// 量化指标阈值。metric 结构:
+//   pass / excellent:达标线 / 优秀线
+//   risk:危险线(低于该值视为危险,除非 direction='max' 表示高于该值危险)
+//   unit:显示单位
+//   direction:'min'(默认,越大越好)| 'max'(越小越好,如商业占比)
+//   fieldKeys:creator 上可能的字段名,用于读值
+const METRICS = {
+  xiaohongshu: {
+    fansCount: { label: '粉丝量', pass: 5000, excellent: 50000, unit: '', direction: 'min', fieldKeys: ['fansCount', 'followerCount', 'followers'] },
+    interactionRate: { label: '近10篇平均互动量占粉丝比', pass: 1, excellent: 3, risk: 0.5, unit: '%', direction: 'min', fieldKeys: ['interactionRate', 'interactionRatePercent', 'interactionRate30d', 'engageRate'] },
+    likeCollectRatio: { label: '赞藏比(收藏/点赞)', pass: 0.5, excellent: 1.0, unit: '', direction: 'min', fieldKeys: ['likeCollectRatio', 'collectLikeRatio', 'collectPerLike'] },
+    platformScore: { label: '蒲公英评分', pass: 4.0, excellent: 4.5, risk: 3.5, unit: '分', direction: 'min', fieldKeys: ['platformScore', 'pgyScore', 'creditLevel', 'score'] },
+    contentVerticality: { label: '内容垂直度', pass: 70, excellent: 90, unit: '%', direction: 'min', fieldKeys: ['contentVerticality', 'verticality', 'contentVerticalRate'] },
+    commercialRatio: { label: '商业笔记占比', pass: 30, excellent: 20, risk: 50, unit: '%', direction: 'max', fieldKeys: ['commercialRatio', 'commercialNoteRatio', 'adRatio'] }
+  },
+  douyin: {
+    fansCount: { label: '粉丝量', pass: 10000, excellent: 1000000, unit: '', direction: 'min', fieldKeys: ['fansCount', 'followerCount', 'followers'] },
+    interactionRate: { label: '近10条平均互动率', pass: 1.5, excellent: 3, risk: 1, unit: '%', direction: 'min', fieldKeys: ['interactionRate', 'interactionRatePercent', 'interactionRate30d', 'engageRate'] },
+    completionRate: { label: '完播率', pass: 20, excellent: 40, risk: 10, unit: '%', direction: 'min', fieldKeys: ['completionRate', 'fullViewRate', 'videoFinishRate', 'videoFullViewRate'] },
+    platformScore: { label: '星图评分', pass: 4.0, excellent: 4.5, risk: 3.5, unit: '分', direction: 'min', fieldKeys: ['platformScore', 'xingtuScore', 'creditLevel', 'score'] },
+    fansMatchRate: { label: '粉丝画像匹配度', pass: 60, excellent: 80, risk: 40, unit: '%', direction: 'min', fieldKeys: ['fansMatchRate', 'taOverlapRate', 'audienceMatchRate', 'matchRate'] },
+    commercialRatio: { label: '商业内容占比', pass: 30, excellent: 20, risk: 50, unit: '%', direction: 'max', fieldKeys: ['commercialRatio', 'commercialNoteRatio', 'adRatio'] }
+  }
+};
+
+function getPlatformRules(platform) {
+  return {
+    tiers: TIERS[platform] || [],
+    metrics: METRICS[platform] || {}
+  };
+}
+
+function classifyTier(platform, fansCount) {
+  const fans = Number(fansCount || 0);
+  const tiers = (TIERS[platform] || []).slice().sort((a, b) => (b.minFans || 0) - (a.minFans || 0));
+  for (const tier of tiers) {
+    const minOk = tier.minFans === undefined || fans >= tier.minFans;
+    const maxOk = tier.maxFans === undefined || fans < tier.maxFans;
+    if (minOk && maxOk) return tier;
+  }
+  return { name: '未知', label: '未知量级' };
+}
+
+// 对单个 creator 做分平台数据质量判定。返回结构化结果,供 ranker 评分与 report 展示。
+function evaluateCreatorDataQuality(creator = {}) {
+  const platform = creator.platform;
+  const rules = getPlatformRules(platform);
+  const fansCount = Number(creator.fansCount || creator.followerCount || creator.followers || 0);
+  const tier = classifyTier(platform, fansCount);
+
+  const checks = [];
+  const missingMetrics = [];
+  for (const [metricKey, metric] of Object.entries(rules.metrics)) {
+    const value = readMetricValue(creator, metric.fieldKeys);
+    if (value === null || value === undefined || value === '') {
+      if (metricKey !== 'fansCount') missingMetrics.push(metric.label);
+      continue;
+    }
+    checks.push(evaluateMetric(metricKey, metric, value));
+  }
+
+  // 粉丝增长率:负增长或剧烈波动视为风险(抖音规则「锯齿状剧烈波动」疑似买粉)
+  const growth = Number(creator.fanGrowthRate30d ?? creator.fanGrowthRate ?? 0);
+  if (growth < 0) {
+    checks.push({ metric: 'fanGrowthRate30d', label: '30天粉丝增长率', value: growth, verdict: 'risk', note: '粉丝负增长,需复核账号健康度' });
+  }
+
+  if (creator.lowActivity === true) {
+    checks.push({ metric: 'lowActivity', label: '账号活跃度', value: true, verdict: 'risk', note: '平台标记为低活跃账号' });
+  }
+
+  const riskCount = checks.filter(item => item.verdict === 'risk').length;
+  const excellentCount = checks.filter(item => item.verdict === 'excellent').length;
+  const passCount = checks.filter(item => item.verdict === 'pass').length;
+
+  // 分数:45 基线 + 已达标指标加分 - 危险信号扣分(缺失指标不扣分,避免误杀)
+  let score = 45;
+  score += excellentCount * 12;
+  score += passCount * 6;
+  score -= riskCount * 15;
+  score = clamp(score);
+
+  return {
+    platform,
+    tier: tier.name,
+    tierLabel: tier.label,
+    score,
+    checks,
+    missingMetrics,
+    riskCount,
+    excellentCount,
+    passCount
+  };
+}
+
+function evaluateMetric(metricKey, metric, rawValue) {
+  const value = Number(rawValue);
+  const base = { metric: metricKey, label: metric.label, value: rawValue };
+  if (!Number.isFinite(value)) return { ...base, verdict: 'unknown' };
+  const direction = metric.direction || 'min';
+  if (direction === 'max') {
+    if (metric.risk !== undefined && value > metric.risk) return { ...base, verdict: 'risk', note: `高于危险线 ${metric.risk}${metric.unit || ''}` };
+    if (metric.excellent !== undefined && value <= metric.excellent) return { ...base, verdict: 'excellent', note: `达到优秀 ${metric.excellent}${metric.unit || ''}` };
+    if (metric.pass !== undefined && value > metric.pass) return { ...base, verdict: 'pass', note: `高于合格线 ${metric.pass}${metric.unit || ''}` };
+    return { ...base, verdict: 'below-pass', note: `未达到合格线 ${metric.pass}${metric.unit || ''}` };
+  }
+  if (metric.risk !== undefined && value < metric.risk) return { ...base, verdict: 'risk', note: `低于危险线 ${metric.risk}${metric.unit || ''}` };
+  if (metric.excellent !== undefined && value >= metric.excellent) return { ...base, verdict: 'excellent', note: `达到优秀 ${metric.excellent}${metric.unit || ''}` };
+  if (metric.pass !== undefined && value >= metric.pass) return { ...base, verdict: 'pass', note: `达到合格线 ${metric.pass}${metric.unit || ''}` };
+  return { ...base, verdict: 'below-pass', note: `低于合格线 ${metric.pass}${metric.unit || ''}` };
+}
+
+function readMetricValue(creator, fieldKeys) {
+  for (const key of fieldKeys) {
+    if (creator[key] !== undefined && creator[key] !== null && creator[key] !== '') return creator[key];
+  }
+  return null;
+}
+
+function clamp(value) {
+  return Math.max(0, Math.min(100, Number(value || 0)));
+}
+
+module.exports = {
+  TIERS,
+  METRICS,
+  getPlatformRules,
+  classifyTier,
+  evaluateCreatorDataQuality
+};

+ 3 - 3
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/progress-reporter.js

@@ -47,7 +47,7 @@ function createReporter(input = {}, context = {}) {
 
 
 class ProgressReporter {
 class ProgressReporter {
   constructor({ sessionToken, taskId, active }) {
   constructor({ sessionToken, taskId, active }) {
-    this.sessionToken = sessionToken;
+    this.sessionToken = String(sessionToken || '');
     this.taskId = taskId;
     this.taskId = taskId;
     this.active = active;
     this.active = active;
     this.title = '';
     this.title = '';
@@ -125,7 +125,7 @@ class ProgressReporter {
       await callFunction({
       await callFunction({
         id: PROGRESS_FN_ID,
         id: PROGRESS_FN_ID,
         action: 'report',
         action: 'report',
-        sessionToken: this.sessionToken,
+        sessionToken: String(this.sessionToken || ''),
         taskId: this.taskId,
         taskId: this.taskId,
         ...payload,
         ...payload,
       });
       });
@@ -182,7 +182,7 @@ class ProgressReporter {
     const tokenResp = await callFunction({
     const tokenResp = await callFunction({
       id: UPLOAD_FN_ID,
       id: UPLOAD_FN_ID,
       action: 'createUploadToken',
       action: 'createUploadToken',
-      sessionToken: this.sessionToken,
+      sessionToken: String(this.sessionToken || ''),
       filename,
       filename,
       mimeType,
       mimeType,
       kind: 'export',
       kind: 'export',

+ 9 - 4
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/ranker.js

@@ -1,5 +1,6 @@
 const { attachHomepageEvidenceToCreator } = require('./homepage-evidence');
 const { attachHomepageEvidenceToCreator } = require('./homepage-evidence');
 const { evaluateImplicitRuleFit } = require('./experience-rules');
 const { evaluateImplicitRuleFit } = require('./experience-rules');
+const { evaluateCreatorDataQuality } = require('./platform-quality-rules');
 
 
 function rankCreators(creators, criteria) {
 function rankCreators(creators, criteria) {
   const seen = new Set();
   const seen = new Set();
@@ -26,7 +27,8 @@ function scoreCreator(creator, criteria) {
   const hardExclusionHits = [...new Set([...riskHits, ...blockedCreatorHits])];
   const hardExclusionHits = [...new Set([...riskHits, ...blockedCreatorHits])];
   const briefFit = scoreBriefFit(creator, criteria, fanScore, priceScore);
   const briefFit = scoreBriefFit(creator, criteria, fanScore, priceScore);
   const referenceFit = scoreReferenceFit(creator, criteria);
   const referenceFit = scoreReferenceFit(creator, criteria);
-  const dataQualityScore = scoreDataQuality(creator);
+  const dataQuality = scoreDataQuality(creator);
+  const dataQualityScore = dataQuality.score;
   const homepageEvidence = creator.homepageEvidence || {};
   const homepageEvidence = creator.homepageEvidence || {};
   const recentContentFitScore = creator.recentContentFitScore || homepageEvidence.recentContentFitScore || 60;
   const recentContentFitScore = creator.recentContentFitScore || homepageEvidence.recentContentFitScore || 60;
   const visualQualityScore = creator.visualQualityScore || homepageEvidence.visualQualityScore || 0;
   const visualQualityScore = creator.visualQualityScore || homepageEvidence.visualQualityScore || 0;
@@ -85,6 +87,8 @@ function scoreCreator(creator, criteria) {
     briefFitScore: briefFit.score,
     briefFitScore: briefFit.score,
     referenceStyleFitScore: referenceFit.score,
     referenceStyleFitScore: referenceFit.score,
     dataQualityScore,
     dataQualityScore,
+    dataQuality: dataQuality.result,
+    tierLabel: dataQuality.result.tierLabel,
     recentContentFitScore,
     recentContentFitScore,
     visualQualityScore,
     visualQualityScore,
     toneConsistencyScore,
     toneConsistencyScore,
@@ -188,9 +192,10 @@ function fallbackReferencePoints(creator, criteria) {
 }
 }
 
 
 function scoreDataQuality(creator) {
 function scoreDataQuality(creator) {
-  let score = 45;
+  const result = evaluateCreatorDataQuality(creator);
+  let score = result.score;
+  // 字段完整度加分(衡量数据完整度,与平台质量阈值互补;粉丝量已进入 platformResult,不重复加)
   if (creator.profileUrl) score += 10;
   if (creator.profileUrl) score += 10;
-  if (creator.fansCount) score += 10;
   if (creator.minPrice || creator.imagePrice || creator.videoPrice) score += 10;
   if (creator.minPrice || creator.imagePrice || creator.videoPrice) score += 10;
   if ((creator.contentTags || []).length) score += 10;
   if ((creator.contentTags || []).length) score += 10;
   if ((creator.personaTags || []).length) score += 10;
   if ((creator.personaTags || []).length) score += 10;
@@ -200,7 +205,7 @@ function scoreDataQuality(creator) {
   const lastPostDaysAgo = Number(creator.lastPostDaysAgo ?? creator.daysSinceLastPost ?? 0);
   const lastPostDaysAgo = Number(creator.lastPostDaysAgo ?? creator.daysSinceLastPost ?? 0);
   if (lastPostDaysAgo > 30) score -= 20;
   if (lastPostDaysAgo > 30) score -= 20;
   if (Number(creator.repeatedCommenterCount || creator.repeatedCommenterHits || 0) > 0) score -= 18;
   if (Number(creator.repeatedCommenterCount || creator.repeatedCommenterHits || 0) > 0) score -= 18;
-  return clamp(score);
+  return { score: clamp(score), result };
 }
 }
 
 
 function buildManualReviewFields(creator, criteria, riskHits) {
 function buildManualReviewFields(creator, criteria, riskHits) {

+ 1 - 1
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/reference-enrichment-provider.js

@@ -3,7 +3,7 @@ const { buildSocialRechargeInfo } = require('../../core/payment-links');
 const { normalizeBaseline } = require('./reference-baselines');
 const { normalizeBaseline } = require('./reference-baselines');
 
 
 async function enrichReferenceBaselines(criteria, input = {}) {
 async function enrichReferenceBaselines(criteria, input = {}) {
-  const baseUrl = input.referenceEnrichmentBaseUrl || input.socialAnalysisBaseUrl || input.tikhubBaseUrl || process.env.SOCIAL_ANALYSIS_BASE_URL || process.env.TIKHUB_BASE_URL || '';
+  const baseUrl = input.referenceEnrichmentBaseUrl || input.socialAnalysisBaseUrl || process.env.SOCIAL_ANALYSIS_BASE_URL || process.env.TIHAO_REFERENCE_ENRICHMENT_BASE_URL || '';
   const vocSocialBaseUrl = input.vocSocialBaseUrl || process.env.VOC_SOCIAL_BASE_URL || 'https://server.fmode.cn/api/voc-social';
   const vocSocialBaseUrl = input.vocSocialBaseUrl || process.env.VOC_SOCIAL_BASE_URL || 'https://server.fmode.cn/api/voc-social';
   const token = readSocialAnalysisToken(input);
   const token = readSocialAnalysisToken(input);
   const useVocSocial = Boolean(input.enableVocSocialReferenceEnrichment || input.useVocSocialReferenceEnrichment || process.env.TIHAO_USE_VOC_SOCIAL_REFERENCE === 'true' || (!baseUrl && token && (criteria.referenceLinks || []).length));
   const useVocSocial = Boolean(input.enableVocSocialReferenceEnrichment || input.useVocSocialReferenceEnrichment || process.env.TIHAO_USE_VOC_SOCIAL_REFERENCE === 'true' || (!baseUrl && token && (criteria.referenceLinks || []).length));

+ 112 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/report.js

@@ -12,6 +12,7 @@ const SOFTWARE_TABLE_HEADER = [
   '平台',
   '平台',
   '账号名称',
   '账号名称',
   '账号ID',
   '账号ID',
+  '平台短ID',
   '主页链接',
   '主页链接',
   '粉丝数',
   '粉丝数',
   '图文报价',
   '图文报价',
@@ -98,6 +99,11 @@ function buildSourcingReport({ criteria, candidates, collectionMode, outputDir,
     '',
     '',
     ...formatRequirementLayers(criteria),
     ...formatRequirementLayers(criteria),
     '',
     '',
+    ...formatGoalStrategy(criteria),
+    ...formatBriefRulePatterns(criteria),
+    ...formatFewshotReference(criteria),
+    ...formatCompliance(criteria),
+    '',
     '## 参考账号可参考性判断',
     '## 参考账号可参考性判断',
     '',
     '',
     ...formatReferenceUsability(criteria),
     ...formatReferenceUsability(criteria),
@@ -234,6 +240,7 @@ function softwareRows(criteria, rows) {
       平台: labelPlatform(item.platform),
       平台: labelPlatform(item.platform),
       账号名称: item.displayName || item.name || '',
       账号名称: item.displayName || item.name || '',
       账号ID: item.platformUserId || item.userId || '',
       账号ID: item.platformUserId || item.userId || '',
+      平台短ID: item.platformShortId || item.redId || item.uniqueId || '',
       主页链接: item.profileUrl || item.homepageUrl || '',
       主页链接: item.profileUrl || item.homepageUrl || '',
       粉丝数: formatNumber(item.fansCount),
       粉丝数: formatNumber(item.fansCount),
       图文报价: formatNumber(item.imagePrice || item.picturePrice),
       图文报价: formatNumber(item.imagePrice || item.picturePrice),
@@ -407,6 +414,111 @@ function formatRequirementLayers(criteria) {
   return lines;
   return lines;
 }
 }
 
 
+function formatGoalStrategy(criteria) {
+  const goal = criteria.goalStrategy || {};
+  if (!goal.goalLabel) return [];
+  const lines = [
+    '## 投放目标与提号策略',
+    '',
+    `- 投放目标:${goal.goalLabel}`,
+    `- 匹配账号类型:${(goal.accountTypes || []).join('、')}`,
+    `- 量级提示:${goal.tierHint || ''}`,
+    `- 策略说明:${goal.note || ''}`,
+    `- 提号数量:${goal.suggestionNote || ''}`,
+    ''
+  ];
+  const rules = goal.suggestionRules || [];
+  if (rules.length) {
+    lines.push('| 提号规则 | 说明 |');
+    lines.push('| --- | --- |');
+    for (const rule of rules) lines.push(`| ${escapeCell(rule.title || '')} | ${escapeCell(rule.description || '')} |`);
+    lines.push('');
+  }
+  return lines;
+}
+
+function formatBriefRulePatterns(criteria) {
+  const patterns = criteria.briefRulePatterns || [];
+  if (!patterns.length) return [];
+  const lines = [
+    '## 真实案例规则模式命中',
+    '',
+    '| 模式 | 说明 | 命中原文 |',
+    '| --- | --- | --- |'
+  ];
+  for (const item of patterns.slice(0, 8)) {
+    lines.push(`| ${escapeCell(item.label)} | ${escapeCell(item.note)} | ${escapeCell(item.raw)} |`);
+  }
+  lines.push('');
+  lines.push('- 以上模式来自 13 个真实 brief 案例提炼,命中即按历史经验约束本轮选号。');
+  lines.push('');
+  return lines;
+}
+
+function formatFewshotReference(criteria) {
+  const reference = criteria.fewshotReference || {};
+  if (!reference.status || reference.status === 'no_case_library') {
+    return ['## 历史相似案例参照', '', `- ${reference.note || '未加载历史案例库。'}`, ''];
+  }
+  const cases = reference.cases || [];
+  const lines = [
+    '## 历史相似案例参照',
+    '',
+    `- 状态:已加载历史案例库(共 ${reference.totalCases || 0} 条)。`,
+    `- 说明:${reference.note || '按品类/平台/投放目标匹配历史相似案例。'}`,
+    ''
+  ];
+  if (cases.length) {
+    lines.push('| 案例 | 品类 | 平台 | 结果 | 选中账号特征 | 关键原因 |');
+    lines.push('| --- | --- | --- | --- | --- | --- |');
+    for (const item of cases) {
+      const outcome = item.outcome === 'positive' ? '✅正样本' : item.outcome === 'negative' ? '❌负样本' : '中性';
+      lines.push(`| ${escapeCell(item.id || '')} | ${escapeCell(item.category || '')} | ${escapeCell(item.platformLabel || item.platform || '')} | ${outcome} | ${escapeCell(item.selectedAccountProfile || '')} | ${escapeCell(item.keyReason || '')} |`);
+    }
+    lines.push('');
+  }
+  lines.push('- 历史案例仅作参照,不静默替代评分与人工判断;正样本作为类型/调性锚点,负样本作为风险提示。');
+  lines.push('');
+  return lines;
+}
+
+function formatCompliance(criteria) {
+  const compliance = criteria.compliance || {};
+  if (!compliance.bannedWordReplacements && !compliance.generalRules) return [];
+  const lines = [
+    '## 内容合规白名单',
+    '',
+    `- 高危词检测:${compliance.note || '未检测'}`,
+    ''
+  ];
+  if ((compliance.bannedHits || []).length) {
+    lines.push('| 检出高危词 | 建议替换 | 备注 |');
+    lines.push('| --- | --- | --- |');
+    for (const hit of compliance.bannedHits) lines.push(`| ${escapeCell(hit.banned)} | ${escapeCell(hit.safe)} | ${escapeCell(hit.note)} |`);
+    lines.push('');
+  }
+  const rules = compliance.bannedWordReplacements || [];
+  if (rules.length) {
+    lines.push('| 高危词 | 推荐替代词 | 备注 |');
+    lines.push('| --- | --- | --- |');
+    for (const item of rules) lines.push(`| ${escapeCell(item.banned)} | ${escapeCell(item.safe)} | ${escapeCell(item.note)} |`);
+    lines.push('');
+  }
+  const platformRules = compliance.platformRules || [];
+  if (platformRules.length) {
+    lines.push('平台特规:');
+    for (const item of platformRules) {
+      lines.push(`- ${labelPlatform(item.platform)}:禁 ${(item.banned || []).join('、')};建议 ${(item.safe || []).join('、')}`);
+    }
+    lines.push('');
+  }
+  if ((compliance.safeWords || []).length) {
+    lines.push(`- 安全词库:${compliance.safeWords.join('、')}`);
+    lines.push('');
+  }
+  return lines;
+}
+
 function formatReferenceUsability(criteria) {
 function formatReferenceUsability(criteria) {
   const usability = criteria.referenceUsability || {};
   const usability = criteria.referenceUsability || {};
   const lines = [
   const lines = [

+ 2 - 0
claude-code-tihao-sourcing/mcp/src/features/tihao-sourcing/sourcing-workflow.js

@@ -9,6 +9,7 @@ const { enrichReferenceBaselines } = require('./reference-enrichment-provider');
 const { attachReferenceStyleFingerprints } = require('./reference-style-fingerprint');
 const { attachReferenceStyleFingerprints } = require('./reference-style-fingerprint');
 const { attachEvidenceCards, loadMultimodalEvidence } = require('./multimodal-evidence');
 const { attachEvidenceCards, loadMultimodalEvidence } = require('./multimodal-evidence');
 const { enrichCriteriaWithExperience } = require('./experience-rules');
 const { enrichCriteriaWithExperience } = require('./experience-rules');
+const { buildFewshotReference } = require('./few-shot-cases');
 const { enrichHomepageEvidence } = require('./homepage-evidence-provider');
 const { enrichHomepageEvidence } = require('./homepage-evidence-provider');
 const { rankCreators } = require('./ranker');
 const { rankCreators } = require('./ranker');
 const { buildSourcingReport } = require('./report');
 const { buildSourcingReport } = require('./report');
@@ -47,6 +48,7 @@ async function runTihaoSourcingInner(input, collectionMode, outputDir, reporter)
     referenceResources: enrichment.referenceResources || []
     referenceResources: enrichment.referenceResources || []
   });
   });
   criteria = enrichCriteriaWithExperience(criteria);
   criteria = enrichCriteriaWithExperience(criteria);
+  criteria = { ...criteria, fewshotReference: buildFewshotReference(criteria, effectiveInput) };
 
 
   let creators = SAMPLE_CREATORS;
   let creators = SAMPLE_CREATORS;
   let warnings = [...(enrichment.warnings || [])];
   let warnings = [...(enrichment.warnings || [])];

+ 172 - 0
claude-code-tihao-sourcing/mcp/src/providers/ecommerce-gateway.js

@@ -0,0 +1,172 @@
+const ECOMMERCE_GATEWAY_ROOT = 'https://server.fmode.cn/api/voc-e-commerce';
+
+function resolveEcommerceRoot(baseUrl) {
+  return String(
+    baseUrl ||
+      process.env.TIHAO_ECOMMERCE_BASE_URL ||
+      process.env.VOC_ECOMMERCE_BASE_URL ||
+      ECOMMERCE_GATEWAY_ROOT
+  ).replace(/\/$/, '');
+}
+
+function buildGatewayUrl(root, proxyPath, query = {}) {
+  const url = new URL(`${root.replace(/\/$/, '')}/${String(proxyPath || '').replace(/^\//, '')}`);
+  Object.entries(query || {}).forEach(([key, value]) => {
+    if (value !== undefined && value !== null && value !== '') {
+      url.searchParams.set(key, String(value));
+    }
+  });
+  return url.toString();
+}
+
+function unwrapData(json) {
+  if (!json || typeof json !== 'object') return {};
+  return json.data?.data || json.data || json;
+}
+
+function pickBusinessStatus(json) {
+  const topCode = Number(json?.code);
+  if (Number.isFinite(topCode) && topCode !== 0 && topCode !== 200) {
+    return topCode;
+  }
+  const nestedCode = Number(json?.data?.status_code);
+  if (Number.isFinite(nestedCode) && nestedCode !== 0 && nestedCode !== 200) {
+    return nestedCode;
+  }
+  return 0;
+}
+
+function redactSecret(value) {
+  return String(value || '')
+    .replace(/Bearer\s+[^"'\s,}]+/gi, 'Bearer [REDACTED]')
+    .replace(/\bsk-[A-Za-z0-9_-]{6,}\b/g, 'sk-[REDACTED]')
+    .replace(/\br:[A-Za-z0-9]{4,}\b/g, 'r:[REDACTED]')
+    .replace(/("Authorization"\s*:\s*")([^"]+)(")/gi, '$1[REDACTED]$3')
+    .replace(/(sessionToken["':=\s]+)[A-Za-z0-9._~+/=:-]+/gi, '$1[REDACTED]');
+}
+
+function isSuccessCode(json) {
+  const topCode = Number(json?.code);
+  if (Number.isFinite(topCode) && ![0, 200].includes(topCode)) {
+    return false;
+  }
+  const statusCode = Number(json?.data?.status_code);
+  if (Number.isFinite(statusCode) && ![0, 200].includes(statusCode)) {
+    return false;
+  }
+  return true;
+}
+
+function classifyApiError({ response, json, message }) {
+  const httpStatus = response?.status || 0;
+  const businessStatus = pickBusinessStatus(json);
+  const status = businessStatus || httpStatus;
+  const text = String(message || '');
+  if (/company或用户信息不存在|用户信息不存在|用户不存在|账[号户]信息不存在|未登录|登录失效|请输入.*token|sessiontoken/i.test(text)) {
+    return 'auth';
+  }
+  if (status === 401 || /invalid token|无效.*token|token.*invalid|未授权|unauthorized/i.test(text)) {
+    return 'auth';
+  }
+  if (status === 402 || /余额不足|额度不足|没开通|未开通|开通.*权限|权限.*额度|insufficient|balance|quota|payment|充值/i.test(text)) {
+    return 'billing';
+  }
+  if (status === 403 && /余额不足|额度不足|没开通|未开通|insufficient|balance|quota|payment/i.test(text)) {
+    return 'billing';
+  }
+  if (/参数|入参|keyword|关键词|invalid request|bad request/i.test(text) || [400, 422].includes(status)) {
+    return 'request';
+  }
+  if (status >= 500) {
+    return 'upstream';
+  }
+  if (status === 403 || /permission|权限|无权限/i.test(text)) {
+    return 'permission';
+  }
+  return 'upstream';
+}
+
+const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+async function requestJson(url, token, { method = 'GET', body, retries = 3 } = {}) {
+  let lastError;
+  for (let attempt = 0; attempt <= retries; attempt++) {
+    let response;
+    let text;
+    try {
+      response = await fetch(url, {
+        method,
+        headers: {
+          Authorization: `Bearer ${token}`,
+          Accept: 'application/json',
+          ...(body ? { 'Content-Type': 'application/json' } : {})
+        },
+        body: body ? JSON.stringify(body) : undefined
+      });
+      text = await response.text();
+    } catch (networkError) {
+      lastError = networkError;
+      if (attempt < retries) {
+        await delay(400 * (attempt + 1));
+        continue;
+      }
+      const error = new Error(redactSecret(networkError && networkError.message ? networkError.message : 'fetch failed'));
+      error.kind = 'upstream';
+      error.httpStatus = 0;
+      throw error;
+    }
+    let json;
+    try {
+      json = JSON.parse(text);
+    } catch {
+      json = undefined;
+    }
+    if (!response.ok || (json && !isSuccessCode(json))) {
+      const message = json?.message || json?.msg || json?.data?.msg || json?.data?.message || json?.mess || text || `HTTP ${response.status}`;
+      const error = new Error(redactSecret(message));
+      error.httpStatus = pickBusinessStatus(json) || response.status || 500;
+      error.kind = classifyApiError({ response, json, message });
+      error.responseBody = redactSecret(text);
+      if (error.httpStatus >= 500 && attempt < retries) {
+        lastError = error;
+        await delay(400 * (attempt + 1));
+        continue;
+      }
+      throw error;
+    }
+    return { json, data: unwrapData(json), rawText: text, httpStatus: response.status };
+  }
+  throw lastError;
+}
+
+// 优先用 newapi 的 sk- token 发请求;若该 token 鉴权失败(kind === 'auth',
+// 例如服务端尚未迁移 / 该号未入仓 / token 未被接受)且提供了回退 token(平台
+// sessionToken),自动用回退 token 重试一次。402(余额不足)/403(无权限)不回退。
+async function callEcommerceGateway({ proxyPath, method = 'GET', query = {}, body, token, fallbackToken, baseUrl, retries = 3 } = {}) {
+  const root = resolveEcommerceRoot(baseUrl);
+  const url = buildGatewayUrl(root, proxyPath, query);
+  const upper = String(method || 'GET').toUpperCase();
+  const requestOptions = {
+    method: upper,
+    body: upper === 'GET' ? undefined : body,
+    retries
+  };
+  try {
+    return await requestJson(url, token, requestOptions);
+  } catch (error) {
+    const canFallback = fallbackToken && fallbackToken !== token && error && error.kind === 'auth';
+    if (!canFallback) throw error;
+    return requestJson(url, fallbackToken, requestOptions);
+  }
+}
+
+module.exports = {
+  ECOMMERCE_GATEWAY_ROOT,
+  resolveEcommerceRoot,
+  buildGatewayUrl,
+  classifyApiError,
+  redactSecret,
+  unwrapData,
+  requestJson,
+  callEcommerceGateway
+};

+ 51 - 0
claude-code-tihao-sourcing/mcp/src/providers/social-gateway.js

@@ -0,0 +1,51 @@
+// 社媒数据中转网关(voc-social)。用于小红书素人/非蒲公英等社媒接口(全量代理,
+// 上游为社媒数据中台)。鉴权与计费、错误分类逻辑复用电商网关的实现,只是根地址不同。
+const {
+  buildGatewayUrl,
+  requestJson,
+  classifyApiError,
+  redactSecret,
+  unwrapData
+} = require('./ecommerce-gateway');
+
+const SOCIAL_GATEWAY_ROOT = 'https://server.fmode.cn/api/voc-social';
+
+function resolveSocialRoot(baseUrl) {
+  return String(
+    baseUrl ||
+      process.env.TIHAO_SOCIAL_BASE_URL ||
+      process.env.VOC_SOCIAL_BASE_URL ||
+      process.env.VOC_SOCIAL_API_BASE_URL ||
+      SOCIAL_GATEWAY_ROOT
+  ).replace(/\/$/, '');
+}
+
+// 与 callEcommerceGateway 完全一致的「优先 newapi sk-、鉴权失败回退 sessionToken」逻辑,
+// 只是走 voc-social 根地址。
+async function callSocialGateway({ proxyPath, method = 'GET', query = {}, body, token, fallbackToken, baseUrl, retries = 3 } = {}) {
+  const root = resolveSocialRoot(baseUrl);
+  const url = buildGatewayUrl(root, proxyPath, query);
+  const upper = String(method || 'GET').toUpperCase();
+  const requestOptions = {
+    method: upper,
+    body: upper === 'GET' ? undefined : body,
+    retries
+  };
+  try {
+    return await requestJson(url, token, requestOptions);
+  } catch (error) {
+    const canFallback = fallbackToken && fallbackToken !== token && error && error.kind === 'auth';
+    if (!canFallback) throw error;
+    return requestJson(url, fallbackToken, requestOptions);
+  }
+}
+
+module.exports = {
+  SOCIAL_GATEWAY_ROOT,
+  resolveSocialRoot,
+  buildGatewayUrl,
+  classifyApiError,
+  redactSecret,
+  unwrapData,
+  callSocialGateway
+};

+ 102 - 3
claude-code-tihao-sourcing/mcp/src/server.js

@@ -5,6 +5,7 @@ const { z } = require('zod');
 const { runTihaoSourcing } = require('./tools/tihao-brief-sourcing-run');
 const { runTihaoSourcing } = require('./tools/tihao-brief-sourcing-run');
 const { updatePreferenceMemory } = require('./tools/tihao-preference-update');
 const { updatePreferenceMemory } = require('./tools/tihao-preference-update');
 const { analyzeFmodeImage } = require('./tools/fmode-image-analysis');
 const { analyzeFmodeImage } = require('./tools/fmode-image-analysis');
+const { searchTihaoApis, getTihaoApiDoc, callTihaoApi } = require('./tools/tihao-api-catalog-run');
 const { readTihaoToken } = require('./core/credentials');
 const { readTihaoToken } = require('./core/credentials');
 const { buildTihaoRechargeInfo, buildMissingTokenMessage } = require('./core/payment-links');
 const { buildTihaoRechargeInfo, buildMissingTokenMessage } = require('./core/payment-links');
 
 
@@ -144,9 +145,6 @@ function createServer() {
         referencePostsLimit: z.number().int().min(1).max(50).optional(),
         referencePostsLimit: z.number().int().min(1).max(50).optional(),
         socialAnalysisBaseUrl: z.string().optional(),
         socialAnalysisBaseUrl: z.string().optional(),
         socialAnalysisToken: z.string().optional(),
         socialAnalysisToken: z.string().optional(),
-        tikhubBaseUrl: z.string().optional(),
-        tikhubToken: z.string().optional(),
-        tikHubToken: z.string().optional(),
         vocSocialBaseUrl: z.string().optional(),
         vocSocialBaseUrl: z.string().optional(),
         vocSocialToken: z.string().optional(),
         vocSocialToken: z.string().optional(),
         enableVocSocialReferenceEnrichment: z.boolean().optional(),
         enableVocSocialReferenceEnrichment: z.boolean().optional(),
@@ -277,6 +275,107 @@ function createServer() {
     async input => asToolResult(updatePreferenceMemory(input))
     async input => asToolResult(updatePreferenceMemory(input))
   );
   );
 
 
+  server.registerTool(
+    'tihao_api_search',
+    {
+      title: 'Search Tihao E-Commerce Data Endpoint Catalog',
+      description: [
+        'List or search the registered voc-e-commerce creator/blogger data endpoints (douyin/xiaohongshu search + detail).',
+        'Use when you need 媒体详情/绩效数据 such as CPM/CPE、商单数、阅读/互动/播放中位数、报价, or to discover which detail endpoints exist for second-wave enrichment.',
+        'First-wave search endpoints return a creator list with ids (douyin star_id / xiaohongshu userId); detail endpoints take that id to return performance metrics.',
+        'Returns endpoint id, title, method, proxyPath, required params and tags so you can freely choose which detail calls to make.'
+      ].join(' '),
+      inputSchema: {
+        query: z.string().optional(),
+        q: z.string().optional(),
+        keyword: z.string().optional(),
+        platform: z.string().optional(),
+        tag: z.string().optional(),
+        limit: z.number().int().min(1).max(100).optional()
+      },
+      outputSchema: {
+        status: z.string(),
+        assistantMessage: z.string(),
+        summary: z.object({}).passthrough().optional(),
+        data: z.object({}).passthrough().optional(),
+        files: z.array(z.string()).optional(),
+        nextActions: z.array(z.string()).optional(),
+        warnings: z.array(z.any()).optional(),
+        errors: z.array(z.any()).optional()
+      }
+    },
+    async input => asToolResult(await searchTihaoApis(input))
+  );
+
+  server.registerTool(
+    'tihao_api_doc',
+    {
+      title: 'Read Tihao E-Commerce Endpoint Parameter Doc',
+      description: [
+        'Read the full parameter schema and a ready-to-use call template for one voc-e-commerce endpoint by id or proxyPath.',
+        'Use after tihao_api_search to learn exactly how to pass params before calling a detail endpoint with tihao_api_call.'
+      ].join(' '),
+      inputSchema: {
+        id: z.string().optional(),
+        endpointId: z.string().optional(),
+        proxyPath: z.string().optional(),
+        rawPath: z.string().optional(),
+        path: z.string().optional()
+      },
+      outputSchema: {
+        status: z.string(),
+        assistantMessage: z.string(),
+        summary: z.object({}).passthrough().optional(),
+        data: z.object({}).passthrough().optional(),
+        files: z.array(z.string()).optional(),
+        nextActions: z.array(z.string()).optional(),
+        warnings: z.array(z.any()).optional(),
+        errors: z.array(z.any()).optional()
+      }
+    },
+    async input => asToolResult(await getTihaoApiDoc(input))
+  );
+
+  server.registerTool(
+    'tihao_api_call',
+    {
+      title: 'Call Tihao E-Commerce Data Endpoint',
+      description: [
+        'Call a registered voc-e-commerce endpoint by id (with params), or any unregistered proxyPath via rawPath + method + params.',
+        'Use this for second-wave detail enrichment: pass a creator id (douyin oAuthorId = search star_id; xiaohongshu userId) to fetch CPM/CPE、商单、中位数 etc.',
+        'Hides raw auth/recharge/upstream errors behind actionable states (needs_token, needs_valid_token, needs_recharge, needs_input, upstream_unstable).'
+      ].join(' '),
+      inputSchema: {
+        id: z.string().optional(),
+        endpointId: z.string().optional(),
+        rawPath: z.string().optional(),
+        proxyPath: z.string().optional(),
+        path: z.string().optional(),
+        method: z.string().optional(),
+        params: z.object({}).passthrough().optional(),
+        query: z.object({}).passthrough().optional(),
+        body: z.object({}).passthrough().optional(),
+        baseUrl: z.string().optional(),
+        retries: z.number().int().min(0).max(5).optional(),
+        tihaoToken: z.string().optional(),
+        sessionToken: z.string().optional(),
+        vocToken: z.string().optional(),
+        allowEnvToken: z.boolean().optional()
+      },
+      outputSchema: {
+        status: z.string(),
+        assistantMessage: z.string(),
+        summary: z.object({}).passthrough().optional(),
+        data: z.object({}).passthrough().optional(),
+        files: z.array(z.string()).optional(),
+        nextActions: z.array(z.string()).optional(),
+        warnings: z.array(z.any()).optional(),
+        errors: z.array(z.any()).optional()
+      }
+    },
+    async input => asToolResult(await callTihaoApi(input))
+  );
+
   return server;
   return server;
 }
 }
 
 

+ 329 - 0
claude-code-tihao-sourcing/mcp/src/tools/tihao-api-catalog-run.js

@@ -0,0 +1,329 @@
+const { readTihaoToken, readNewApiToken } = require('../core/credentials');
+const {
+  listEndpoints,
+  findEndpoint,
+  searchEndpoints,
+  summarizeEndpoint,
+  hydrateEndpoint,
+  loadCatalog
+} = require('../core/api-catalog');
+const { callEcommerceGateway, redactSecret } = require('../providers/ecommerce-gateway');
+const { callSocialGateway } = require('../providers/social-gateway');
+const {
+  buildTihaoRechargeInfo,
+  buildMissingTokenMessage,
+  buildRechargeRequiredMessage,
+  buildFmodeApiRechargeUrl,
+  buildFmodeApiRechargeMessage,
+  buildPermissionDeniedMessage
+} = require('../core/payment-links');
+const { okResult, errorResult } = require('../core/result-envelope');
+
+function buildCallTemplate(endpoint) {
+  const args = { id: endpoint.id, params: {} };
+  for (const param of endpoint.params || []) {
+    if (param.required || param.default !== undefined) {
+      args.params[param.name] = param.default !== undefined ? param.default : `<${param.name}>`;
+    }
+  }
+  return args;
+}
+
+async function searchTihaoApis(input = {}) {
+  const catalog = loadCatalog();
+  const query = input.query || input.q || input.keyword || '';
+  const matches = (query || input.platform || input.tag)
+    ? searchEndpoints({ query, platform: input.platform, tag: input.tag, limit: input.limit || 20 })
+    : listEndpoints();
+  const results = matches.map(summarizeEndpoint);
+  const lines = [
+    '## 提号电商数据转发接口清单',
+    '',
+    `网关:${catalog.gateway?.ecommerce?.baseUrl || ''}(通用转发,proxyPath 透传到公司电商数据上游;每次成功调用计费 1 次)。`,
+    '',
+    catalog.workflow || '',
+    '',
+    query ? `匹配关键词「${query}」的接口(${results.length} 条):` : `全部已登记接口(${results.length} 条):`,
+    '',
+    ...results.map(
+      item =>
+        `- ${item.id} | ${item.title}(${item.wave === 'detail' ? '详情/第二波' : '搜索/第一波'} · ${item.method} ${item.proxyPath})必填参数: ${item.requiredParams.join(', ') || '无'}`
+    ),
+    '',
+    '下一步:用 tihao_api_doc 读取某个接口的详细参数文档,再用 tihao_api_call 传参调用。',
+    '清单里没有的接口,可直接用 tihao_api_call 传 rawPath + method + params 调用。'
+  ];
+  return okResult({
+    assistantMessage: lines.join('\n'),
+    summary: { total: results.length, query: query || null },
+    data: { endpoints: results, gateway: catalog.gateway, platforms: catalog.platforms, workflow: catalog.workflow }
+  });
+}
+
+async function getTihaoApiDoc(input = {}) {
+  const idOrPath = input.id || input.endpointId || input.proxyPath || input.rawPath || input.path;
+  if (!idOrPath) {
+    return errorResult('请提供接口 id 或 proxyPath(可先用 tihao_api_search 查清单)。', {
+      data: { endpoints: listEndpoints().map(summarizeEndpoint) }
+    });
+  }
+  let endpoint = findEndpoint(idOrPath);
+  if (!endpoint) {
+    return errorResult(`清单里没有找到接口「${idOrPath}」。可用 tihao_api_search 查询,或直接用 tihao_api_call 传 rawPath 调用未登记接口。`, {
+      data: { endpoints: listEndpoints().map(summarizeEndpoint) }
+    });
+  }
+  endpoint = hydrateEndpoint(endpoint);
+  const paramLines = (endpoint.params || []).map(p => {
+    const flag = p.required ? '必填' : '可选';
+    const def = p.default !== undefined ? `,默认 ${JSON.stringify(p.default)}` : '';
+    return `- ${p.name}(${p.in}, ${p.type}, ${flag}${def}):${p.desc || ''}`;
+  });
+  const template = buildCallTemplate(endpoint);
+  const lines = [
+    `## ${endpoint.title}(${endpoint.id})`,
+    '',
+    endpoint.summary || '',
+    '',
+    `请求:${endpoint.method} ${endpoint.proxyPath}(计费 ${endpoint.billing || 1} 次)`,
+    '',
+    '参数:',
+    ...paramLines,
+    '',
+    endpoint.responseHint ? `返回:${endpoint.responseHint}` : '',
+    '',
+    '调用示例(传给 tihao_api_call):',
+    '```json',
+    JSON.stringify(template, null, 2),
+    '```'
+  ].filter(line => line !== '');
+  return okResult({
+    assistantMessage: lines.join('\n'),
+    summary: { id: endpoint.id, method: endpoint.method, proxyPath: endpoint.proxyPath },
+    data: { endpoint, callTemplate: template }
+  });
+}
+
+function resolveParamValue(name, params, input) {
+  if (params && Object.prototype.hasOwnProperty.call(params, name)) return params[name];
+  if (Object.prototype.hasOwnProperty.call(input, name)) return input[name];
+  return undefined;
+}
+
+function buildRequestParts(endpoint, input) {
+  const params = input.params && typeof input.params === 'object' ? input.params : {};
+  const query = {};
+  const body = {};
+  const missing = [];
+  const target = (param) => {
+    const where = param.in || endpoint.paramsIn || (endpoint.method === 'GET' ? 'query' : 'body');
+    return where === 'query' ? query : body;
+  };
+  for (const param of endpoint.params || []) {
+    let value = resolveParamValue(param.name, params, input);
+    if ((value === undefined || value === null || value === '') && param.default !== undefined) {
+      value = param.default;
+    }
+    if (value === undefined || value === null || value === '') {
+      if (param.required) missing.push(param.name);
+      continue;
+    }
+    target(param)[param.name] = value;
+  }
+  return { query, body: endpoint.method === 'GET' ? undefined : body, missing };
+}
+
+function buildRawRequestParts(input) {
+  const method = String(input.method || 'GET').toUpperCase();
+  const explicitQuery = input.query && typeof input.query === 'object' ? input.query : null;
+  const explicitBody = input.body && typeof input.body === 'object' ? input.body : null;
+  const params = input.params && typeof input.params === 'object' ? input.params : {};
+  if (method === 'GET') {
+    return { method, query: explicitQuery || params, body: undefined };
+  }
+  return { method, query: explicitQuery || {}, body: explicitBody || params };
+}
+
+async function callTihaoApi(input = {}) {
+  const idOrPath = input.id || input.endpointId;
+  const rawPath = input.rawPath || input.proxyPath || (idOrPath ? null : input.path);
+  let endpoint = idOrPath || rawPath ? findEndpoint(idOrPath || rawPath) : undefined;
+
+  if (!endpoint && !rawPath) {
+    return errorResult('请提供接口 id(先用 tihao_api_search 查清单),或提供 rawPath + method 调用未登记接口。', {
+      data: { endpoints: listEndpoints().map(summarizeEndpoint) }
+    });
+  }
+  if (endpoint) endpoint = hydrateEndpoint(endpoint);
+
+  // 计费优先走 NewAPI(fmode-api)的 sk- token;若该 token 鉴权失败,自动回退平台 sessionToken。
+  const newapiToken = readNewApiToken(input);
+  const sessionToken = (readTihaoToken(input));
+  const token = newapiToken || sessionToken;
+  const fallbackToken = newapiToken ? sessionToken : '';
+  if (!token) {
+    const recharge = await buildTihaoRechargeInfo();
+    return {
+      status: 'needs_token',
+      assistantMessage: buildMissingTokenMessage(recharge.paymentUrl),
+      summary: { endpoint: endpoint?.id || rawPath },
+      data: { recharge },
+      files: [],
+      nextActions: [
+        `打开开通/充值链接:${recharge.paymentUrl}`,
+        '把 NewAPI 的 fmode-api token(sk-)或平台 sessionToken 配置到当前工作区 .env.local / ~/.claude/voc-credentials.json,或传入 newapiToken / sessionToken / tihaoToken 字段后重试'
+      ],
+      warnings: [],
+      errors: []
+    };
+  }
+
+  // 已登记接口可带 channel 字段:social 走社媒中转网关(voc-social,小红书素人/非蒲公英等),
+  // 其余默认走电商网关(voc-e-commerce)。未登记的 rawPath 调用默认电商网关,可用 input.channel 覆盖。
+  const channel = String((endpoint && endpoint.channel) || input.channel || 'ecommerce').toLowerCase();
+  const callGateway = channel === 'social' ? callSocialGateway : callEcommerceGateway;
+
+  let proxyPath;
+  let method;
+  let query;
+  let body;
+  if (endpoint) {
+    proxyPath = endpoint.proxyPath;
+    method = endpoint.method;
+    const parts = buildRequestParts(endpoint, input);
+    if (parts.missing.length) {
+      const missingMessage = `缺少必填参数:${parts.missing.join(', ')}。这不是类目/关键词/余额问题,请补齐参数后再调用(可用 tihao_api_doc 查看参数说明)。`;
+      return {
+        status: 'needs_input',
+        assistantMessage: missingMessage,
+        summary: { endpoint: endpoint.id, missing: parts.missing },
+        data: { endpoint: summarizeEndpoint(endpoint), missing: parts.missing },
+        files: [],
+        nextActions: ['用 tihao_api_doc 核对必填参数', '补齐参数后重新调用'],
+        warnings: [],
+        errors: [{ message: `缺少必填参数:${parts.missing.join(', ')}`, kind: 'request', httpStatus: 0 }]
+      };
+    }
+    query = parts.query;
+    body = parts.body;
+  } else {
+    proxyPath = rawPath;
+    const parts = buildRawRequestParts(input);
+    method = parts.method;
+    query = parts.query;
+    body = parts.body;
+  }
+
+  try {
+    const result = await callGateway({
+      proxyPath,
+      method,
+      query,
+      body,
+      token,
+      fallbackToken,
+      baseUrl: input.baseUrl,
+      retries: Number.isFinite(input.retries) ? input.retries : 3
+    });
+    return okResult({
+      assistantMessage: `调用成功:${proxyPath}(${method})。`,
+      summary: {
+        endpoint: endpoint?.id || proxyPath,
+        method,
+        proxyPath,
+        billing: endpoint?.billing || 1,
+        httpStatus: result.httpStatus
+      },
+      data: { result: result.data, raw: result.json }
+    });
+  } catch (error) {
+    const kind = String(error && (error.kind || error.errorKind) || 'upstream');
+    const httpStatus = Number(error && error.httpStatus) || 0;
+    const safeMessage = redactSecret(error && error.message ? error.message : 'request failed');
+
+    if (kind === 'auth') {
+      const recharge = await buildTihaoRechargeInfo({ sessionToken: (sessionToken || token) });
+      return {
+        status: 'needs_valid_token',
+        assistantMessage: [
+          '当前提号电商数据服务 token 没有通过校验,暂时不能调用该接口。',
+          '',
+          '这是 token 缺失/无效/已失效,不是关键词/类目/参数问题:请重新配置有效的 NewAPI(fmode-api)token(sk-)或平台 sessionToken 后重试。',
+          '',
+          `如需开通/充值,请打开:${recharge.paymentUrl}`
+        ].join('\n'),
+        summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
+        data: { recharge },
+        files: [],
+        nextActions: ['配置有效的 NewAPI token(sk-)或平台 sessionToken 后重试', `如需开通/充值:${recharge.paymentUrl}`],
+        warnings: [],
+        errors: []
+      };
+    }
+    if (kind === 'billing') {
+      // 402:NewAPI(fmode-api)余额不足,跳转 fmode-api 余额充值弹窗。
+      const fmodeApiRechargeUrl = buildFmodeApiRechargeUrl();
+      return {
+        status: 'needs_recharge',
+        assistantMessage: buildFmodeApiRechargeMessage(fmodeApiRechargeUrl),
+        summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
+        data: { recharge: { paymentUrl: fmodeApiRechargeUrl, kind: 'fmode-api' } },
+        files: [],
+        nextActions: [`打开充值链接补充余额:${fmodeApiRechargeUrl}`],
+        warnings: [],
+        errors: []
+      };
+    }
+    if (kind === 'permission') {
+      // 403:用户被禁用/无权限,区别于余额不足,不引导充值。
+      return {
+        status: 'needs_permission',
+        assistantMessage: buildPermissionDeniedMessage(),
+        summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
+        data: {},
+        files: [],
+        nextActions: ['联系服务方确认账号状态或接口权限后重试'],
+        warnings: [],
+        errors: []
+      };
+    }
+    if (kind === 'request') {
+      return {
+        status: 'needs_input',
+        assistantMessage: [
+          `接口 ${proxyPath} 返回了入参/请求错误(${safeMessage})。`,
+          '',
+          '这不是余额不足,也不是类目不支持:请检查参数是否符合接口文档(可用 tihao_api_doc 查看),修正后重试。',
+          '提示:抖音详情接口入参 oAuthorId 取自搜索结果的 star_id;小红书详情接口入参 userId 取自搜索结果的 userId。'
+        ].join('\n'),
+        summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
+        data: { message: safeMessage },
+        files: [],
+        nextActions: ['用 tihao_api_doc 核对参数', '修正参数后重新调用'],
+        warnings: [],
+        errors: [{ message: safeMessage, kind, httpStatus }]
+      };
+    }
+    return {
+      status: 'upstream_unstable',
+      assistantMessage: [
+        `接口 ${proxyPath} 这次调用失败,是上游电商数据接口返回错误或暂时不稳定(如 5xx、连接超时、fetch failed)。`,
+        '',
+        '这不是关键词问题,也不是类目不支持、也不是余额不足:通常稍后重试即可(本工具已自动重试若干次)。',
+        '若持续失败,可换用其它详情接口或先用搜索结果里已有的基础字段继续。'
+      ].join('\n'),
+      summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
+      data: { message: safeMessage },
+      files: [],
+      nextActions: ['稍后重试该接口调用'],
+      warnings: [],
+      errors: [{ message: safeMessage, kind, httpStatus }]
+    };
+  }
+}
+
+module.exports = {
+  searchTihaoApis,
+  getTihaoApiDoc,
+  callTihaoApi
+};

+ 2 - 2
claude-code-tihao-sourcing/package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "@vocmarket/tihao",
   "name": "@vocmarket/tihao",
-  "version": "1.0.11",
+  "version": "1.0.19",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "@vocmarket/tihao",
       "name": "@vocmarket/tihao",
-      "version": "1.0.11",
+      "version": "1.0.19",
       "license": "ISC",
       "license": "ISC",
       "dependencies": {
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.29.0",
         "@modelcontextprotocol/sdk": "^1.29.0",

+ 13 - 2
claude-code-tihao-sourcing/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@vocmarket/tihao",
   "name": "@vocmarket/tihao",
-  "version": "1.0.11",
+  "version": "1.0.19",
   "description": "Claude Code skill package for Tihao AI creator sourcing and Fmode image analysis: upload/read briefs, call VOC e-commerce proxy, analyze images with Doubao vision, rank creators, export business-ready blogger lists, and keep preference memory.",
   "description": "Claude Code skill package for Tihao AI creator sourcing and Fmode image analysis: upload/read briefs, call VOC e-commerce proxy, analyze images with Doubao vision, rank creators, export business-ready blogger lists, and keep preference memory.",
   "type": "commonjs",
   "type": "commonjs",
   "bin": {
   "bin": {
@@ -55,6 +55,17 @@
     "history:from-csv": "node scripts/history-dataset-from-csv.js",
     "history:from-csv": "node scripts/history-dataset-from-csv.js",
     "history:from-csv:smoke": "node scripts/history-dataset-from-csv-smoke.js",
     "history:from-csv:smoke": "node scripts/history-dataset-from-csv-smoke.js",
     "history:audit:smoke": "node scripts/historical-dataset-audit-smoke.js",
     "history:audit:smoke": "node scripts/historical-dataset-audit-smoke.js",
+    "case:import": "node scripts/case-template-import.js",
+    "case:import:smoke": "node scripts/case-template-import-smoke.js",
+    "platform:quality-rules:smoke": "node scripts/platform-quality-rules-smoke.js",
+    "goal:compliance:smoke": "node scripts/goal-compliance-rules-smoke.js",
+    "brief:patterns": "node scripts/generate-brief-rule-patterns.js",
+    "brief:patterns:smoke": "node scripts/brief-rule-patterns-smoke.js",
+    "fewshot:smoke": "node scripts/few-shot-cases-smoke.js",
+    "pgy:schema": "node scripts/generate-pgy-schema.js",
+    "pgy:schema:smoke": "node scripts/pgy-schema-smoke.js",
+    "media:schema": "node scripts/generate-media-library-schema.js",
+    "media:schema:smoke": "node scripts/media-library-schema-smoke.js",
     "data:intake-template": "node scripts/create-data-intake-pack.js",
     "data:intake-template": "node scripts/create-data-intake-pack.js",
     "data:intake-template:smoke": "node scripts/create-data-intake-pack-smoke.js",
     "data:intake-template:smoke": "node scripts/create-data-intake-pack-smoke.js",
     "intake:readiness": "node scripts/intake-readiness-audit.js",
     "intake:readiness": "node scripts/intake-readiness-audit.js",
@@ -147,7 +158,7 @@
     "blogger-list",
     "blogger-list",
     "brief",
     "brief",
     "mcp",
     "mcp",
-    "justone"
+    "voc-e-commerce"
   ],
   ],
   "dependencies": {
   "dependencies": {
     "@modelcontextprotocol/sdk": "^1.29.0",
     "@modelcontextprotocol/sdk": "^1.29.0",

+ 48 - 0
claude-code-tihao-sourcing/scripts/brief-rule-patterns-smoke.js

@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+const { CASE_PATTERNS, PATTERN_LIBRARY, extractPatternHits } = require('../mcp/src/features/tihao-sourcing/brief-rule-patterns');
+const { parseBrief } = require('../mcp/src/features/tihao-sourcing/brief-parser');
+
+function main() {
+  // 13 个真实案例结构化
+  assert(CASE_PATTERNS.length === 13, `应有 13 个真实案例,实际 ${CASE_PATTERNS.length}`);
+  assert(CASE_PATTERNS.some(item => item.id === 'case-2-dha'), '应包含 DHA 案例');
+  assert(CASE_PATTERNS.some(item => item.id === 'case-6-zelimei'), '应包含泽立美案例');
+  assert(CASE_PATTERNS.every(item => item.caseName && item.accountTypes), '每个案例应有 caseName 和 accountTypes');
+
+  // 规则模式库
+  assert(PATTERN_LIBRARY.cpc && PATTERN_LIBRARY.cpv && PATTERN_LIBRARY.recentPlays, '应包含 CPC/CPV/近3条播放模式');
+  assert(PATTERN_LIBRARY.ipMustBe && PATTERN_LIBRARY.sameProvince, '应包含地域硬约束模式');
+  assert(PATTERN_LIBRARY.underAgeRatio && PATTERN_LIBRARY.interestTags, '应包含低龄占比/兴趣标签模式');
+
+  // 模式提取
+  const dha = extractPatternHits('女性25-44岁占比70%以上,兴趣前三标签含2个:母婴/萌娃/记录生活,CPE≤3,阅读中位数≥4000,预估阅读单价≤1.0');
+  assert(dha.some(item => item.key === 'interestTags'), '应识别兴趣标签模式');
+  assert(dha.some(item => item.key === 'ageRatio'), '应识别年龄占比模式');
+
+  const region = extractPatternHits('达人ip必须为宁夏');
+  assert(region.some(item => item.key === 'ipMustBe'), '应识别达人IP硬约束');
+
+  // parseBrief 端到端识别真实案例表达
+  const dhaBrief = parseBrief({ briefText: '产品DHA,账号类型干货科普营养师妈妈。CPE≤3,阅读中位数≥4000,CPC≤0.8,近3条视频平均播放>30万,女性25-44岁占比70%以上,兴趣前三标签含2个,达人ip必须宁夏' });
+  assert(dhaBrief.performanceRequirements.some(item => /CPE≤3/.test(item)), '应解析 CPE≤3');
+  assert(dhaBrief.performanceRequirements.some(item => /CPC≤0.8/.test(item)), '应解析 CPC≤0.8');
+  assert(dhaBrief.performanceRequirements.some(item => /近3条平均播放>30万/.test(item)), '应解析近3条播放');
+  assert(dhaBrief.performanceRequirements.some(item => /25-44岁占比70%以上/.test(item)), '应解析年龄占比');
+  assert(dhaBrief.performanceRequirements.some(item => /兴趣前三标签含2个/.test(item)), '应解析兴趣标签');
+  assert(dhaBrief.performanceRequirements.some(item => /达人IP必须宁夏/.test(item)), '应解析达人IP');
+  assert(Array.isArray(dhaBrief.briefRulePatterns) && dhaBrief.briefRulePatterns.length > 0, '应输出 briefRulePatterns');
+
+  console.log(JSON.stringify({
+    ok: true,
+    caseCount: CASE_PATTERNS.length,
+    patternCount: Object.keys(PATTERN_LIBRARY).length,
+    parsedRequirementCount: dhaBrief.performanceRequirements.length,
+    matchedPatternCount: dhaBrief.briefRulePatterns.length
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 77 - 0
claude-code-tihao-sourcing/scripts/case-template-import-smoke.js

@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+const {
+  parseCasesFromSheets,
+  buildFewshotCases,
+  buildInsights,
+  buildHistoryCsv,
+  buildRuleSuggestions,
+  extractSignalTags
+} = require('./case-template-import');
+
+// 模拟 readXlsxRows 返回的 sheets 结构(含标题行、说明行、表头行、数据行)
+const SHEETS = [
+  {
+    name: '案例喂养',
+    rows: [
+      ['AI 选号 · 案例喂养模板(每单执行后回填,用于训练选号模型)'],
+      ['说明:结果标签用于 few-shot/反向学习;关键原因请写实,越具体模型越准。'],
+      ['案例ID', '品类', '投放目标', '目标人群', '品牌调性', '平台', '选中账号特征', '结果标签', '关键原因', '客户反馈', '执行数据'],
+      ['如 CASE-2026-001', '美妆护肤/3C/母婴...', '种草转化/品牌曝光...', '如 20-28岁油痘肌女性', '硬核测评/生活化种草...', 'douyin/xiaohongshu...', '粉丝量/互动率/调性/报价 摘要', '爆文 / 达标 / 一般 / 翻车', '为什么成/败?', '确认/修改/否决及理由', '互动量/ROI/搜索提升等'],
+      ['CASE-2026-001', '美妆护肤', '种草转化', '20-28岁油痘肌女性', '硬核测评', 'xiaohongshu', '18w粉/互动率3.5%/成分党/报价8k', '爆文', '赞藏比高+垂直度92%,与油皮人群高度契合', '客户确认续投', '单篇赞评藏1200,搜索占位TOP3'],
+      ['CASE-2026-002', '美妆护肤', '种草转化', '20-28岁油痘肌女性', '硬核测评', 'douyin', '500w粉/互动率0.8%/搞笑/报价20w', '翻车', '粉丝画像匹配仅35%,互动率虚胖,内容疲劳', '客户否决', '无转化,评论区负面'],
+      ['CASE-2026-003', '3C', '品牌曝光', '25-35岁男性', '硬核测评', 'douyin', '200w粉/互动率1.2%/科技测评/报价15w', '一般', '数据达标但人群略泛', '待二次确认', '阅读稳定']
+    ]
+  }
+];
+
+function main() {
+  const cases = parseCasesFromSheets(SHEETS);
+  assert(cases.length === 3, '应解析出 3 条案例,跳过说明行');
+
+  const positive = cases.filter(item => item.outcome === 'positive');
+  const neutral = cases.filter(item => item.outcome === 'neutral');
+  const negative = cases.filter(item => item.outcome === 'negative');
+  assert(positive.length === 1, '爆文应为正样本');
+  assert(neutral.length === 1, '一般应为中性样本');
+  assert(negative.length === 1, '翻车应为负样本');
+
+  assert(cases[0].platform === 'xiaohongshu', '平台应归一化为内部代码');
+  assert(cases[0].platformLabel === '小红书', '平台中文标签应正确');
+  assert(cases[1].platform === 'douyin', '抖音平台应归一化');
+
+  assert(extractSignalTags('互动率虚胖,粉丝画像匹配仅35%').includes('数据虚胖'), '应识别数据虚胖信号');
+  assert(extractSignalTags('垂直度92%,人群高度契合').includes('人群契合'), '应识别人群契合信号');
+
+  const fewshot = buildFewshotCases(cases, 'case_template.xlsx');
+  assert(fewshot.version === 1, 'few-shot 库应带版本号');
+  assert(fewshot.cases.length === 3, 'few-shot 库应包含全部案例');
+  assert(fewshot.cases[0].outcome === 'positive', 'few-shot 库应保留 outcome 方向');
+
+  const insights = buildInsights(cases);
+  assert(insights.includes('正样本共性'), '洞察应包含正样本段落');
+  assert(insights.includes('负样本共性'), '洞察应包含负样本段落');
+  assert(insights.includes('CASE-2026-002'), '洞察应包含翻车案例');
+
+  const historyCsv = buildHistoryCsv(cases);
+  assert(historyCsv.startsWith('brief编号'), 'history CSV 应以 HISTORY_HEADER 开头');
+  assert(historyCsv.includes('CASE-2026-002') && historyCsv.includes('客户拒绝'), '负样本应映射为客户拒绝');
+  assert(historyCsv.includes('粉丝画像匹配仅35%'), '负样本拒绝原因应写入 CSV');
+
+  // 规则建议:两条负样本同属「互动率」信号才会触发建议,当前只有一条负样本,应返回空
+  assert(buildRuleSuggestions(cases).length === 0, '样本不足时不应生成高频规则建议');
+
+  console.log(JSON.stringify({
+    ok: true,
+    caseCount: cases.length,
+    positive: positive.length,
+    neutral: neutral.length,
+    negative: negative.length,
+    fewshotCases: fewshot.cases.length
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 337 - 0
claude-code-tihao-sourcing/scripts/case-template-import.js

@@ -0,0 +1,337 @@
+#!/usr/bin/env node
+const fs = require('fs');
+const path = require('path');
+const { readXlsxRows } = require('../mcp/src/core/files');
+
+// case_template.xlsx 的字段(中文表头 → 内部 key)
+const FIELD_MAP = {
+  '案例ID': 'id',
+  '品类': 'category',
+  '投放目标': 'goal',
+  '目标人群': 'audience',
+  '品牌调性': 'brandTone',
+  '平台': 'platform',
+  '选中账号特征': 'selectedAccountProfile',
+  '结果标签': 'outcomeLabel',
+  '关键原因': 'keyReason',
+  '客户反馈': 'customerFeedback',
+  '执行数据': 'executionData'
+};
+
+// 结果标签枚举(说明行 r3 的值是「爆文 / 达标 / 一般 / 翻车」,含斜杠,会被此白名单排除)
+const OUTCOME_LABELS = ['爆文', '达标', '一般', '翻车'];
+
+// 平台中文 → 内部代码
+const PLATFORM_CODE = {
+  '小红书': 'xiaohongshu',
+  'xiaohongshu': 'xiaohongshu',
+  'xhs': 'xiaohongshu',
+  '抖音': 'douyin',
+  'douyin': 'douyin',
+  'b站': 'bilibili',
+  'bilibili': 'bilibili'
+};
+
+// 结果标签 → outcome 方向
+const OUTCOME_BY_LABEL = {
+  '爆文': 'positive',
+  '达标': 'positive',
+  '一般': 'neutral',
+  '翻车': 'negative'
+};
+
+// 结果标签 → 客户选择(用于 history 数据集)
+const CUSTOMER_DECISION_BY_LABEL = {
+  '爆文': '客户选中',
+  '达标': '客户选中',
+  '一般': '待客户反馈',
+  '翻车': '客户拒绝'
+};
+
+// 从关键原因抽取的信号标签(用于给案例打标签,便于后续检索同类案例 / 沉淀规则)
+const SIGNAL_DICT = [
+  { tag: '互动率', words: ['互动率', '互动', '赞藏比', '赞评藏', '点赞', '收藏'] },
+  { tag: '垂直度', words: ['垂直度', '垂直', '垂直度92', '垂直度35'] },
+  { tag: '画像匹配', words: ['画像匹配', '画像', '粉丝画像', '匹配'] },
+  { tag: '调性匹配', words: ['调性', '成分党', '硬核测评', '测评', '素人', '生活化', '科普'] },
+  { tag: '内容质量', words: ['内容', '疲劳', '干货', '深度', '创意'] },
+  { tag: '数据虚胖', words: ['虚胖', '水分', '刷', '重复'] },
+  { tag: '转化/ROI', words: ['转化', 'ROI', 'roi', '搜索', '占位', 'GMV', '下单'] },
+  { tag: '负面反馈', words: ['负面', '差评', '投诉', '无转化', '掉粉', '翻车'] },
+  { tag: '报价匹配', words: ['报价', '预算', 'CPM', 'CPE', '性价比', '成本'] },
+  { tag: '人群契合', words: ['契合', '对口', '精准', '人群', '年龄'] }
+];
+
+function main() {
+  const args = parseArgs(process.argv.slice(2));
+  const input = path.resolve(
+    args.input || args.xlsx || process.env.TIHAO_CASE_TEMPLATE ||
+    path.join(__dirname, '..', '..', 'docs', '20260813 - AI提号规则及喂养资料', '历史选号案例', 'case_template.xlsx')
+  );
+  const outputDir = path.resolve(args.output || path.join(path.dirname(input), 'case-import'));
+  if (!fs.existsSync(input)) throw new Error(`case_template.xlsx 不存在:${input}`);
+
+  const cases = readCaseTemplate(input);
+  if (cases.length === 0) throw new Error(`未在 ${input} 里解析到任何案例数据行`);
+
+  fs.mkdirSync(outputDir, { recursive: true });
+  // 技能包内打包路径:随 npm 包分发(package.json files 含 docs/),运行时 few-shot-cases.js 优先读这里
+  const pkgCaseLibraryDir = path.join(__dirname, '..', 'docs', 'case-library');
+  fs.mkdirSync(pkgCaseLibraryDir, { recursive: true });
+  const files = {
+    fewshotCases: path.join(outputDir, 'fewshot-cases.json'),
+    insights: path.join(outputDir, 'case-insights.md'),
+    historyCsv: path.join(outputDir, 'history-data-template.csv'),
+    summary: path.join(outputDir, 'case-import-summary.json'),
+    pkgFewshotCases: path.join(pkgCaseLibraryDir, 'fewshot-cases.json')
+  };
+
+  const fewshotPayload = buildFewshotCases(cases, input);
+  fs.writeFileSync(files.fewshotCases, JSON.stringify(fewshotPayload, null, 2), 'utf8');
+  fs.writeFileSync(files.pkgFewshotCases, JSON.stringify(fewshotPayload, null, 2), 'utf8');
+  fs.writeFileSync(files.insights, withBom(buildInsights(cases)), 'utf8');
+  fs.writeFileSync(files.historyCsv, withBom(buildHistoryCsv(cases)), 'utf8');
+
+  const summary = {
+    input,
+    outputDir,
+    generatedAt: new Date().toISOString(),
+    caseCount: cases.length,
+    positive: cases.filter(item => item.outcome === 'positive').length,
+    neutral: cases.filter(item => item.outcome === 'neutral').length,
+    negative: cases.filter(item => item.outcome === 'negative').length,
+    files
+  };
+  fs.writeFileSync(files.summary, JSON.stringify(summary, null, 2), 'utf8');
+  console.log(JSON.stringify(summary, null, 2));
+}
+
+// 读取 xlsx,定位表头行,跳过说明行,返回映射后的案例数组
+function readCaseTemplate(file) {
+  return parseCasesFromSheets(readXlsxRows(file));
+}
+
+function parseCasesFromSheets(sheets) {
+  const cases = [];
+  for (const sheet of sheets) {
+    const headerIndex = sheet.rows.findIndex(row => hasHeader(row));
+    if (headerIndex < 0) continue;
+    const header = sheet.rows[headerIndex];
+    for (let index = headerIndex + 1; index < sheet.rows.length; index += 1) {
+      const raw = sheet.rows[index];
+      const mapped = mapRow(header, raw);
+      if (!mapped || !OUTCOME_LABELS.includes(mapped.outcomeLabel)) continue; // 跳过说明行和空行
+      cases.push(mapped);
+    }
+  }
+  return cases;
+}
+
+function hasHeader(row) {
+  const text = row.map(item => String(item || '')).join('|');
+  return text.includes('案例ID') && text.includes('结果标签');
+}
+
+function mapRow(header, raw) {
+  const record = {};
+  header.forEach((label, index) => {
+    const key = FIELD_MAP[String(label || '').trim()];
+    if (key) record[key] = String(raw[index] || '').trim();
+  });
+  if (!record.id) return null;
+  const platform = normalizePlatform(record.platform);
+  const outcome = OUTCOME_BY_LABEL[record.outcomeLabel] || 'neutral';
+  return {
+    ...record,
+    platform,
+    platformLabel: labelPlatform(platform),
+    outcome,
+    signalTags: extractSignalTags(record.keyReason)
+  };
+}
+
+function buildFewshotCases(cases, source) {
+  return {
+    version: 1,
+    source,
+    generatedAt: new Date().toISOString(),
+    purpose: '案例喂养 few-shot 库:品类+投放目标+人群+调性+平台 -> 选中账号特征 -> 结果标签。用于下次选号时做正/负样本参照。',
+    cases: cases.map(item => ({
+      id: item.id,
+      category: item.category,
+      goal: item.goal,
+      audience: item.audience,
+      brandTone: item.brandTone,
+      platform: item.platform,
+      platformLabel: item.platformLabel,
+      selectedAccountProfile: item.selectedAccountProfile,
+      outcomeLabel: item.outcomeLabel,
+      outcome: item.outcome,
+      keyReason: item.keyReason,
+      customerFeedback: item.customerFeedback,
+      executionData: item.executionData,
+      signalTags: item.signalTags
+    }))
+  };
+}
+
+function buildInsights(cases) {
+  const positive = cases.filter(item => item.outcome === 'positive');
+  const neutral = cases.filter(item => item.outcome === 'neutral');
+  const negative = cases.filter(item => item.outcome === 'negative');
+  const lines = [];
+  lines.push('# 案例归因洞察');
+  lines.push('');
+  lines.push(`> 由 case_template.xlsx 自动生成,共 ${cases.length} 条案例(正 ${positive.length} / 中性 ${neutral.length} / 负 ${negative.length})。`);
+  lines.push('> 用途:把每单执行结果沉淀成可复用信号,人工复核后升级到 experience-rules 或 preference memory。');
+  lines.push('');
+
+  lines.push('## 正样本共性(爆文 / 达标)');
+  lines.push('');
+  if (positive.length) {
+    positive.forEach(item => lines.push(`- ${item.id}|${item.category}|${item.brandTone}|「${item.keyReason}」`));
+    const tags = countTags(positive);
+    lines.push('');
+    lines.push('成功信号频次:');
+    lines.push('');
+    lines.push('| 信号 | 次数 |');
+    lines.push('| --- | --- |');
+    Object.entries(tags).sort((a, b) => b[1] - a[1]).forEach(([tag, count]) => lines.push(`| ${tag} | ${count} |`));
+  } else {
+    lines.push('暂无正样本。');
+  }
+  lines.push('');
+
+  lines.push('## 负样本共性(翻车)');
+  lines.push('');
+  if (negative.length) {
+    negative.forEach(item => lines.push(`- ${item.id}|${item.category}|${item.platformLabel}|「${item.keyReason}」→ ${item.customerFeedback}`));
+    const tags = countTags(negative);
+    lines.push('');
+    lines.push('失败信号频次:');
+    lines.push('');
+    lines.push('| 信号 | 次数 |');
+    lines.push('| --- | --- |');
+    Object.entries(tags).sort((a, b) => b[1] - a[1]).forEach(([tag, count]) => lines.push(`| ${tag} | ${count} |`));
+  } else {
+    lines.push('暂无负样本。');
+  }
+  lines.push('');
+
+  lines.push('## 建议沉淀的规则');
+  lines.push('');
+  const suggestions = buildRuleSuggestions(cases);
+  if (suggestions.length) {
+    suggestions.forEach(item => lines.push(`- ${item}`));
+  } else {
+    lines.push('- 案例量不足,暂无高频信号可沉淀。建议按模板每天补充案例后重跑本导入器。');
+  }
+  return lines.join('\n');
+}
+
+function buildRuleSuggestions(cases) {
+  const positiveTags = countTags(cases.filter(item => item.outcome === 'positive'));
+  const negativeTags = countTags(cases.filter(item => item.outcome === 'negative'));
+  const suggestions = [];
+  const negativeEntries = Object.entries(negativeTags).sort((a, b) => b[1] - a[1]);
+  for (const [tag, count] of negativeEntries) {
+    if (count >= 2) suggestions.push(`负样本信号「${tag}」出现 ${count} 次,建议在 ranker 风险扣分或 experience-rules 里加入对应剔除/降级规则。`);
+  }
+  const positiveEntries = Object.entries(positiveTags).sort((a, b) => b[1] - a[1]);
+  for (const [tag, count] of positiveEntries) {
+    if (count >= 2) suggestions.push(`正样本信号「${tag}」出现 ${count} 次,建议在 ranker 评分或 brief-parser 风格词表里加入对应加分项。`);
+  }
+  return suggestions;
+}
+
+// 对齐 HISTORY_HEADER 的 CSV(博主名称为「选中账号特征」摘要占位,需回填真实博主/主页才能进客户效果审计)
+function buildHistoryCsv(cases) {
+  const header = [
+    'brief编号', '项目名称', '类目', '客户原始Brief', '历史人工补号量基线',
+    '参考账号或视频', '平台', '博主名称', '主页链接', '人工复核标签', '客户选择', '拒绝原因'
+  ];
+  const rows = [header];
+  for (const item of cases) {
+    const isNegative = item.outcome === 'negative';
+    rows.push([
+      item.id,
+      `${item.category}-${item.goal}`,
+      item.category,
+      `【${item.brandTone}调性】【${item.audience}人群】【${item.goal}目标】`,
+      '',
+      '',
+      item.platformLabel,
+      item.selectedAccountProfile,
+      '',
+      '',
+      CUSTOMER_DECISION_BY_LABEL[item.outcomeLabel] || '待客户反馈',
+      isNegative ? item.keyReason : ''
+    ]);
+  }
+  return rows.map(row => row.map(csvCell).join(',')).join('\n');
+}
+
+function extractSignalTags(reason) {
+  const text = String(reason || '');
+  return SIGNAL_DICT.filter(item => item.words.some(word => text.includes(word))).map(item => item.tag);
+}
+
+function countTags(cases) {
+  const counter = {};
+  for (const item of cases) {
+    for (const tag of item.signalTags || []) counter[tag] = (counter[tag] || 0) + 1;
+  }
+  return counter;
+}
+
+function normalizePlatform(value) {
+  return PLATFORM_CODE[String(value || '').trim().toLowerCase()] || String(value || '').trim();
+}
+
+function labelPlatform(platform) {
+  return { xiaohongshu: '小红书', douyin: '抖音', bilibili: 'B站' }[platform] || platform;
+}
+
+function csvCell(value) {
+  const text = String(value ?? '');
+  return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
+}
+
+function withBom(text) {
+  return `${text}`;
+}
+
+function parseArgs(argv) {
+  const args = {};
+  for (let index = 0; index < argv.length; index += 1) {
+    const raw = argv[index];
+    if (!raw.startsWith('--')) continue;
+    const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
+    const next = argv[index + 1];
+    if (!next || next.startsWith('--')) args[key] = true;
+    else {
+      args[key] = next;
+      index += 1;
+    }
+  }
+  return args;
+}
+
+if (require.main === module) {
+  try {
+    main();
+  } catch (error) {
+    console.error(error && error.stack ? error.stack : String(error));
+    process.exit(1);
+  }
+}
+
+module.exports = {
+  readCaseTemplate,
+  parseCasesFromSheets,
+  buildFewshotCases,
+  buildInsights,
+  buildHistoryCsv,
+  buildRuleSuggestions,
+  extractSignalTags
+};

+ 1 - 1
claude-code-tihao-sourcing/scripts/create-video-intake-pack.js

@@ -49,7 +49,7 @@ function main() {
       '真实记录感|高质感女性场景|轻讲解',
       '真实记录感|高质感女性场景|轻讲解',
       '室内自然光、真人出镜、护肤前后对比、口播节奏稳定',
       '室内自然光、真人出镜、护肤前后对比、口播节奏稳定',
       '需复核广告合规表达和报价档期。',
       '需复核广告合规表达和报价档期。',
-      'TikHub/VOC social/人工补充均可,写来源但不要写密钥。',
+      'VOC social/人工补充均可,写来源但不要写密钥。',
       '是'
       '是'
     ],
     ],
     [
     [

+ 36 - 0
claude-code-tihao-sourcing/scripts/few-shot-cases-smoke.js

@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+const { loadFewshotCases, matchSimilarCases, buildFewshotReference } = require('../mcp/src/features/tihao-sourcing/few-shot-cases');
+
+function main() {
+  // 加载真实案例库(案例导入器产物)
+  const loaded = loadFewshotCases();
+  assert(loaded && Array.isArray(loaded.cases) && loaded.cases.length >= 1, '应加载真实 fewshot-cases.json 案例库');
+
+  // 匹配相似案例(品类匹配)
+  const criteria = { category: loaded.cases[0].category || '', platforms: [loaded.cases[0].platform || 'xiaohongshu'], goalStrategy: { goalLabel: loaded.cases[0].goal || '' } };
+  const similar = matchSimilarCases(loaded.cases, criteria);
+  assert(Array.isArray(similar), 'matchSimilarCases 应返回数组');
+
+  // buildFewshotReference 结构
+  const reference = buildFewshotReference(criteria);
+  assert(reference.status === 'loaded', '应返回 loaded 状态');
+  assert(reference.totalCases === loaded.cases.length, 'totalCases 应一致');
+  assert(Array.isArray(reference.cases), '应返回 cases 数组');
+
+  // explicitPath 不存在时应 fallback 到默认案例库(而非报错)
+  const fallback = buildFewshotReference({ category: '无' }, { fewshotCasesPath: 'e:/nonexistent/fewshot-cases.json' });
+  assert(fallback.status === 'loaded', 'explicitPath 不存在时应 fallback 到默认案例库');
+
+  console.log(JSON.stringify({
+    ok: true,
+    loadedCases: loaded.cases.length,
+    matchedCases: similar.length,
+    referenceStatus: reference.status
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 50 - 0
claude-code-tihao-sourcing/scripts/generate-brief-rule-patterns.js

@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+const fs = require('fs');
+const path = require('path');
+const { CASE_PATTERNS, PATTERN_LIBRARY } = require('../mcp/src/features/tihao-sourcing/brief-rule-patterns');
+
+function main() {
+  const args = parseArgs(process.argv.slice(2));
+  const outputDir = path.resolve(args.output || path.join(__dirname, '..', 'docs'));
+  fs.mkdirSync(outputDir, { recursive: true });
+  const outFile = path.join(outputDir, 'brief-rule-patterns.json');
+  fs.writeFileSync(outFile, JSON.stringify({
+    generatedAt: new Date().toISOString(),
+    caseCount: CASE_PATTERNS.length,
+    casePatterns: CASE_PATTERNS,
+    patternLibrary: Object.entries(PATTERN_LIBRARY).map(([key, item]) => ({
+      key,
+      label: item.label,
+      note: item.note,
+      regex: item.regex.source
+    }))
+  }, null, 2), 'utf8');
+  console.log(JSON.stringify({ outFile, caseCount: CASE_PATTERNS.length, patternCount: Object.keys(PATTERN_LIBRARY).length }, null, 2));
+}
+
+function parseArgs(argv) {
+  const args = {};
+  for (let index = 0; index < argv.length; index += 1) {
+    const raw = argv[index];
+    if (!raw.startsWith('--')) continue;
+    const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
+    const next = argv[index + 1];
+    if (!next || next.startsWith('--')) args[key] = true;
+    else {
+      args[key] = next;
+      index += 1;
+    }
+  }
+  return args;
+}
+
+if (require.main === module) {
+  try {
+    main();
+  } catch (error) {
+    console.error(error && error.stack ? error.stack : String(error));
+    process.exit(1);
+  }
+}
+
+module.exports = { main };

+ 65 - 0
claude-code-tihao-sourcing/scripts/generate-media-library-schema.js

@@ -0,0 +1,65 @@
+#!/usr/bin/env node
+const fs = require('fs');
+const path = require('path');
+const { CORE_FIELDS, REQUIRED_FIELDS, MANUAL_ONLY_FIELDS, PGY_ALIAS, INDEXES, buildDdl, buildQueryMap } = require('../mcp/src/features/tihao-sourcing/media-library-schema');
+
+function main() {
+  const args = parseArgs(process.argv.slice(2));
+  const outputDir = path.resolve(args.output || path.join(__dirname, '..', 'docs'));
+  fs.mkdirSync(outputDir, { recursive: true });
+
+  const files = {
+    schema: path.join(outputDir, 'media-library-field-schema.json'),
+    ddl: path.join(outputDir, 'media-library-collection-ddl.txt'),
+    queryMap: path.join(outputDir, 'media-library-query-map.md')
+  };
+
+  fs.writeFileSync(files.schema, JSON.stringify({
+    collection: 'media_library',
+    generatedAt: new Date().toISOString(),
+    coreFields: CORE_FIELDS,
+    requiredFields: REQUIRED_FIELDS,
+    manualOnlyFields: MANUAL_ONLY_FIELDS,
+    pgyAlias: PGY_ALIAS,
+    indexes: INDEXES
+  }, null, 2), 'utf8');
+  fs.writeFileSync(files.ddl, buildDdl(), 'utf8');
+  fs.writeFileSync(files.queryMap, buildQueryMap(), 'utf8');
+
+  console.log(JSON.stringify({
+    outputDir,
+    fieldCount: Object.keys(CORE_FIELDS).length,
+    requiredCount: REQUIRED_FIELDS.length,
+    manualOnlyCount: MANUAL_ONLY_FIELDS.length,
+    pgyAliasCount: Object.keys(PGY_ALIAS).length,
+    indexCount: INDEXES.length,
+    files
+  }, null, 2));
+}
+
+function parseArgs(argv) {
+  const args = {};
+  for (let index = 0; index < argv.length; index += 1) {
+    const raw = argv[index];
+    if (!raw.startsWith('--')) continue;
+    const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
+    const next = argv[index + 1];
+    if (!next || next.startsWith('--')) args[key] = true;
+    else {
+      args[key] = next;
+      index += 1;
+    }
+  }
+  return args;
+}
+
+if (require.main === module) {
+  try {
+    main();
+  } catch (error) {
+    console.error(error && error.stack ? error.stack : String(error));
+    process.exit(1);
+  }
+}
+
+module.exports = { main };

+ 62 - 0
claude-code-tihao-sourcing/scripts/generate-pgy-schema.js

@@ -0,0 +1,62 @@
+#!/usr/bin/env node
+const fs = require('fs');
+const path = require('path');
+const { SOURCES, DIMENSIONS, CORE_FIELDS, INDEXES, buildDdl, buildQueryMap } = require('../mcp/src/features/tihao-sourcing/pgy-schema');
+
+function main() {
+  const args = parseArgs(process.argv.slice(2));
+  const outputDir = path.resolve(args.output || path.join(__dirname, '..', 'docs'));
+  fs.mkdirSync(outputDir, { recursive: true });
+
+  const files = {
+    schema: path.join(outputDir, 'pgy-field-schema.json'),
+    ddl: path.join(outputDir, 'pgy-collection-ddl.txt'),
+    queryMap: path.join(outputDir, 'pgy-sourcing-query-map.md')
+  };
+
+  fs.writeFileSync(files.schema, JSON.stringify({
+    collection: 'pgy_blogger',
+    generatedAt: new Date().toISOString(),
+    sources: SOURCES,
+    dimensions: DIMENSIONS,
+    coreFields: CORE_FIELDS,
+    indexes: INDEXES
+  }, null, 2), 'utf8');
+  fs.writeFileSync(files.ddl, buildDdl(), 'utf8');
+  fs.writeFileSync(files.queryMap, buildQueryMap(), 'utf8');
+
+  console.log(JSON.stringify({
+    outputDir,
+    coreFieldCount: Object.keys(CORE_FIELDS).length,
+    dimensionCount: Object.keys(DIMENSIONS).length,
+    indexCount: INDEXES.length,
+    files
+  }, null, 2));
+}
+
+function parseArgs(argv) {
+  const args = {};
+  for (let index = 0; index < argv.length; index += 1) {
+    const raw = argv[index];
+    if (!raw.startsWith('--')) continue;
+    const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
+    const next = argv[index + 1];
+    if (!next || next.startsWith('--')) args[key] = true;
+    else {
+      args[key] = next;
+      index += 1;
+    }
+  }
+  return args;
+}
+
+if (require.main === module) {
+  try {
+    main();
+  } catch (error) {
+    console.error(error && error.stack ? error.stack : String(error));
+    process.exit(1);
+  }
+}
+
+module.exports = { main };

+ 55 - 0
claude-code-tihao-sourcing/scripts/goal-compliance-rules-smoke.js

@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+const { buildGoalStrategy, detectGoal, SUGGESTION_RULES } = require('../mcp/src/features/tihao-sourcing/goal-strategy');
+const { buildComplianceCheck, detectComplianceRisks, BANNED_WORD_REPLACEMENTS, PLATFORM_RULES, SAFE_WORDS } = require('../mcp/src/features/tihao-sourcing/compliance-rules');
+const { enrichCriteriaWithExperience } = require('../mcp/src/features/tihao-sourcing/experience-rules');
+
+function main() {
+  // 投放目标识别
+  assert(detectGoal('需要找博主做种草转化,带货成交') === 'seed_conversion', '应识别种草转化');
+  assert(detectGoal('品牌曝光,提升知名度,破圈传播') === 'brand_exposure', '应识别品牌曝光');
+  assert(detectGoal('搜索引流,卡小蓝词,关键词沉淀') === 'search_drain', '应识别搜索引流');
+  assert(detectGoal('大促活动冲量,同城本地铺量') === 'campaign_volume', '应识别活动冲量');
+
+  // 1.5 倍提号规则
+  const strategy = buildGoalStrategy({ rawText: '种草转化', targetCount: 10 });
+  assert(strategy.goalLabel === '种草转化', '目标中文标签应正确');
+  assert(strategy.suggestionCount === 15, '10 位需求应提 15 位(1.5 倍)');
+  assert(strategy.suggestionRules.length === 3, '应有 3 条提号规则');
+  assert(SUGGESTION_RULES.some(rule => rule.id === 'suggest-1.5x'), '应有 1.5 倍提号规则');
+  assert(SUGGESTION_RULES.some(rule => rule.id === 'style-match-80'), '应有 80% 风格匹配规则');
+
+  // 合规词检测
+  const risks = detectComplianceRisks('这个产品美白祛痘效果第一,是平替神器');
+  const banned = risks.map(item => item.banned);
+  assert(banned.includes('美白'), '应检出美白');
+  assert(banned.includes('祛痘'), '应检出祛痘');
+  assert(banned.includes('第一'), '应检出第一');
+  assert(BANNED_WORD_REPLACEMENTS.some(item => item.banned === '美白' && item.safe.includes('提亮肤色')), '美白应替换为提亮肤色');
+
+  // 合规检查结构
+  const compliance = buildComplianceCheck({ rawText: '美白产品', platforms: ['xiaohongshu', 'douyin'] });
+  assert(compliance.bannedHits.length >= 1, '应检出美白高危词');
+  assert(compliance.platformRules.length === 2, '两个平台特规');
+  assert(compliance.safeWords.length > 0 && SAFE_WORDS.includes('皮肤屏障'), '应有安全词库');
+  assert(PLATFORM_RULES.douyin && PLATFORM_RULES.xiaohongshu && PLATFORM_RULES.bilibili, '三个平台特规齐全');
+
+  // 接入 experience-rules
+  const enriched = enrichCriteriaWithExperience({ rawText: '美白种草转化,目标10位', targetCount: 10, platforms: ['xiaohongshu'], keywords: ['美白'] });
+  assert(enriched.goalStrategy && enriched.goalStrategy.goalLabel, '应输出 goalStrategy');
+  assert(enriched.compliance && enriched.compliance.bannedWordReplacements, '应输出 compliance');
+
+  console.log(JSON.stringify({
+    ok: true,
+    goalStrategies: Object.keys(require('../mcp/src/features/tihao-sourcing/goal-strategy').GOAL_STRATEGIES).length,
+    suggestionRuleCount: SUGGESTION_RULES.length,
+    bannedWordCount: BANNED_WORD_REPLACEMENTS.length,
+    platformRuleCount: Object.keys(PLATFORM_RULES).length,
+    safeWordCount: SAFE_WORDS.length
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 1 - 1
claude-code-tihao-sourcing/scripts/homepage-evidence-readiness-audit.js

@@ -156,7 +156,7 @@ function normalizeCandidate(record, rowNumber) {
   const postTitleOrTextCount = posts.filter(post => value(post.title || post.desc || post.description || post.text || post.content || post.summary).length > 0).length;
   const postTitleOrTextCount = posts.filter(post => value(post.title || post.desc || post.description || post.text || post.content || post.summary).length > 0).length;
   const publishTimeCount = posts.filter(post => value(post.publishTime || post.publishedAt || post.createdAt || post.time).length > 0).length;
   const publishTimeCount = posts.filter(post => value(post.publishTime || post.publishedAt || post.createdAt || post.time).length > 0).length;
   const interactionCount = posts.filter(hasInteraction).length;
   const interactionCount = posts.filter(hasInteraction).length;
-  const hasProviderEvidence = ['provider', 'path', 'api', 'tikhub', 'justone', 'voc-social'].includes(evidenceSource.toLowerCase()) ||
+  const hasProviderEvidence = ['provider', 'path', 'api', 'social-analysis', 'voc-e-commerce', 'voc-social'].includes(evidenceSource.toLowerCase()) ||
     Boolean(record.recentPosts || record.posts || homepageEvidence.recentPosts || homepageEvidence.posts) ||
     Boolean(record.recentPosts || record.posts || homepageEvidence.recentPosts || homepageEvidence.posts) ||
     homepageEvidence.confidence === 'provider';
     homepageEvidence.confidence === 'provider';
 
 

+ 2 - 2
claude-code-tihao-sourcing/scripts/intake-readiness-smoke.js

@@ -150,7 +150,7 @@ function videoRows() {
       '真实记录感|高质感|轻讲解',
       '真实记录感|高质感|轻讲解',
       '真人出镜、自然光、生活方式场景、节奏稳定',
       '真人出镜、自然光、生活方式场景、节奏稳定',
       '需复核广告合规和竞品合作。',
       '需复核广告合规和竞品合作。',
-      'TikHub/VOC social',
+      'VOC social',
       '是'
       '是'
     ],
     ],
     [
     [
@@ -170,7 +170,7 @@ function videoRows() {
       '生活方式|真实测评|轻口播',
       '生活方式|真实测评|轻口播',
       '真人出镜、室内自然光、产品近景、节奏稳定',
       '真人出镜、室内自然光、产品近景、节奏稳定',
       '需复核报价档期。',
       '需复核报价档期。',
-      'TikHub/VOC social',
+      'VOC social',
       '是'
       '是'
     ]
     ]
   ];
   ];

+ 4 - 4
claude-code-tihao-sourcing/scripts/live-preflight.js

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
 #!/usr/bin/env node
 const fs = require('fs');
 const fs = require('fs');
 const path = require('path');
 const path = require('path');
-const { readTihaoToken, readEvidenceProviderToken, readTikhubToken } = require('../mcp/src/core/credentials');
+const { readTihaoToken, readEvidenceProviderToken, readSocialAnalysisToken } = require('../mcp/src/core/credentials');
 const { resolveTihaoUserInfo } = require('../mcp/src/core/payment-links');
 const { resolveTihaoUserInfo } = require('../mcp/src/core/payment-links');
 const { redactSecrets } = require('../mcp/src/core/result-envelope');
 const { redactSecrets } = require('../mcp/src/core/result-envelope');
 
 
@@ -13,11 +13,11 @@ async function main() {
   fs.mkdirSync(outputRoot, { recursive: true });
   fs.mkdirSync(outputRoot, { recursive: true });
 
 
   const token = readTihaoToken({});
   const token = readTihaoToken({});
-  const tikhubToken = readTikhubToken({});
+  const socialAnalysisToken = readSocialAnalysisToken({});
   const evidenceToken = readEvidenceProviderToken({});
   const evidenceToken = readEvidenceProviderToken({});
   const companyFromEnv = process.env.TIHAO_COMPANY || process.env.VOC_ECOMMERCE_COMPANY || process.env.COMPANY_OBJECT_ID || '';
   const companyFromEnv = process.env.TIHAO_COMPANY || process.env.VOC_ECOMMERCE_COMPANY || process.env.COMPANY_OBJECT_ID || '';
   const baseUrl = process.env.TIHAO_ECOMMERCE_BASE_URL || 'https://server.fmode.cn/api/voc-e-commerce';
   const baseUrl = process.env.TIHAO_ECOMMERCE_BASE_URL || 'https://server.fmode.cn/api/voc-e-commerce';
-  const referenceBaseUrl = process.env.TIKHUB_BASE_URL || process.env.TIHAO_REFERENCE_ENRICHMENT_BASE_URL || '';
+  const referenceBaseUrl = process.env.SOCIAL_ANALYSIS_BASE_URL || process.env.TIHAO_REFERENCE_ENRICHMENT_BASE_URL || '';
   const homepageBaseUrl = process.env.TIHAO_HOMEPAGE_EVIDENCE_BASE_URL || process.env.HOMEPAGE_EVIDENCE_BASE_URL || '';
   const homepageBaseUrl = process.env.TIHAO_HOMEPAGE_EVIDENCE_BASE_URL || process.env.HOMEPAGE_EVIDENCE_BASE_URL || '';
   const evidenceBaseUrl = process.env.TIHAO_EVIDENCE_BASE_URL || process.env.MULTIMODAL_EVIDENCE_BASE_URL || '';
   const evidenceBaseUrl = process.env.TIHAO_EVIDENCE_BASE_URL || process.env.MULTIMODAL_EVIDENCE_BASE_URL || '';
   const videoBaseUrl = process.env.VIDEO_ANALYSIS_BASE_URL || process.env.DOUBAO_VISION_BASE_URL || '';
   const videoBaseUrl = process.env.VIDEO_ANALYSIS_BASE_URL || process.env.DOUBAO_VISION_BASE_URL || '';
@@ -30,7 +30,7 @@ async function main() {
   checks.push(check('homepage-provider-configured', Boolean(homepageBaseUrl), '未配置主页最近内容 provider;主页证据会使用 fallback/已有标签。', 'warn'));
   checks.push(check('homepage-provider-configured', Boolean(homepageBaseUrl), '未配置主页最近内容 provider;主页证据会使用 fallback/已有标签。', 'warn'));
   checks.push(check('evidence-or-video-provider-configured', Boolean(evidenceBaseUrl || videoBaseUrl), '未配置多模态 evidence 或豆包视频分析 provider;不能证明视频证据提升。', 'warn'));
   checks.push(check('evidence-or-video-provider-configured', Boolean(evidenceBaseUrl || videoBaseUrl), '未配置多模态 evidence 或豆包视频分析 provider;不能证明视频证据提升。', 'warn'));
   checks.push(check('video-model-doubao-seed-2-0-pro-260215', !videoBaseUrl || videoModel === 'doubao-seed-2-0-pro-260215', `当前视频模型为 ${videoModel},用户指定模型应为 doubao-seed-2-0-pro-260215。`));
   checks.push(check('video-model-doubao-seed-2-0-pro-260215', !videoBaseUrl || videoModel === 'doubao-seed-2-0-pro-260215', `当前视频模型为 ${videoModel},用户指定模型应为 doubao-seed-2-0-pro-260215。`));
-  checks.push(check('tikhub-token-present-if-provider', !referenceBaseUrl || Boolean(tikhubToken || token), '参考 provider 已配置,但没有 TIKHUB_TOKEN/VOC_SOCIAL_TOKEN/sessionToken。'));
+  checks.push(check('reference-token-present-if-provider', !referenceBaseUrl || Boolean(socialAnalysisToken || token), '参考账号补证 provider 已配置,但没有 VOC_SOCIAL_TOKEN/sessionToken。'));
   checks.push(check('evidence-token-present-if-provider', !evidenceBaseUrl && !videoBaseUrl || Boolean(evidenceToken), '证据/视频 provider 已配置,但没有 TIHAO_EVIDENCE_TOKEN / VIDEO_ANALYSIS_TOKEN / DOUBAO_VISION_TOKEN。'));
   checks.push(check('evidence-token-present-if-provider', !evidenceBaseUrl && !videoBaseUrl || Boolean(evidenceToken), '证据/视频 provider 已配置,但没有 TIHAO_EVIDENCE_TOKEN / VIDEO_ANALYSIS_TOKEN / DOUBAO_VISION_TOKEN。'));
 
 
   let identity = null;
   let identity = null;

+ 2 - 0
claude-code-tihao-sourcing/scripts/live-provider-normalization-smoke.js

@@ -8,6 +8,7 @@ const payload = {
         itemList: [
         itemList: [
           {
           {
             id: 'xhs-real-001',
             id: 'xhs-real-001',
+            redId: 'tiya824824',
             nickname: '真实探店号',
             nickname: '真实探店号',
             xiaohongshuUrl: 'https://www.xiaohongshu.com/user/profile/xhs-real-001',
             xiaohongshuUrl: 'https://www.xiaohongshu.com/user/profile/xhs-real-001',
             fans_count: '63,666',
             fans_count: '63,666',
@@ -50,6 +51,7 @@ const payload = {
 const creators = normalizeProviderCreators(payload, 'xiaohongshu');
 const creators = normalizeProviderCreators(payload, 'xiaohongshu');
 assert(creators.length === 2, 'should normalize provider creator list');
 assert(creators.length === 2, 'should normalize provider creator list');
 assert(creators[0].displayName === '真实探店号', 'should map display name');
 assert(creators[0].displayName === '真实探店号', 'should map display name');
+assert(creators[0].platformShortId === 'tiya824824', 'should map xiaohongshu redId as platform short id');
 assert(creators[0].fansCount === 63666, 'should parse comma fan count');
 assert(creators[0].fansCount === 63666, 'should parse comma fan count');
 assert(creators[0].imagePrice === 2300, 'should parse snake_case picture price');
 assert(creators[0].imagePrice === 2300, 'should parse snake_case picture price');
 assert(creators[0].videoPrice === 2500, 'should parse snake_case video price');
 assert(creators[0].videoPrice === 2500, 'should parse snake_case video price');

+ 3 - 3
claude-code-tihao-sourcing/scripts/local-seed-material-index.js

@@ -72,9 +72,9 @@ function buildLocalSeedMaterialIndex({ root, workspaceRoot, outputsDir }) {
       ]
       ]
     },
     },
     {
     {
-      id: 'dha-poc-tikhub-reference-workbook',
-      title: 'AI 提号 DHA TikHub 参考分析版',
-      file: path.join(workspaceRoot, 'output', 'dha-tihao-poc', 'AI提号-DHA小红书推荐名单-TikHub参考分析版.xlsx'),
+      id: 'dha-poc-social-reference-workbook',
+      title: 'AI 提号 DHA 社媒参考分析版',
+      file: path.join(workspaceRoot, 'output', 'dha-tihao-poc', 'AI提号-DHA小红书推荐名单-社媒参考分析版.xlsx'),
       role: 'reference_analysis_seed',
       role: 'reference_analysis_seed',
       usableFor: ['参考账号基准分析', '候选池补充', '视频/主页证据字段设计参考'],
       usableFor: ['参考账号基准分析', '候选池补充', '视频/主页证据字段设计参考'],
       missingForProof: [
       missingForProof: [

+ 1 - 1
claude-code-tihao-sourcing/scripts/local-seed-to-intake-worklist.js

@@ -305,7 +305,7 @@ function profileHint(xhsId) {
 
 
 function strategyFromCandidate(candidate) {
 function strategyFromCandidate(candidate) {
   const source = `${candidate.sourceWorkbook || ''} ${candidate.sourceSheet || ''}`.toLowerCase();
   const source = `${candidate.sourceWorkbook || ''} ${candidate.sourceSheet || ''}`.toLowerCase();
-  if (source.includes('tikhub')) return 'reference-account';
+  if (source.includes('参考分析')) return 'reference-account';
   if (/主推|main|strong/i.test(candidate.recommendationLevel || candidate.sourceSheet || '')) return 'result-first-broad';
   if (/主推|main|strong/i.test(candidate.recommendationLevel || candidate.sourceSheet || '')) return 'result-first-broad';
   return 'candidate-seed';
   return 'candidate-seed';
 }
 }

+ 66 - 0
claude-code-tihao-sourcing/scripts/media-library-schema-smoke.js

@@ -0,0 +1,66 @@
+#!/usr/bin/env node
+const {
+  CORE_FIELDS,
+  REQUIRED_FIELDS,
+  MANUAL_ONLY_FIELDS,
+  PGY_ALIAS,
+  INDEXES,
+  buildDdl,
+  buildQueryMap
+} = require('../mcp/src/features/tihao-sourcing/media-library-schema');
+
+function main() {
+  // 字段总数与必填数
+  assert(Object.keys(CORE_FIELDS).length >= 30, '字段总数应覆盖模板表头 35 列');
+  assert(REQUIRED_FIELDS.length === 8, `必填字段应为 8 个,实际 ${REQUIRED_FIELDS.length}`);
+
+  // 必填字段(模板「*」标记)
+  ['platform', 'content_type', 'nickname', 'fans_count', 'homepage_url', 'quote_url', 'fans_profile', 'tax_inclusive_price'].forEach(key => {
+    assert(CORE_FIELDS[key] && CORE_FIELDS[key].required === true, `${key} 应为必填字段`);
+  });
+
+  // 报价/成本字段
+  ['tax_inclusive_price', 'picture_price', 'video_price', 'cpv', 'cpe'].forEach(key => {
+    assert(CORE_FIELDS[key] && CORE_FIELDS[key].type === 'number', `${key} 应为数值字段`);
+  });
+
+  // 商单交付侧字段应含人工补录项
+  ['schedule', 'case_url', 'report_form', 'tier', 'verified'].forEach(key => {
+    assert(MANUAL_ONLY_FIELDS.includes(key), `${key} 应属于人工补录字段`);
+  });
+
+  // 与蒲公英对账字段
+  ['blogger_id', 'fans_count', 'picture_price', 'video_price', 'interaction_rate'].forEach(key => {
+    assert(PGY_ALIAS[key], `${key} 应有蒲公英对账别名`);
+  });
+
+  // 索引唯一键
+  assert(INDEXES.some(index => index.name === 'uk_blogger_id' && index.options.unique), '应有 platform+blogger_id 唯一索引');
+  assert(INDEXES.length >= 5, '应覆盖选号热路径索引');
+
+  // DDL 与查询映射生成
+  const ddl = buildDdl('media_library');
+  assert(ddl.includes('createCollection'), 'DDL 应包含建表语句');
+  assert(ddl.includes('uk_blogger_id'), 'DDL 应包含唯一索引');
+  assert(ddl.includes('tax_inclusive_price'), 'DDL 应包含必填字段说明');
+
+  const queryMap = buildQueryMap();
+  assert(queryMap.includes('公司媒体资源库字段映射'), '查询映射应有标题');
+  assert(queryMap.includes('必填字段'), '查询映射应包含必填字段节');
+  assert(queryMap.includes('蒲公英采集库字段对账'), '查询映射应包含对账节');
+
+  console.log(JSON.stringify({
+    ok: true,
+    fieldCount: Object.keys(CORE_FIELDS).length,
+    requiredCount: REQUIRED_FIELDS.length,
+    manualOnlyCount: MANUAL_ONLY_FIELDS.length,
+    pgyAliasCount: Object.keys(PGY_ALIAS).length,
+    indexCount: INDEXES.length
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 56 - 0
claude-code-tihao-sourcing/scripts/pgy-schema-smoke.js

@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+const {
+  SOURCES,
+  DIMENSIONS,
+  CORE_FIELDS,
+  INDEXES,
+  buildDdl,
+  buildQueryMap
+} = require('../mcp/src/features/tihao-sourcing/pgy-schema');
+
+function main() {
+  // schema 覆盖选号核心维度
+  const dimensions = Object.keys(DIMENSIONS);
+  ['fanRange', 'interaction', 'price', 'verticality', 'score', 'location', 'cpm', 'activity', 'fansProfile'].forEach(key => {
+    assert(dimensions.includes(key), `应包含选号维度 ${key}`);
+  });
+
+  // 报价字段必采(field_dictionary 的 ⭐ 标记)
+  ['picture_price', 'video_price', 'lower_price'].forEach(field => {
+    assert(CORE_FIELDS[field] && CORE_FIELDS[field].priority === 3, `${field} 应为必采报价字段`);
+  });
+
+  // 每个维度字段都应能在 CORE_FIELDS 里找到或属于派生
+  for (const dim of Object.values(DIMENSIONS)) {
+    assert(Array.isArray(dim.fields) && dim.fields.length > 0, `维度 ${dim.zh} 应有字段`);
+    assert(dim.query && typeof dim.query === 'object', `维度 ${dim.zh} 应有查询示例`);
+  }
+
+  // 索引唯一键
+  assert(INDEXES.some(index => index.name === 'uk_blogger_id' && index.options.unique), '应有 blogger_id 唯一索引');
+  assert(INDEXES.length >= 6, '应覆盖选号热路径索引');
+
+  // DDL 与查询映射生成
+  const ddl = buildDdl('pgy_blogger');
+  assert(ddl.includes('createCollection'), 'DDL 应包含建表语句');
+  assert(ddl.includes('uk_blogger_id'), 'DDL 应包含唯一索引');
+
+  const queryMap = buildQueryMap();
+  assert(queryMap.includes('选号查询映射'), '查询映射应有标题');
+  assert(queryMap.includes('粉丝范围'), '查询映射应包含粉丝维度');
+  assert(queryMap.includes('estimate_video_cpm'), '查询映射应包含 CPM 字段');
+
+  console.log(JSON.stringify({
+    ok: true,
+    dimensionCount: dimensions.length,
+    coreFieldCount: Object.keys(CORE_FIELDS).length,
+    sourceCount: Object.keys(SOURCES).length,
+    indexCount: INDEXES.length
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 49 - 0
claude-code-tihao-sourcing/scripts/platform-quality-rules-smoke.js

@@ -0,0 +1,49 @@
+#!/usr/bin/env node
+const {
+  getPlatformRules,
+  classifyTier,
+  evaluateCreatorDataQuality
+} = require('../mcp/src/features/tihao-sourcing/platform-quality-rules');
+
+function main() {
+  // 规则库应覆盖技能包支持的平台
+  assert(getPlatformRules('xiaohongshu').tiers.length >= 4, '小红书应有完整达人分级');
+  assert(getPlatformRules('douyin').tiers.length >= 5, '抖音应有完整达人分级');
+  assert(Object.keys(getPlatformRules('xiaohongshu').metrics).length >= 5, '小红书应结构化互动率/赞藏比/评分/垂直度/商业占比阈值');
+
+  // 达人分级
+  assert(classifyTier('xiaohongshu', 180000).name === '腰部KOL', '小红书 18w 应为腰部KOL');
+  assert(classifyTier('xiaohongshu', 3000).name === '素人', '小红书 3000 应为素人');
+  assert(classifyTier('douyin', 3000000).name === '腰部KOL', '抖音 300w 应为腰部KOL');
+  assert(classifyTier('douyin', 20000000).name === '超头部KOL', '抖音 2000w 应为超头部KOL');
+
+  // 成功样本:小红书 18w 粉,粉丝量达标,缺失指标不误扣
+  const xhs = evaluateCreatorDataQuality({ platform: 'xiaohongshu', fansCount: 180000 });
+  assert(xhs.tier === '腰部KOL', '成功样本层级正确');
+  assert(xhs.score >= 50, '粉丝量达标但缺互动率等指标时不应误扣到不及格');
+  assert(xhs.missingMetrics.includes('赞藏比(收藏/点赞)'), '未采集的赞藏比应标待补证');
+
+  // 翻车样本:抖音 500w 粉但互动率虚胖 + 负增长 + 低活跃
+  const dy = evaluateCreatorDataQuality({ platform: 'douyin', fansCount: 5000000, interactionRate: 0.8, fanGrowthRate30d: -5, lowActivity: true });
+  assert(dy.tier === '头部KOL', '翻车样本粉丝层级仍按粉丝量分类');
+  assert(dy.riskCount >= 3, '翻车样本应命中互动率/负增长/低活跃多个危险信号');
+  assert(dy.score < 30, '多个危险信号应显著压低数据质量分');
+
+  // 阈值方向:商业占比是 max 方向(越高越危险)
+  const commercial = evaluateCreatorDataQuality({ platform: 'douyin', fansCount: 500000, commercialRatio: 60 });
+  const commercialCheck = commercial.checks.find(item => item.label === '商业内容占比');
+  assert(commercialCheck && commercialCheck.verdict === 'risk', '商业占比 60% 应判定为危险');
+
+  console.log(JSON.stringify({
+    ok: true,
+    xhsScore: xhs.score,
+    douyinRiskCount: dy.riskCount,
+    douyinScore: dy.score
+  }, null, 2));
+}
+
+function assert(condition, message) {
+  if (!condition) throw new Error(message);
+}
+
+if (require.main === module) main();

+ 9 - 2
claude-code-tihao-sourcing/scripts/product-acceptance-audit.js

@@ -29,7 +29,7 @@ async function main() {
   assertNoSecretLeak(runbookText, 'live provider runbook');
   assertNoSecretLeak(runbookText, 'live provider runbook');
   assertIncludes(roadmapText, 'docs/live-provider-integration-runbook.md', 'reference roadmap runbook link');
   assertIncludes(roadmapText, 'docs/live-provider-integration-runbook.md', 'reference roadmap runbook link');
   assertIncludes(runbookText, '不能宣称', 'live provider wording guard');
   assertIncludes(runbookText, '不能宣称', 'live provider wording guard');
-  assertIncludes(runbookText, '没有跑通 `acceptance:providers`,不能说 TikHub 已接通', 'live provider TikHub guard');
+  assertIncludes(runbookText, '没有跑通 `acceptance:providers`,不能说参考补证 provider 已接通', 'live provider reference guard');
   assertIncludes(readmeText, 'docs/live-provider-integration-runbook.md', 'README runbook link');
   assertIncludes(readmeText, 'docs/live-provider-integration-runbook.md', 'README runbook link');
   scanPackageFiles(root);
   scanPackageFiles(root);
 
 
@@ -193,6 +193,13 @@ function assertNoSecretLeak(text, label) {
   }
   }
 }
 }
 
 
+// 字段 schema 是「未来建库」的结构化元数据(字段定义/DDL/查询映射),不含实际报价数值,
+// 不应被「临时定价泄露」扫描误伤(上方 assertNoSecretLeak 仍会覆盖它们做 token 泄露检查)。
+const PRICING_SCAN_SKIP = [
+  'docs/pgy-field-schema.json',
+  'docs/media-library-field-schema.json'
+];
+
 function scanPackageFiles(root) {
 function scanPackageFiles(root) {
   const files = [];
   const files = [];
   walk(root, files);
   walk(root, files);
@@ -201,7 +208,7 @@ function scanPackageFiles(root) {
     if (shouldSkipScan(rel)) continue;
     if (shouldSkipScan(rel)) continue;
     const text = fs.readFileSync(file, 'utf8');
     const text = fs.readFileSync(file, 'utf8');
     assertNoSecretLeak(text, rel);
     assertNoSecretLeak(text, rel);
-    if (rel.startsWith('docs/') || rel.endsWith('.md')) assertNoPricingLeak(text, rel);
+    if ((rel.startsWith('docs/') || rel.endsWith('.md')) && !PRICING_SCAN_SKIP.includes(rel)) assertNoPricingLeak(text, rel);
   }
   }
 }
 }
 
 

+ 5 - 5
claude-code-tihao-sourcing/scripts/provider-contract-audit.js

@@ -10,10 +10,10 @@ async function main() {
   const useMock = args.has('--mock');
   const useMock = args.has('--mock');
   const mock = useMock ? await startMockProvider() : null;
   const mock = useMock ? await startMockProvider() : null;
   try {
   try {
-    const referenceBaseUrl = mock?.url || process.env.TIKHUB_BASE_URL || process.env.TIHAO_REFERENCE_ENRICHMENT_BASE_URL || '';
+    const referenceBaseUrl = mock?.url || process.env.SOCIAL_ANALYSIS_BASE_URL || process.env.TIHAO_REFERENCE_ENRICHMENT_BASE_URL || '';
     const evidenceBaseUrl = mock?.url || process.env.TIHAO_EVIDENCE_BASE_URL || process.env.MULTIMODAL_EVIDENCE_BASE_URL || '';
     const evidenceBaseUrl = mock?.url || process.env.TIHAO_EVIDENCE_BASE_URL || process.env.MULTIMODAL_EVIDENCE_BASE_URL || '';
     if (!referenceBaseUrl && !evidenceBaseUrl) {
     if (!referenceBaseUrl && !evidenceBaseUrl) {
-      console.log('provider contract audit skipped: set TIKHUB_BASE_URL/TIHAO_EVIDENCE_BASE_URL or run with --mock');
+      console.log('provider contract audit skipped: set SOCIAL_ANALYSIS_BASE_URL/TIHAO_EVIDENCE_BASE_URL or run with --mock');
       return;
       return;
     }
     }
 
 
@@ -29,7 +29,7 @@ async function main() {
       ].join('\n'),
       ].join('\n'),
       referenceEnrichmentBaseUrl: referenceBaseUrl || undefined,
       referenceEnrichmentBaseUrl: referenceBaseUrl || undefined,
       multimodalEvidenceBaseUrl: evidenceBaseUrl || undefined,
       multimodalEvidenceBaseUrl: evidenceBaseUrl || undefined,
-      tikhubToken: process.env.TIKHUB_TOKEN || process.env.TIKHUB_API_KEY || undefined,
+      socialAnalysisToken: process.env.VOC_SOCIAL_TOKEN || process.env.TIHAO_SESSION_TOKEN || undefined,
       evidenceProviderToken: mock ? 'mock-evidence-token' : (process.env.TIHAO_EVIDENCE_TOKEN || process.env.MULTIMODAL_EVIDENCE_TOKEN || undefined),
       evidenceProviderToken: mock ? 'mock-evidence-token' : (process.env.TIHAO_EVIDENCE_TOKEN || process.env.MULTIMODAL_EVIDENCE_TOKEN || undefined),
       output: out
       output: out
     };
     };
@@ -56,7 +56,7 @@ async function main() {
 
 
     const serialized = JSON.stringify(result);
     const serialized = JSON.stringify(result);
     assert(!/Authorization:\s*Bearer/i.test(serialized), 'provider contract result should not leak Authorization headers');
     assert(!/Authorization:\s*Bearer/i.test(serialized), 'provider contract result should not leak Authorization headers');
-    assert(!/(TIKHUB_TOKEN|TIHAO_SESSION_TOKEN|VOC_ECOMMERCE_TOKEN)\s*=/.test(serialized), 'provider contract result should not leak env tokens');
+    assert(!/(VOC_SOCIAL_TOKEN|TIHAO_SESSION_TOKEN|VOC_ECOMMERCE_TOKEN)\s*=/.test(serialized), 'provider contract result should not leak env tokens');
     console.log(`provider contract audit ok: ${out}`);
     console.log(`provider contract audit ok: ${out}`);
   } finally {
   } finally {
     if (mock) await mock.close();
     if (mock) await mock.close();
@@ -390,7 +390,7 @@ function assertMockProviderContract(mock) {
   assert(Array.isArray(evidence.body.requestedCapabilities) && evidence.body.requestedCapabilities.includes('asr_signals'), 'evidence request should include requested capabilities');
   assert(Array.isArray(evidence.body.requestedCapabilities) && evidence.body.requestedCapabilities.includes('asr_signals'), 'evidence request should include requested capabilities');
   assert(evidence.body.criteria && Array.isArray(evidence.body.criteria.referenceBaselines), 'evidence request should include reference baselines');
   assert(evidence.body.criteria && Array.isArray(evidence.body.criteria.referenceBaselines), 'evidence request should include reference baselines');
   assert(evidence.body.criteria && Array.isArray(evidence.body.criteria.referenceResources), 'evidence request should include reference resources');
   assert(evidence.body.criteria && Array.isArray(evidence.body.criteria.referenceResources), 'evidence request should include reference resources');
-  assert(JSON.stringify(evidence.body.criteria.referenceResources).includes('reference-video.mp4'), 'evidence request should include TikHub video URL resource');
+  assert(JSON.stringify(evidence.body.criteria.referenceResources).includes('reference-video.mp4'), 'evidence request should include reference video URL resource');
   assert(Array.isArray(evidence.body.creators) && evidence.body.creators.length >= 1, 'evidence request should include creators');
   assert(Array.isArray(evidence.body.creators) && evidence.body.creators.length >= 1, 'evidence request should include creators');
   assert(evidence.body.creators[0].platformUserId || evidence.body.creators[0].displayName, 'evidence creator payload should include identity');
   assert(evidence.body.creators[0].platformUserId || evidence.body.creators[0].displayName, 'evidence creator payload should include identity');
 }
 }

+ 4 - 2
claude-code-tihao-sourcing/scripts/smoke-package.js

@@ -83,7 +83,8 @@ async function main() {
   const noToken = await runTihaoSourcing({
   const noToken = await runTihaoSourcing({
     collectionMode: 'live',
     collectionMode: 'live',
     briefText,
     briefText,
-    output: path.join(out, 'live-no-token')
+    output: path.join(out, 'live-no-token'),
+    allowEnvToken: false
   });
   });
   assert(noToken.status === 'needs_token', 'live without token should return needs_token');
   assert(noToken.status === 'needs_token', 'live without token should return needs_token');
   assert(Array.isArray(noToken.errors) && noToken.errors.length === 0, 'friendly token state should not return errors');
   assert(Array.isArray(noToken.errors) && noToken.errors.length === 0, 'friendly token state should not return errors');
@@ -91,7 +92,8 @@ async function main() {
 
 
   const defaultLiveNoToken = await runTihaoSourcing({
   const defaultLiveNoToken = await runTihaoSourcing({
     briefText,
     briefText,
-    output: path.join(out, 'default-live-no-token')
+    output: path.join(out, 'default-live-no-token'),
+    allowEnvToken: false
   });
   });
   assert(defaultLiveNoToken.status === 'needs_token', 'omitted collectionMode should enter live and request token instead of sample');
   assert(defaultLiveNoToken.status === 'needs_token', 'omitted collectionMode should enter live and request token instead of sample');
   assert(Array.isArray(defaultLiveNoToken.errors) && defaultLiveNoToken.errors.length === 0, 'default live token state should not return errors');
   assert(Array.isArray(defaultLiveNoToken.errors) && defaultLiveNoToken.errors.length === 0, 'default live token state should not return errors');

+ 3 - 3
claude-code-tihao-sourcing/skill-package-manifest.json

@@ -1,6 +1,6 @@
-{
+{
   "name": "claude-code-tihao",
   "name": "claude-code-tihao",
-  "version": "1.0.10",
+  "version": "1.0.19",
   "description": "Claude Code native Tihao AI creator sourcing package for client brief to blogger recommendation list workflows.",
   "description": "Claude Code native Tihao AI creator sourcing package for client brief to blogger recommendation list workflows.",
   "plugin": "tihao",
   "plugin": "tihao",
   "skills": [
   "skills": [
@@ -23,7 +23,7 @@
     "Read or accept a client brief and turn it into structured sourcing criteria.",
     "Read or accept a client brief and turn it into structured sourcing criteria.",
     "Generate a business-ready blogger recommendation list with strong/backup/review tiers.",
     "Generate a business-ready blogger recommendation list with strong/backup/review tiers.",
     "Use sample mode for demos without credentials.",
     "Use sample mode for demos without credentials.",
-    "Use live mode with the company VOC e-commerce proxy for JustOne-backed creator discovery.",
+    "Use live mode with the company VOC e-commerce proxy for real creator discovery.",
     "Analyze screenshots, product images, UI captures, and image text through Fmode Doubao vision.",
     "Analyze screenshots, product images, UI captures, and image text through Fmode Doubao vision.",
     "Return friendly token/recharge links instead of raw API errors.",
     "Return friendly token/recharge links instead of raw API errors.",
     "Save client and business preference memory for later sourcing rounds."
     "Save client and business preference memory for later sourcing rounds."

+ 97 - 8
claude-code-tihao-sourcing/skills/tihao/SKILL.md

@@ -1,6 +1,6 @@
 ---
 ---
 name: tihao
 name: tihao
-description: Upload/read a client brief and produce a business-ready blogger/creator recommendation list for Tihao AI. Use when the user asks for 提号、找博主、选号、达人名单、商务可用名单、根据 brief 推荐账号,or to run the Tihao SOP with sample/live JustOne-backed data.
+description: Upload/read a client brief and produce a business-ready blogger/creator recommendation list for Tihao AI. Use when the user asks for 提号、找博主、选号、达人名单、商务可用名单、根据 brief 推荐账号,or to run the Tihao SOP with sample/live e-commerce creator data.
 ---
 ---
 
 
 # Tihao Creator Sourcing
 # Tihao Creator Sourcing
@@ -40,7 +40,7 @@ Keep user input natural. The user can say:
 Do not force the user to know `collectionMode`, `sessionToken`, `keywordLimit`, `company`, or output paths. Infer safe defaults:
 Do not force the user to know `collectionMode`, `sessionToken`, `keywordLimit`, `company`, or output paths. Infer safe defaults:
 
 
 - Normal brief-to-list requests, including “读取 brief 出博主名单 / 提号 / 找博主 / 选号 / 商务可用名单 / 高质量名单 / 可直接发客户的名单”: treat as the 高质量首版 — `collectionMode=live`, `resultFirstMode=true`, and enable reference enrichment when reference links/accounts exist. Only narrow to a minimal `keywordLimit=1` / `pagesPerKeyword=1` probe when the user explicitly asks for a 小规模/省额度 run.
 - Normal brief-to-list requests, including “读取 brief 出博主名单 / 提号 / 找博主 / 选号 / 商务可用名单 / 高质量名单 / 可直接发客户的名单”: treat as the 高质量首版 — `collectionMode=live`, `resultFirstMode=true`, and enable reference enrichment when reference links/accounts exist. Only narrow to a minimal `keywordLimit=1` / `pagesPerKeyword=1` probe when the user explicitly asks for a 小规模/省额度 run.
-- Phrases like “用 live 小规模检索出博主名单”, “真实跑”, “真实数据”, “调用接口”, “JustOne-backed”, or “小规模检索” must be treated as live mode.
+- Phrases like “用 live 小规模检索出博主名单”, “真实跑”, “真实数据”, “调用接口”, “真实接口”, or “小规模检索” must be treated as live mode.
 - Use `collectionMode=sample` only when the user explicitly asks for sample, 演示, 无消耗, 不用真实数据, or “先跑通 SOP”.
 - Use `collectionMode=sample` only when the user explicitly asks for sample, 演示, 无消耗, 不用真实数据, or “先跑通 SOP”.
 - If live credentials are missing, return the friendly token/open/recharge state; do not silently fall back to sample unless the user explicitly asks for sample or sets `allowSampleFallback=true`.
 - If live credentials are missing, return the friendly token/open/recharge state; do not silently fall back to sample unless the user explicitly asks for sample or sets `allowSampleFallback=true`.
 - If a brief file path is given, pass it as `brief`.
 - If a brief file path is given, pass it as `brief`.
@@ -65,6 +65,10 @@ Do not force the user to know `collectionMode`, `sessionToken`, `keywordLimit`,
      - Auth: `Authorization: Bearer <Parse sessionToken>`
      - Auth: `Authorization: Bearer <Parse sessionToken>`
      - Xiaohongshu video detail path: `xiaohongshu/app_v2/get_video_note_detail`
      - Xiaohongshu video detail path: `xiaohongshu/app_v2/get_video_note_detail`
      - Input can be `note_id` or a share link; short links may be resolved first.
      - Input can be `note_id` or a share link; short links may be resolved first.
+   - 抖音参考链接解析走接口清单里的 social 线(同一个 `tihao_api_call`):
+     - 参考视频分享链接(含 v.douyin.com 短链)→ `douyin.fetch_one_video_by_share_url` 一步拿到视频完整数据和作者 `sec_user_id`。
+     - 参考账号主页链接 → `douyin.get_sec_user_id` 解析出 `sec_user_id`,再用 `douyin.user_profile` / `douyin.user_posts` 补主页资料与近 10-20 条作品证据,`douyin.video_comments` 查互动真实性。
+     - 要接回星图详情线(报价/CP/商单)时,用 `douyin.xingtu.get_xingtu_kolid_by_sec_user_id`(或客户只给抖音号时用 `get_xingtu_kolid_by_unique_id`)把账号换成星图 kolId,再当 `oAuthorId` 调星图详情接口。
    - Pass returned `videoUrl`, `coverUrl`, `title`, `authorInfo`, subtitle URLs, and frame URLs into Doubao video analysis.
    - Pass returned `videoUrl`, `coverUrl`, `title`, `authorInfo`, subtitle URLs, and frame URLs into Doubao video analysis.
    - If these resources are not available, keep `待补口播证据` / `待补帧图证据` instead of claiming ASR or frame analysis.
    - If these resources are not available, keep `待补口播证据` / `待补帧图证据` instead of claiming ASR or frame analysis.
 5. If sample mode (only when explicitly requested), run the safe built-in sample data. Otherwise the first round is live + `resultFirstMode` per the First Report Standard.
 5. If sample mode (only when explicitly requested), run the safe built-in sample data. Otherwise the first round is live + `resultFirstMode` per the First Report Standard.
@@ -76,7 +80,7 @@ Do not force the user to know `collectionMode`, `sessionToken`, `keywordLimit`,
    - Douyin creator path: `douyin-xingtu/gw/api/gsearch/search_for_author_square/v1`
    - Douyin creator path: `douyin-xingtu/gw/api/gsearch/search_for_author_square/v1`
 7. Prefer platform-specific sourcing paths:
 7. Prefer platform-specific sourcing paths:
    - Xiaohongshu/Pugongying: use reference-account similarity and related-account expansion when available, then re-check type and tone.
    - Xiaohongshu/Pugongying: use reference-account similarity and related-account expansion when available, then re-check type and tone.
-   - Douyin/Xingtu: treat Xingtu labels as a candidate entry point, not proof of fit; if recall is weak, use platform keyword search or recruitment-style replenishment guidance.
+   - Douyin/Xingtu: treat Xingtu labels as a candidate entry point, not proof of fit; if recall is weak, expand with the douyin social line (`douyin.search_user` / `douyin.search_video` keyword search in the Douyin APP ecosystem), then bridge promising accounts back to Xingtu via kolId conversion for quote/performance data, or fall back to recruitment-style replenishment guidance.
 8. Score candidates by fan fit, price fit, content/style fit, reference-style signals, recent homepage evidence, region, cooperation readiness, implicit-rule fit, and risk/exclusion hits.
 8. Score candidates by fan fit, price fit, content/style fit, reference-style signals, recent homepage evidence, region, cooperation readiness, implicit-rule fit, and risk/exclusion hits.
 9. For strong recommendations, require evidence beyond raw platform tags:
 9. For strong recommendations, require evidence beyond raw platform tags:
    - At least two brief hit conditions.
    - At least two brief hit conditions.
@@ -100,6 +104,75 @@ Prefer MCP tools when available:
 - `tihao_brief_sourcing_run`: main brief-to-blogger-list workflow.
 - `tihao_brief_sourcing_run`: main brief-to-blogger-list workflow.
 - `tihao_token_check`: check live token availability without echoing secrets.
 - `tihao_token_check`: check live token availability without echoing secrets.
 - `tihao_preference_update`: save user feedback as sourcing memory.
 - `tihao_preference_update`: save user feedback as sourcing memory.
+- `tihao_api_search`: 发现/检索已登记的电商数据接口清单(抖音/小红书 搜索 + 详情),返回 id、proxyPath、必填参数、标签。需要 CPM/CPE、商单数、阅读/互动/播放中位数等媒体详情/绩效数据时,先用它找到对应详情接口。
+- `tihao_api_doc`: 按 id 或 proxyPath 读取某个接口的完整参数说明与调用模板,调详情接口前用它确认怎么入参。
+- `tihao_api_call`: 实际调用某个接口(搜索或详情)。用于第二波详情补全:把搜索拿到的达人 id 传给详情接口换取绩效数据;也用于小红书素人/非蒲公英接口(channel=social)。计费优先走 NewAPI(fmode-api)sk- token,鉴权失败自动回退平台 sessionToken。失败时返回可执行状态(needs_token / needs_valid_token / needs_recharge / needs_permission / needs_input / upstream_unstable),不暴露底层鉴权/供应商细节。
+
+## 媒体详情数据(接口清单 / 第二波补全)
+
+搜索/列表接口本身不返回绩效数据(CPM/CPE、商单数、阅读/互动/播放中位数、抖音短 id 等)。这些字段要靠「第二波」详情接口按需补全。第二波不写死在主流程里,而是登记在接口清单中,由你(Claude Code)自由编排:
+
+- 接口清单索引:`mcp/catalog/voc-e-commerce-endpoints.json`
+- 每平台参数说明:`mcp/catalog/params/douyin.json`、`mcp/catalog/params/xiaohongshu.json`
+
+两波工作流:
+
+1. 第一波(搜索):调搜索接口拿到达人列表与基础数据,每个达人带一个 id。
+   - 抖音搜索返回 `star_id`。
+   - 小红书搜索返回 `userId`(及短 id `redId`)。
+2. 第二波(详情,按需):对需要绩效数据的达人,用上一步的 id 调详情接口。
+   - 抖音详情接口的达人参数名是 `oAuthorId`,取值 = 搜索结果的 `star_id`。
+   - 小红书详情接口的达人参数名是 `userId`,取值 = 搜索结果的 `userId`。
+
+典型用法:先 `tihao_api_search`(按 `platform` + 关键词如 `cpm`/`商单`/`中位数` 检索)→ 用 `tihao_api_doc` 看清入参 → 用 `tihao_api_call` 传达人 id 取回 CPM/CPE、商单数、中位数等,再合并进名单。所有接口共用同一个 `voc-e-commerce` 代理与同一个平台 `sessionToken`,对外只称「电商数据服务/星图·蒲公英」,不暴露任何底层供应商名。
+
+```json
+{ "id": "douyin.author_cp_info", "params": { "oAuthorId": "<搜索返回的 star_id>" } }
+```
+
+```json
+{ "id": "xiaohongshu.kol_cost_effective", "params": { "userId": "<搜索返回的 userId>" } }
+```
+
+### 小红书素人 / 非蒲公英(channel=social)
+
+蒲公英(PGY)只覆盖「商单博主」(CPM/CPE/商单等绩效,走电商网关 channel=ecommerce)。要搜**素人/非蒲公英**的小红书笔记、用户、评论、话题,用 channel=social 的接口——它们走公司 `voc-social` 全量代理网关(上游全量代理,不存在「电商网关 404」问题),同一套 `tihao_api_search` / `tihao_api_doc` / `tihao_api_call` 流程,`tihao_api_call` 会按 endpoint 的 `channel` 自动选 voc-social 网关。
+
+- 搜笔记:`xiaohongshu.search_notes`(`xiaohongshu/app/search_notes`,必填 keyword、page)。
+- 搜用户:`xiaohongshu.web_v3.fetch_search_users`(必填 keyword)。
+- 笔记详情/评论:`xiaohongshu.note_detail`(note_id)、`xiaohongshu.note_comments`(note_id)。
+- 用户资料/发布笔记:`xiaohongshu.user_info`(user_id)、`xiaohongshu.user_notes`(user_id)。
+- 话题笔记:`xiaohongshu.topic_notes`(page_id、first_load_time)。
+
+```json
+{ "id": "xiaohongshu.search_notes", "params": { "keyword": "敏感肌", "page": 1 } }
+```
+
+```json
+{ "id": "xiaohongshu.user_notes", "params": { "user_id": "<笔记/搜索返回的 user_id>" } }
+```
+
+素人线以 `app` 接口为主(已验证);若需要 web 端字段可用 `web_v3.*` 备用接口,个别 web_v3 接口需按文档补齐 `xsec_token` 等参数。清单里没有的素人接口,可直接用 `tihao_api_call` 传 `rawPath` + `channel: "social"` 调用。
+
+### 抖音素人 / 开放端(channel=social)
+
+星图只覆盖「星图入驻达人」(报价/CP/商单,走电商网关 channel=ecommerce)。要在抖音 APP 生态里搜**素人/非星图**账号与视频、解析参考账号/参考视频链接,用 channel=social 的抖音接口——同走公司 `voc-social` 全量代理网关,同一套 `tihao_api_search` / `tihao_api_doc` / `tihao_api_call` 流程:
+
+- 搜账号:`douyin.search_user`(必填 keyword,返回 `sec_user_id` 与抖音号 `unique_id`)。
+- 搜视频:`douyin.search_video`(必填 keyword);意图不明时用 `douyin.search_general` 综合搜索。
+- 参考链接解析:`douyin.get_sec_user_id`(主页链接→`sec_user_id`)、`douyin.fetch_one_video_by_share_url`(视频分享链接→视频数据+作者)。
+- 主页/近作/评论:`douyin.user_profile`、`douyin.user_posts`、`douyin.video_detail`、`douyin.video_comments`(`sec_user_id` / `aweme_id`)。
+- 桥接星图详情线:`douyin.xingtu.get_xingtu_kolid_by_sec_user_id`、`douyin.xingtu.get_xingtu_kolid_by_unique_id`(账号→星图 kolId→当 `oAuthorId` 查报价/CP/商单)。
+
+```json
+{ "id": "douyin.search_user", "params": { "keyword": "港风穿搭" } }
+```
+
+```json
+{ "id": "douyin.fetch_one_video_by_share_url", "params": { "share_url": "https://v.douyin.com/xxxxxxx/" } }
+```
+
+注意:抖音搜索接口是 POST body 传参(工具会按清单自动组装);清单里没有的抖音素人接口,同样可用 `tihao_api_call` 传 `rawPath` + `channel: "social"` 调用。
 
 
 ## Typical Tool Calls
 ## Typical Tool Calls
 
 
@@ -262,7 +335,17 @@ Preference memory:
 
 
 ## Token And Recharge Handling
 ## Token And Recharge Handling
 
 
-Live mode may use any of these token sources:
+计费已迁移到 NewAPI(fmode-api)。调用任何代理接口时**优先用 NewAPI 的 sk- token** 发 `Authorization: Bearer <sk- token>`;当 sk- token 鉴权失败(401,例如服务端尚未迁移 / 该号未入仓 / token 未被接受)时,**自动回退**用平台 sessionToken 重试一次。两者都没有才返回 needs_token。
+
+NewAPI(fmode-api)sk- token 来源(优先):
+
+- request field `newapiToken`(或 `fmodeApiKey`)
+- `NEWAPI_TOKEN` / `FMODE_API_KEY`(含当前工作区 `.env.local`)
+- `~/.claude/voc-credentials.json` / `~/.claude/tihao-credentials.json` 里的 `fmodeApiKey`
+- `~/.fmode/config.json` 的 `newapiToken` / `fmodeApiKey`
+- **`~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`**(Claude Code 用户默认就配在这里:`sk-` 开头、且 `ANTHROPIC_BASE_URL` 指向 `api.fmode.cn` 时自动采纳;装完技能通常无需再手动配置)
+
+平台 sessionToken 来源(回退 + 充值页 userId 解析):
 
 
 - request field `tihaoToken`
 - request field `tihaoToken`
 - request field `sessionToken`
 - request field `sessionToken`
@@ -280,13 +363,18 @@ Provider token sources may include `evidenceProviderToken`, `multimodalEvidenceT
 
 
 社媒分析服务(参考账号补证)可用 `socialAnalysisToken`、`vocSocialToken` 或 `VOC_SOCIAL_TOKEN`,默认也复用平台 `sessionToken`;这些都是 Parse sessionToken,绝不能回显。对外只称「社媒分析服务」。
 社媒分析服务(参考账号补证)可用 `socialAnalysisToken`、`vocSocialToken` 或 `VOC_SOCIAL_TOKEN`,默认也复用平台 `sessionToken`;这些都是 Parse sessionToken,绝不能回显。对外只称「社媒分析服务」。
 
 
-Friendly states:
+Friendly states(按 HTTP 状态码区分):
 
 
-- No token: return `needs_token`, show the open/recharge link, keep `errors=[]`.
-- 403 or quota issue: return `needs_recharge`, show the 「社媒分析服务充值」 open/recharge link, keep `errors=[]`. 对外只说「社媒分析服务」,不暴露底层供应商名。
-- 401 or invalid token: return `needs_valid_token`, ask for a valid sessionToken, keep `errors=[]`.
+- No token: return `needs_token`。**先自救**:读取 `~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`(`sk-` 开头)并用 `FMODE_API_KEY=sk-…` / 入参 `newapiToken` 重试;仍读不到才回退 `r:` 平台 sessionToken,充值只作最后兜底(仅真 402)。keep `errors=[]`.
+- 401(token 缺失/失效/无效,且回退 sessionToken 也未通过): return `needs_valid_token`, ask for a valid NewAPI sk- token 或平台 sessionToken, keep `errors=[]`.
+- 402(NewAPI / fmode-api 余额不足): return `needs_recharge`, 展示 fmode-api 余额充值链接 `https://app.fmode.cn/dev/studio/?balance=fmodeapi`(携带 `?balance=fmodeapi` 会自动打开余额充值弹窗),keep `errors=[]`.
+- 403(账号被禁用 / 无该接口权限,区别于余额不足): return `needs_permission`, 提示联系服务方确认账号状态或权限,不引导充值, keep `errors=[]`.
 - Normal run: return `ok`.
 - Normal run: return `ok`.
 
 
+> ⚠️ 不要把 **403/401** 一律说成「余额不足」。只有 **402** 才是真的没钱要充值;403 是权限、401 是 token 问题。完整对照表、真实报文样例与处理纪律见 **`references/error-codes.md`**。
+
+社媒分析服务(参考账号补证)那条线仍沿用其原有的「社媒分析服务充值」提示。
+
 ## Output Standard
 ## Output Standard
 
 
 The chat answer should include:
 The chat answer should include:
@@ -350,6 +438,7 @@ Do not promote a personal preference into a team-wide hard rule unless repeated
 
 
 ## References
 ## References
 
 
+- `references/error-codes.md` — 错误码速查(401/402/403 对照、真实报文、处理纪律;区分 403≠余额不足)
 - `references/user-workflow.md`
 - `references/user-workflow.md`
 - `references/live-mode.md`
 - `references/live-mode.md`
 - `references/output-format.md`
 - `references/output-format.md`

+ 67 - 0
claude-code-tihao-sourcing/skills/tihao/references/error-codes.md

@@ -0,0 +1,67 @@
+# 提号(tihao)数据采集 · 错误码速查
+
+> 给 Agent 按需查阅。**核心纪律:不是所有报错都是「余额不足」。** 只有 **HTTP 402** 才提示充值;
+> **401 = token 问题**(缺失/失效/类型不对),**403 = 账号禁用/无权限**,都**不是没钱**,不要引导充值。
+
+适用范围:`tihao_api_call`(按 `channel` 走 voc-e-commerce 蒲公英网关 / voc-social 素人网关)、
+以及达人直采(`live-provider.js`)。两条网关共用同一套状态码判定与话术。
+
+## 一、总览表(HTTP 状态 → 含义 → 工具返回 → 给客户的话术 → Agent 动作)
+
+| HTTP | 后端 `mess` 样例 | 判定 kind | 工具 status | 给客户 Agent 的话术 | Agent 该做什么 |
+|------|------------------|-----------|-------------|----------------------|----------------|
+| 200 | —(`{"code":200,"data":{...}}`) | — | `ok` | 正常返回 | 直接用 `data.result` |
+| 401 | `Invalid NewAPI token` / `Missing NewAPI token` / `请输入API_KEY或用户sessionToken` / `当前用户不存在,无使用权限` | `auth` | `needs_valid_token`(无任何 token 时 `needs_token`) | 「token 缺失/失效/类型不对,**不是没钱、也不是关键词/类目问题**」 | 配置有效的 NewAPI `sk-` token(或回退 `r:` sessionToken)后重试 |
+| 402 | `余额不足` / `额度不足` | `billing` | `needs_recharge` | 「余额不足,请充值」 | 给充值链接 `https://app.fmode.cn/dev/studio/?balance=fmodeapi`(自动打开余额弹窗) |
+| 403 | `用户禁用` / `无权限` / `permission`(**不含**余额/开通关键词) | `permission` | `needs_permission` | 「账号被禁用或没有该接口权限,**≠ 余额不足**」 | 联系服务方确认账号状态/接口权限,**不要**引导充值 |
+| 403 | 含 `未开通` / `没开通` / `余额` / `额度` / `insufficient` / `balance` | `billing` | `needs_recharge` | 「需开通/充值后使用」 | 给充值/开通链接 |
+| 400 / 422 | `参数` / `入参` / `keyword` / `bad request` | `request` | `needs_input` | 「参数有误,**不是没数据/不是类目不支持**」 | 用 `tihao_api_doc` 核对参数后重试 |
+| 5xx / 连接失败 | `fetch failed` / 超时 / `500` | `upstream` | `upstream_unstable` | 「上游接口波动」 | 稍后重试,不要误报成关键词/余额问题 |
+
+## 二、真实报文样例(实打抓取)
+
+```text
+# 有效 sk- token(NewAPI / fmode-api)
+HTTP 200  {"code":200,"data":{"code":0,"data":{"kols":[{"name":"徐卷卷","redId":"daisyue",...}]}}}
+
+# sk- token 无效/写错
+HTTP 401  {"code":401,"mess":"Invalid NewAPI token"}
+
+# 发的是旧版 Parse sessionToken(r:),后端已迁 NewAPI 计费
+HTTP 401  {"code":401,"mess":"Missing NewAPI token"}
+
+# 完全没带 Authorization
+HTTP 401  {"code":401,"mess":"请输入API_KEY或用户sessionToken"}
+
+# token 对应的用户不存在
+HTTP 401  {"code":401,"mess":"当前用户不存在,无使用权限"}
+
+# 余额不足(NewAPI 计费额度用尽)—— 仅此情形才引导充值
+HTTP 402  {"code":402,"mess":"余额不足"}
+
+# 用户被禁用 / 无该接口权限(区别于余额不足)
+HTTP 403  {"code":403,"mess":"用户禁用或无权限"}
+```
+
+## 三、Token 优先级与回退
+
+计费已迁移到 **NewAPI(fmode-api)**,请求头统一 `Authorization: Bearer ${token}`:
+
+1. **优先**用 NewAPI 的 `sk-` token(取值:入参 `newapiToken/fmodeApiKey` → 环境变量 `FMODE_API_KEY`/`NEWAPI_TOKEN` → `~/.claude/voc-credentials.json` 的 `fmodeApiKey`)。
+2. 若 `sk-` token **鉴权失败(kind=auth / 401)** 且存在平台 `r:` sessionToken,**自动回退** sessionToken 重试一次(覆盖「服务端未迁完 / 该号未入仓 / token 未被接受」的过渡期)。
+3. **402(余额不足)/ 403(无权限)不回退**——它们是终态,按上表处理。
+
+## 四、判定规则摘要(`classifyApiError`)
+
+判定顺序(命中即返回):
+
+1. 文案含「用户/账号信息不存在、未登录、登录失效、请输入…token、sessiontoken」→ `auth`
+2. `status===401` 或文案含「invalid token / 无效token / 未授权 / unauthorized」→ `auth`
+3. `status===402` 或文案含「余额不足 / 额度不足 / 未开通 / 开通…权限 / insufficient / balance / quota / payment / 充值」→ `billing`
+4. `status===403` **且**含余额/开通类关键词 → `billing`
+5. 文案含「参数 / 入参 / keyword / 关键词 / bad request」或 `status∈{400,422}` → `request`
+6. `status>=500` → `upstream`
+7. `status===403` 或文案含「permission / 权限 / 无权限」→ `permission`
+8. 其它 → `upstream`
+
+> 注意第 4 与第 7 条的区别:**带「余额/开通」关键词的 403 才当充值**;**纯权限 403 一律 `permission`,不充值**。