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/
 .mcp.json
 _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",
   "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": {
     "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`,扩大参考感知召回、批量分析候选并按证据重排,直接产出商务可发客户的强名单。
 - 普通“读取 brief 出博主名单/提号/找博主/选号/可直接发客户的名单”都按上面的 live 高质量首版处理;只有用户明确要小规模/省额度时才缩到 `keywordLimit=1`。
-- `live` 模式调用公司 `voc-e-commerce` 代理接口,使用 JustOne-backed 数据;缺 token 时返回友好的开通/充值提示并主动要 sessionToken,而不是退回 sample。
+- `live` 模式调用公司 `voc-e-commerce` 代理接口,使用真实电商数据;缺 token 时返回友好的开通/充值提示并主动要 sessionToken,而不是退回 sample。
 - `sample` 模式仅在用户明确要求演示、无消耗、不用真实数据或“先跑通 SOP”时使用,无 token 跑通 brief -> 博主名单流程。
 - 输出 Markdown、JSON、CSV 三份结果。
 - 无 token、401、403、额度不足时返回友好的开通/充值提示,不暴露原始错误或 token。
 - 支持偏好记忆,把客户反馈沉淀到下次选号。
 - 支持 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 篇内容证据、平台相似推荐和客户选中率验收。
 - 支持 overnight 策略矩阵:`baseline-live`、`reference-account`、`homepage-evidence`、`video-enhanced`、`result-first`、`result-first-risk`、`result-first-broad`。
 - 支持人工复核指标统计:商务可用率、负样本率、负样本归因覆盖率、客户选中率、重复键和每个 brief 内排名连续性。

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

@@ -48,13 +48,13 @@ npm run acceptance:pm
 npm run acceptance:providers:mock
 ```
 
-它用于验证 TikHub 兼容参考账号补证合同、多模态证据卡合同、豆包视频分析 OpenAI 兼容合同。
+它用于验证参考账号补证合同、多模态证据卡合同、豆包视频分析 OpenAI 兼容合同。
 
 有真实 provider 权限时再执行:
 
 ```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_TOKEN="<optional evidence provider token>"
 npm run acceptance:providers
@@ -164,7 +164,7 @@ production live ready:
 
 multimodal live ready:
 
-- `npm run acceptance:providers` 能连真实 TikHub/证据 provider;
+- `npm run acceptance:providers` 能连真实参考账号补证/证据 provider;
 - 至少一张真实 ffmpeg/ASR/Vision 证据卡经过人工复核。
 
 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
 
-本文档用于拿到真实 TikHub、VOC social、视频分析或多模态 evidence provider 权限后的联调验收。它不替代默认发布门禁;默认发布门禁仍然是:
+本文档用于拿到真实参考账号补证、VOC social、视频分析或多模态 evidence provider 权限后的联调验收。它不替代默认发布门禁;默认发布门禁仍然是:
 
 ```powershell
 npm run acceptance
@@ -8,7 +8,7 @@ npm run acceptance
 
 真实 provider gate 只有在具备安全测试账号、余额和样本时才运行。
 
-发布红线:不能宣称未验证的 provider 已完成真实接入。没有跑通 `acceptance:providers`,不能说 TikHub 已接通;没有跑通 `acceptance:video-ab`,不能说参考视频分析已经证明能提升提号结果。
+发布红线:不能宣称未验证的 provider 已完成真实接入。没有跑通 `acceptance:providers`,不能说参考补证 provider 已接通;没有跑通 `acceptance:video-ab`,不能说参考视频分析已经证明能提升提号结果。
 
 ## 1. 联调目标
 
@@ -26,7 +26,7 @@ npm run acceptance
 
 - 一份安全测试 Brief,推荐使用 DHA、敏感肌、清洁、零食等已有 fixture;
 - 至少 1 条可访问的参考博主链接,优先选择带视频资源的链接;
-- TikHub 兼容参考补证 provider URL;
+- 参考账号补证 provider URL;
 - 多模态 evidence provider URL,或 OpenAI 兼容豆包视频分析 URL;
 - 如 provider 需要鉴权,准备运行时 token;
 - 如要跑 live 候选检索,准备 Tihao `sessionToken` 和 `Company objectId`。
@@ -38,8 +38,8 @@ npm run acceptance
 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_TOKEN="<optional-evidence-provider-token>"
 $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
 ```
 
-对外只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名(如 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`;
 - 客户反馈中的“像某某账号”“不要某类账号”可以沉淀为下一轮提号偏好。
 
-## 2. TikHub / VOC social 参考账号补证
+## 2. VOC social 参考账号补证
 
 目标:把 Brief 中的参考链接升级为参考账号基线,而不是只看链接文本。
 
@@ -64,7 +64,7 @@ Provider 输入建议:
 {
   "referenceEnrichmentBaseUrl": "https://<provider>",
   "referenceEnrichmentPath": "reference-baselines",
-  "tikhubToken": "<runtime optional token>",
+  "socialAnalysisToken": "<runtime optional token>",
   "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。
 - 新增 `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。
   - `manual-review-and-customer-effect`:`manualReviewDraftRows=12`,逐字稿覆盖客户选中率目标和人工复核逻辑;仍缺真实人工复核标签、客户选中/拒绝、反馈原因、本轮人工补号量,并且必须绑定通过的 history audit。
 - 审计报告把候选名单、逐字稿经验、视频资源 readiness 和旧 failure artifacts 的边界写清楚:它们是补证入口和失败证据,不是业务效果证明。
@@ -224,7 +224,7 @@ Validation:
 - 强推荐必须同时通过 Brief 和参考风格门槛;超预算、硬排除或高风险账号会被降级或剔除。
 - Markdown 和 CSV 输出面向商务验收增强,包含 `Brief 解析摘要`、`参考视频风格指纹`、`候选召回记录`、`多模态证据卡`、`商务可用名单`、`剔除/降级原因`、`下一轮校准问题`。
 - 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 验收。
 - 证据卡现在会影响候选参考风格命中点、分数和重排,不再只是报告里的被动展示。
 - 增加防跑偏的参考感知召回:允许生成 `DHA 专业科普` 这类和 Brief 绑定的召回词;禁止婚礼、宴会、布景、满月宴、仪式感等跑偏场景词污染召回。
@@ -269,7 +269,7 @@ Validation:
 已知边界:
 
 - 首次组合 live 运行出现过一次短暂 `live_network_error`,重试后通过。后续 runbook 保留 retry/backoff,并建议对单关键词失败做软失败处理。
-- 未经过真实 provider gate 的能力只能称为合同验证或 mock 验证,不能宣称真实 TikHub、ffmpeg、讯飞 ASR 或豆包视觉已完成上线。
+- 未经过真实 provider gate 的能力只能称为合同验证或 mock 验证,不能宣称真实参考补证、ffmpeg、讯飞 ASR 或豆包视觉已完成上线。
 - 没有真实历史客户选择数据前,不能宣称客户选中率达到 30%/50%,只能使用 `review:metrics` 统计已标注样本。
 - 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 请求带上品牌、品类、平台、关键词和参考信号。
 - 多模态 evidence provider 请求带上文本、ASR、视觉、帧图证据和风险复核能力。
 - 支持通过运行时 token 为 evidence provider 鉴权。
@@ -427,7 +427,7 @@ Validation:
 - CLI 反馈和剔除项报告:支持保存商务反馈,报告中说明剔除/降级原因。
 - 提号规则与记忆循环加固:更明确地区分首轮假设、硬排除规则和偏好记忆。
 - 文档和 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` 写入客户工作区。
 - 首个 npm 发布版本曾使用 `@gangvy/tihao-sop`,后续迁移到 `@vocmarket/tihao-sop`。
 

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

@@ -1,15 +1,15 @@
 {
   "table": "skills",
-  "updatedAt": "2026-06-17",
+  "updatedAt": "2026-08-14",
   "records": [
     {
       "skillName": "tihao",
       "displayName": "提号",
       "status": "published",
       "npmPackage": "@vocmarket/tihao",
-      "npmVersion": "1.0.10",
+      "npmVersion": "1.0.19",
       "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",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "workspaceSkillPath": ".claude/skills/tihao/SKILL.md",
@@ -49,7 +49,7 @@
       "displayName": "Fmode 图片分析",
       "status": "local",
       "npmPackage": "@vocmarket/tihao",
-      "npmVersion": "1.0.10",
+      "npmVersion": "1.0.19",
       "installCommand": "npx --yes @vocmarket/tihao@latest workspace --smoke",
       "globalInstallCommand": "npm install -g @vocmarket/tihao && tihao install --smoke",
       "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`。
 - 新增 `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`。
@@ -316,7 +316,7 @@
 - 当前优化没有偏离提号需求:优化重心仍是把真实历史 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`。
 - 当前未到位的是业务效果证明本身:`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。
 
 ### 本轮修正
@@ -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`。
 - 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` 之间,因此每轮证据台账会读到最新种子材料索引。
@@ -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`。
 - 可以说“本地种子材料已纳入可复验索引并进入证据台账”;不能说提号率已提升、客户效果已达标或人工补号量已下降。
 
@@ -1660,7 +1660,7 @@ npm run overnight:quality
 - `runs=2`
 - `failures=0`
 - `gatePass=true`
-- 参考补证状态显示为 `needs_tikhub_enrichment`,不会误报真实补证完成。
+- 参考补证状态显示为 `needs_social_analysis_enrichment`,不会误报真实补证完成。
 
 完整验收:
 
@@ -1691,7 +1691,7 @@ npm run acceptance
 
 - `TIHAO_GATE_REQUIRE_REFERENCE_PROVIDER=true`
   - `reference-account` 策略的参考补证状态必须为 `ok`。
-  - 没有真实参考补证 provider、VOC social 视频详情或 TikHub 补证时会失败。
+  - 没有真实参考补证 provider 或 VOC social 视频详情时会失败。
 - `TIHAO_GATE_REQUIRE_HOMEPAGE_PROVIDER=true`
   - `homepage-evidence` 策略的主页证据状态必须为 `ok`。
   - 只使用 fallback/标签轻量判断时会失败。
@@ -1730,7 +1730,7 @@ npm run overnight:quality
 - 输出目录:`outputs/overnight-quality-1780734793371`
 - `gatePass=false`
 - 失败门禁:`reference-provider-ok`
-- 当前值:`needs_tikhub_enrichment`
+- 当前值:`needs_social_analysis_enrichment`
 - 目标:`ok`
 
 严格主页 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、帧图或正文后,才能声明视频分析完成。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - 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、帧图或正文后,才能声明视频分析完成。
 - 没有真实资源时,只能写“待补视频证据”或“待补帧图证据”。
 - 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.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 三缺口真实资料核对:`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 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 行。
@@ -59,7 +59,7 @@
 | 3 | 最新表单索引 | `outputs/latest-form-index-latest/latest-form-index.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。 |
-| 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 的草稿行。 |
 | 8 | 经验逐字稿索引 | `outputs/experience-transcript-index-latest/experience-transcript-index.md` | 查看提号经验逐字稿覆盖的 8 条经验规则;只作为经验来源追踪和规则校准种子。 |
 | 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;
 }
 
+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 = {}) {
+  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 env = { ...process.env, ...fileEnv };
   return (
-    input.tihaoToken ||
-    input.sessionToken ||
-    input.vocToken ||
     env.TIHAO_SESSION_TOKEN ||
     env.VOC_ECOMMERCE_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(对外只叫「社媒分析服务」)。
-// 优先用平台 sessionToken / vocSocialToken;tikhub* 只作为历史兼容项保留,不对外暴露。
+// 优先用平台 sessionToken / vocSocialToken。
 function readSocialAnalysisToken(input = {}) {
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const env = { ...process.env, ...fileEnv };
@@ -56,23 +158,13 @@ function readSocialAnalysisToken(input = {}) {
     input.sessionToken ||
     input.tihaoToken ||
     input.vocToken ||
-    input.tikhubToken ||
-    input.tikHubToken ||
     env.VOC_SOCIAL_TOKEN ||
     env.TIHAO_SESSION_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 = {}) {
   const fileEnv = readEnvFile(input.cwd || process.cwd());
   const env = { ...process.env, ...fileEnv };
@@ -101,8 +193,11 @@ function readEvidenceProviderToken(input = {}) {
 
 module.exports = {
   readTihaoToken,
+  readNewApiToken,
   readSocialAnalysisToken,
-  readTikhubToken,
   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;
 }
 
+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) {
   const sharedStrings = parseSharedStrings(readZipEntry(buffer, 'xl/sharedStrings.xml'));
   const entries = listZipEntries(buffer)
@@ -178,6 +245,7 @@ function extractDocxText(buffer) {
 module.exports = {
   ensureDir,
   readTextMaybe,
+  readXlsxRows,
   writeJson,
   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 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 充值服务。
 // 对客户只暴露「社媒分析服务」这一名称,不暴露任何底层供应商名。
 const VOC_SOCIAL_APIG_ID = 'Vo3ROWEvDy';
@@ -107,13 +110,23 @@ function buildSocialRechargeRequiredMessage(paymentUrl) {
   ].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 [
-    '还没有检测到提号电商数据服务 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。'
   ].join('\n');
@@ -129,6 +142,30 @@ function buildRechargeRequiredMessage(paymentUrl) {
   ].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 = {
   TIHAO_ECOMMERCE_APIG_ID,
   TIHAO_ECOMMERCE_APIG_PATH,
@@ -139,6 +176,9 @@ module.exports = {
   resolveTihaoUserInfo,
   buildMissingTokenMessage,
   buildRechargeRequiredMessage,
+  buildFmodeApiRechargeUrl,
+  buildFmodeApiRechargeMessage,
+  buildPermissionDeniedMessage,
   buildSocialRechargeInfo,
   buildSocialRechargeUrl,
   buildSocialMissingTokenMessage,

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

@@ -1,5 +1,6 @@
 const path = require('path');
 const { readTextMaybe } = require('../../core/files');
+const { extractPatternHits } = require('./brief-rule-patterns');
 
 function parseBrief(input = {}) {
   const productContextText = normalizeTextInput(input.productIntro || input.productDescription || input.productContext || input.product);
@@ -65,6 +66,7 @@ function parseBrief(input = {}) {
     blockedCreators,
     exclusions,
     performanceRequirements: resolvePerformanceRequirements(text),
+    briefRulePatterns: extractPatternHits(text),
     deliveryRequirements: ['账号名称', '平台', '粉丝数', '报价', '推荐理由', '风险提示', '分级']
   };
 }
@@ -77,6 +79,12 @@ function resolvePerformanceRequirements(text) {
   if (cp1) requirements.push(`CP1≤${cp1[1]}`);
   const cpm = text.match(/CPM\s*[≤<=]\s*(\d+(?:\.\d+)?)/i);
   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})*)/);
   if (readMedian) requirements.push(`阅读中位数≥${readMedian[1]}`);
   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]}`);
   const audience = text.match(/(\d{2}-\d{2}岁占比\d+%以上)/);
   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);
 }
 

+ 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 = {}) {
   const categoryRules = buildCategoryRules(criteria);
   const requirementLayers = buildRequirementLayers(criteria, categoryRules);
   const referenceUsability = buildReferenceUsability(criteria);
   const sourcingStrategy = buildPlatformSourcingStrategy(criteria);
+  const goalStrategy = buildGoalStrategy(criteria);
+  const compliance = buildComplianceCheck(criteria);
   return {
     ...criteria,
     categoryRules,
     requirementLayers,
     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 {
   buildTihaoRechargeInfo,
   buildMissingTokenMessage,
-  buildRechargeRequiredMessage,
+  buildFmodeApiRechargeUrl,
+  buildFmodeApiRechargeMessage,
+  buildPermissionDeniedMessage,
   resolveTihaoUserInfo
 } = require('../../core/payment-links');
 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';
 
 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) {
     return friendlyState('needs_token', buildMissingTokenMessage(recharge.paymentUrl), {
       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 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 platforms = criteria.platforms.filter(platform => platform === 'xiaohongshu' || platform === 'douyin');
   const recallKeywords = buildRecallKeywords(criteria, input);
@@ -46,12 +52,7 @@ async function searchLiveCreators(criteria, input = {}) {
         if (companyId) url.searchParams.set('company', String(companyId));
         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) {
           return friendlyState(
@@ -75,9 +76,17 @@ async function searchLiveCreators(criteria, input = {}) {
             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) {
-          return friendlyState('needs_recharge', buildRechargeRequiredMessage(recharge.paymentUrl), {
-            data: { recharge }
+          // 用户被禁用/无权限,区别于余额不足。
+          return friendlyState('needs_permission', buildPermissionDeniedMessage(), {
+            data: {}
           });
         }
         if (!response.ok) {
@@ -248,6 +257,23 @@ async function fetchWithRetry(url, options) {
   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) {
   if (platform === 'douyin') return 'douyin-xingtu/gw/api/gsearch/search_for_author_square/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);
   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 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, [
     'picturePrice', 'picture_price', 'imagePrice', 'image_price', 'notePrice', 'note_price',
     'graphicPrice', 'graphicsPrice', 'graphics_price', 'imageTextPrice', 'image_text_price',
@@ -316,6 +343,7 @@ function normalizeCreator(item, platform, index) {
   return {
     platform,
     platformUserId: userId,
+    platformShortId: shortId,
     displayName: String(item.name || item.nickname || item.nickName || attr.nick_name || attr.nickname || attr.name || ''),
     profileUrl: resolveProfileUrl(item, attr, platform, userId, secUid),
     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 {
   constructor({ sessionToken, taskId, active }) {
-    this.sessionToken = sessionToken;
+    this.sessionToken = String(sessionToken || '');
     this.taskId = taskId;
     this.active = active;
     this.title = '';
@@ -125,7 +125,7 @@ class ProgressReporter {
       await callFunction({
         id: PROGRESS_FN_ID,
         action: 'report',
-        sessionToken: this.sessionToken,
+        sessionToken: String(this.sessionToken || ''),
         taskId: this.taskId,
         ...payload,
       });
@@ -182,7 +182,7 @@ class ProgressReporter {
     const tokenResp = await callFunction({
       id: UPLOAD_FN_ID,
       action: 'createUploadToken',
-      sessionToken: this.sessionToken,
+      sessionToken: String(this.sessionToken || ''),
       filename,
       mimeType,
       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 { evaluateImplicitRuleFit } = require('./experience-rules');
+const { evaluateCreatorDataQuality } = require('./platform-quality-rules');
 
 function rankCreators(creators, criteria) {
   const seen = new Set();
@@ -26,7 +27,8 @@ function scoreCreator(creator, criteria) {
   const hardExclusionHits = [...new Set([...riskHits, ...blockedCreatorHits])];
   const briefFit = scoreBriefFit(creator, criteria, fanScore, priceScore);
   const referenceFit = scoreReferenceFit(creator, criteria);
-  const dataQualityScore = scoreDataQuality(creator);
+  const dataQuality = scoreDataQuality(creator);
+  const dataQualityScore = dataQuality.score;
   const homepageEvidence = creator.homepageEvidence || {};
   const recentContentFitScore = creator.recentContentFitScore || homepageEvidence.recentContentFitScore || 60;
   const visualQualityScore = creator.visualQualityScore || homepageEvidence.visualQualityScore || 0;
@@ -85,6 +87,8 @@ function scoreCreator(creator, criteria) {
     briefFitScore: briefFit.score,
     referenceStyleFitScore: referenceFit.score,
     dataQualityScore,
+    dataQuality: dataQuality.result,
+    tierLabel: dataQuality.result.tierLabel,
     recentContentFitScore,
     visualQualityScore,
     toneConsistencyScore,
@@ -188,9 +192,10 @@ function fallbackReferencePoints(creator, criteria) {
 }
 
 function scoreDataQuality(creator) {
-  let score = 45;
+  const result = evaluateCreatorDataQuality(creator);
+  let score = result.score;
+  // 字段完整度加分(衡量数据完整度,与平台质量阈值互补;粉丝量已进入 platformResult,不重复加)
   if (creator.profileUrl) score += 10;
-  if (creator.fansCount) score += 10;
   if (creator.minPrice || creator.imagePrice || creator.videoPrice) score += 10;
   if ((creator.contentTags || []).length) score += 10;
   if ((creator.personaTags || []).length) score += 10;
@@ -200,7 +205,7 @@ function scoreDataQuality(creator) {
   const lastPostDaysAgo = Number(creator.lastPostDaysAgo ?? creator.daysSinceLastPost ?? 0);
   if (lastPostDaysAgo > 30) score -= 20;
   if (Number(creator.repeatedCommenterCount || creator.repeatedCommenterHits || 0) > 0) score -= 18;
-  return clamp(score);
+  return { score: clamp(score), result };
 }
 
 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');
 
 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 token = readSocialAnalysisToken(input);
   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',
   '主页链接',
   '粉丝数',
   '图文报价',
@@ -98,6 +99,11 @@ function buildSourcingReport({ criteria, candidates, collectionMode, outputDir,
     '',
     ...formatRequirementLayers(criteria),
     '',
+    ...formatGoalStrategy(criteria),
+    ...formatBriefRulePatterns(criteria),
+    ...formatFewshotReference(criteria),
+    ...formatCompliance(criteria),
+    '',
     '## 参考账号可参考性判断',
     '',
     ...formatReferenceUsability(criteria),
@@ -234,6 +240,7 @@ function softwareRows(criteria, rows) {
       平台: labelPlatform(item.platform),
       账号名称: item.displayName || item.name || '',
       账号ID: item.platformUserId || item.userId || '',
+      平台短ID: item.platformShortId || item.redId || item.uniqueId || '',
       主页链接: item.profileUrl || item.homepageUrl || '',
       粉丝数: formatNumber(item.fansCount),
       图文报价: formatNumber(item.imagePrice || item.picturePrice),
@@ -407,6 +414,111 @@ function formatRequirementLayers(criteria) {
   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) {
   const usability = criteria.referenceUsability || {};
   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 { attachEvidenceCards, loadMultimodalEvidence } = require('./multimodal-evidence');
 const { enrichCriteriaWithExperience } = require('./experience-rules');
+const { buildFewshotReference } = require('./few-shot-cases');
 const { enrichHomepageEvidence } = require('./homepage-evidence-provider');
 const { rankCreators } = require('./ranker');
 const { buildSourcingReport } = require('./report');
@@ -47,6 +48,7 @@ async function runTihaoSourcingInner(input, collectionMode, outputDir, reporter)
     referenceResources: enrichment.referenceResources || []
   });
   criteria = enrichCriteriaWithExperience(criteria);
+  criteria = { ...criteria, fewshotReference: buildFewshotReference(criteria, effectiveInput) };
 
   let creators = SAMPLE_CREATORS;
   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 { updatePreferenceMemory } = require('./tools/tihao-preference-update');
 const { analyzeFmodeImage } = require('./tools/fmode-image-analysis');
+const { searchTihaoApis, getTihaoApiDoc, callTihaoApi } = require('./tools/tihao-api-catalog-run');
 const { readTihaoToken } = require('./core/credentials');
 const { buildTihaoRechargeInfo, buildMissingTokenMessage } = require('./core/payment-links');
 
@@ -144,9 +145,6 @@ function createServer() {
         referencePostsLimit: z.number().int().min(1).max(50).optional(),
         socialAnalysisBaseUrl: z.string().optional(),
         socialAnalysisToken: z.string().optional(),
-        tikhubBaseUrl: z.string().optional(),
-        tikhubToken: z.string().optional(),
-        tikHubToken: z.string().optional(),
         vocSocialBaseUrl: z.string().optional(),
         vocSocialToken: z.string().optional(),
         enableVocSocialReferenceEnrichment: z.boolean().optional(),
@@ -277,6 +275,107 @@ function createServer() {
     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;
 }
 

+ 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",
-  "version": "1.0.11",
+  "version": "1.0.19",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@vocmarket/tihao",
-      "version": "1.0.11",
+      "version": "1.0.19",
       "license": "ISC",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.29.0",

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

@@ -1,6 +1,6 @@
 {
   "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.",
   "type": "commonjs",
   "bin": {
@@ -55,6 +55,17 @@
     "history:from-csv": "node scripts/history-dataset-from-csv.js",
     "history:from-csv:smoke": "node scripts/history-dataset-from-csv-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:smoke": "node scripts/create-data-intake-pack-smoke.js",
     "intake:readiness": "node scripts/intake-readiness-audit.js",
@@ -147,7 +158,7 @@
     "blogger-list",
     "brief",
     "mcp",
-    "justone"
+    "voc-e-commerce"
   ],
   "dependencies": {
     "@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 publishTimeCount = posts.filter(post => value(post.publishTime || post.publishedAt || post.createdAt || post.time).length > 0).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) ||
     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
 const fs = require('fs');
 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 { redactSecrets } = require('../mcp/src/core/result-envelope');
 
@@ -13,11 +13,11 @@ async function main() {
   fs.mkdirSync(outputRoot, { recursive: true });
 
   const token = readTihaoToken({});
-  const tikhubToken = readTikhubToken({});
+  const socialAnalysisToken = readSocialAnalysisToken({});
   const evidenceToken = readEvidenceProviderToken({});
   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 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 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 || '';
@@ -30,7 +30,7 @@ async function main() {
   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('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。'));
 
   let identity = null;

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

@@ -8,6 +8,7 @@ const payload = {
         itemList: [
           {
             id: 'xhs-real-001',
+            redId: 'tiya824824',
             nickname: '真实探店号',
             xiaohongshuUrl: 'https://www.xiaohongshu.com/user/profile/xhs-real-001',
             fans_count: '63,666',
@@ -50,6 +51,7 @@ const payload = {
 const creators = normalizeProviderCreators(payload, 'xiaohongshu');
 assert(creators.length === 2, 'should normalize provider creator list');
 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].imagePrice === 2300, 'should parse snake_case picture 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',
       usableFor: ['参考账号基准分析', '候选池补充', '视频/主页证据字段设计参考'],
       missingForProof: [

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

@@ -305,7 +305,7 @@ function profileHint(xhsId) {
 
 function strategyFromCandidate(candidate) {
   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';
   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');
   assertIncludes(roadmapText, 'docs/live-provider-integration-runbook.md', 'reference roadmap runbook link');
   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');
   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) {
   const files = [];
   walk(root, files);
@@ -201,7 +208,7 @@ function scanPackageFiles(root) {
     if (shouldSkipScan(rel)) continue;
     const text = fs.readFileSync(file, 'utf8');
     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 mock = useMock ? await startMockProvider() : null;
   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 || '';
     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;
     }
 
@@ -29,7 +29,7 @@ async function main() {
       ].join('\n'),
       referenceEnrichmentBaseUrl: referenceBaseUrl || 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),
       output: out
     };
@@ -56,7 +56,7 @@ async function main() {
 
     const serialized = JSON.stringify(result);
     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}`);
   } finally {
     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(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(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(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({
     collectionMode: 'live',
     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(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({
     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(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",
-  "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.",
   "plugin": "tihao",
   "skills": [
@@ -23,7 +23,7 @@
     "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.",
     "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.",
     "Return friendly token/recharge links instead of raw API errors.",
     "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
-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
@@ -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:
 
 - 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”.
 - 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`.
@@ -65,6 +65,10 @@ Do not force the user to know `collectionMode`, `sessionToken`, `keywordLimit`,
      - Auth: `Authorization: Bearer <Parse sessionToken>`
      - 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.
+   - 抖音参考链接解析走接口清单里的 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.
    - 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.
@@ -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`
 7. Prefer platform-specific sourcing paths:
    - 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.
 9. For strong recommendations, require evidence beyond raw platform tags:
    - 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_token_check`: check live token availability without echoing secrets.
 - `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
 
@@ -262,7 +335,17 @@ Preference memory:
 
 ## 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 `sessionToken`
@@ -280,13 +363,18 @@ Provider token sources may include `evidenceProviderToken`, `multimodalEvidenceT
 
 社媒分析服务(参考账号补证)可用 `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`.
 
+> ⚠️ 不要把 **403/401** 一律说成「余额不足」。只有 **402** 才是真的没钱要充值;403 是权限、401 是 token 问题。完整对照表、真实报文样例与处理纪律见 **`references/error-codes.md`**。
+
+社媒分析服务(参考账号补证)那条线仍沿用其原有的「社媒分析服务充值」提示。
+
 ## Output Standard
 
 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/error-codes.md` — 错误码速查(401/402/403 对照、真实报文、处理纪律;区分 403≠余额不足)
 - `references/user-workflow.md`
 - `references/live-mode.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`,不充值**。