---
name: moras-A2A 种草分发与归因网关
overview: 在 moras-A2A 仓库构建一个独立的「极度种草卡片 + 归因闭环」HTTP 服务，REST API 作为唯一事实源，外加 Skills 包 / A2A JSON-RPC / OpenAPI manifest / 薄 MCP 包装四种渠道适配，把 Moras App 体系内已生产的爆款视频塞进 OpenClaw、Hermes、Gemini、ChatGPT 等 Agent 平台供用户免费消费。归因走自己的短链与表，moras-api 完全不改动。
todos:
  - id: p1-skeleton
    content: "Phase 1: 仓库骨架 + Express 入口 + a2a_sessions / pcd_cards / attribution_clicks / attribution_orders 表"
    status: pending
  - id: p1-pcd-schema
    content: "Phase 1: 定义 PCD (Product Card Document) 规范：基础信息+核心优势+种草故事+多 KOC 视频+追踪购买链接"
    status: pending
  - id: p1-pcd-builder
    content: "Phase 1: PCD Builder：直读 ai_autocut（沿用 harness.js 模式）+ Gemini 抽取卖点 + 排序"
    status: pending
  - id: p1-rest-api
    content: "Phase 1: REST API + OpenAPI 3.0 spec：/v1/recommend /v1/cards/:id /v1/creators/:u/showcase"
    status: pending
  - id: p1-redirect
    content: "Phase 1: r.moras.ai 短链：/r/:recId → 写 attribution_clicks → 302 到 TikTok Shop 联盟链接"
    status: pending
  - id: p2-skill-pack
    content: "Phase 2: 发布 moras-shop SKILL.md 包，覆盖 OpenClaw/Claude Code/Cursor/Codex"
    status: pending
  - id: p2-a2a
    content: "Phase 2: A2A JSON-RPC + Agent Card，artifact 用 PCD parts，验证 Hermes / CrewAI"
    status: pending
  - id: p2-openapi-manifest
    content: "Phase 2: 发布 Gemini Extension OpenAPI manifest 直指 REST API"
    status: pending
  - id: p2-render-adapters
    content: "Phase 2: 渲染适配层：把 PCD 转成 markdown / chatgpt-apps 组件 / a2a artifact / gemini 富响应"
    status: pending
  - id: p3-mcp-thin
    content: "Phase 3: 薄 MCP 包装（仅为 ChatGPT Apps SDK 等强制 MCP 的客户端），内部转调 REST"
    status: pending
  - id: p3-attribution
    content: "Phase 3: 归因摄入：TikTok Shop 联盟 API webhook + Daily fuzzy-match cron，写 attribution_orders"
    status: pending
  - id: p3-roi-report
    content: "Phase 3: ROI 报表 API（仅供 supply side 消费）：channel × creator × product 维度的曝光/点击/CVR/GMV"
    status: pending
  - id: p4-prod
    content: "Phase 4: 限频/反作弊、Region 隔离开关、Prometheus、PM2 上线"
    status: pending
---

# moras-A2A 种草分发与归因网关

## 一、商业拓扑

```mermaid
flowchart LR
    subgraph supply [Supply Side · Moras App · 收费]
      Creator[达人]
      Brand[商家]
      Pipeline["12-Agent 流水线<br/>(moras-api · 不改)"]
      Creator -- "制作种草视频" --> Pipeline
      Brand -- "投流/选品/付费" --> Pipeline
    end

    Pipeline --> AssetPool[(爆款视频资产池<br/>ai_autocut MySQL)]

    subgraph gateway [moras-A2A · 独立进程]
      RestAPI[REST API<br/>事实源]
      PCD[PCD Builder]
      Redirect[r.moras.ai 短链]
      Attr[归因表]
      AssetPool --> PCD
      PCD --> RestAPI
      RestAPI --> Redirect
      Redirect --> Attr
    end

    subgraph adapters [渠道适配层 · 薄]
      Skills[moras-shop SKILL.md 包]
      A2A[A2A JSON-RPC]
      OpenAPI[OpenAPI Manifest]
      MCP[薄 MCP 包装]
    end

    Skills --> RestAPI
    A2A --> RestAPI
    OpenAPI --> RestAPI
    MCP --> RestAPI

    subgraph demand [Demand Side · 用户免费]
      OpenClaw -- "Skills" --> Skills
      ClaudeCode[Claude Code/Cursor/Codex] -- "Skills" --> Skills
      Hermes -- "A2A" --> A2A
      CrewAI[CrewAI/AutoGen] -- "A2A" --> A2A
      Gemini -- "Extension" --> OpenAPI
      ChatGPT -- "Apps SDK 强制 MCP" --> MCP
    end

    User[终端用户] -- "点购买" --> Redirect
    Redirect --> TikTokShop[TikTok Shop]
    TikTokShop -- "联盟回传" --> Attr

    BI[moras-bi ETL] -- "JOIN attribution_*" --> Attr
```

**三句话定性**：
1. moras-A2A 是**独立进程**，对内只读 MySQL（与 harness.js 同库），对外 REST 为本，不调用也不修改 moras-api。
2. **REST API 是唯一事实源**，所有渠道适配器只是把 REST 响应翻译成各平台原生格式；这条是为什么放弃"MCP-first"的关键。
3. supply side 付费的诱因 = ChatGPT/Gemini/OpenClaw 这些大流量入口免费分发**自己的视频**，且每笔成交可归因结算。

## 二、为什么各渠道走不同协议（决策依据）

| 渠道 | 适配方式 | 为什么 |
|---|---|---|
| **OpenClaw** | Skills 包 | OpenClaw 是 skill-aware Agent。SKILL.md 教 Agent 怎么 curl 我们 REST API，零安装、零进程、零 MCP server 维护成本。改 prompt = 改 markdown，无需发版。 |
| **Claude Code / Cursor / Codex** | 同一个 Skills 包 | Anthropic 系 Agent 都吃 SKILL.md 格式，一份多用。 |
| **Hermes / CrewAI / AutoGen / LangGraph** | A2A JSON-RPC | 这些 Agent 框架原生 A2A 协议，必须提供。Agent Card + 4 个 skill 暴露在 `/.well-known/agent-card.json`。 |
| **Gemini Extensions** | OpenAPI 3.0 manifest | Gemini Extension 直接吃 OpenAPI spec，自动渲染卡片，**完全不需要我们写代码**——只需把 REST API 的 OpenAPI 文件放到 manifest URL。 |
| **ChatGPT Apps SDK** | 薄 MCP 包装 | 这是唯一**强制要求 MCP** 的客户端。我们写一层最薄的 MCP server，内部所有 tool 都直接转调 REST API，不持有业务逻辑。Phase 3 才做。 |

**核心原则**：业务逻辑只活在 REST API 一处，所有渠道适配器都是 < 200 行的胶水代码。

## 三、Product Card Document (PCD) — 极度种草的原子单位

```js
{
  rec_id: "rec_2026042001a3f7",
  product: {
    id: 1729384756,
    title: "Eco-friendly silicone teether",
    brand: "GentleBaby",
    price_usd: 12.99,
    original_price_usd: 19.99,
    discount_label: "限时 35% OFF",
    main_image: "https://oss/...",
    gallery: ["...", "..."],
  },
  hero_pitch: {
    one_liner: "0-3 岁宝宝长牙必备，FDA 食品级硅胶，妈妈党回购率 78%",
    why_it_wins_on_tiktok: "近 30 天 #babyteether 标签下转化率 Top 3，平均观看 8.2s",
    key_benefits: [
      { icon: "shield", title: "100% 食品级硅胶", desc: "通过 FDA & EU EN-71 双认证" },
      { icon: "tooth", title: "牙龈舒缓", desc: "凹凸纹理设计帮助宝宝长牙不哭闹" },
      { icon: "wash", title: "可水煮消毒", desc: "100°C 5 分钟杀菌" }
    ],
    target_audience: { persona: "新手妈妈 0-2 岁宝宝", age: "25-35", region: ["US","UK","CA"] }
  },
  selection_story: {
    headline: "为什么 Moras 给 1,200 位达人推荐了这款",
    bullets: [
      "近 7 天 Moras 池内同品类 GMV 排名第 2",
      "佣金率 28%，行业均值 15%",
      "竞争密度低（仅 12 位达人在挂车）",
      "已有 47 条爆款视频可复用模板"
    ],
    moras_score: 9.2
  },
  videos: [
    {
      task_id: "video_1764237820203_r4p625luv",
      creator: { username: "mom_lifestyle_us", avatar: "...", followers: 84000 },
      thumbnail: "https://oss/...jpg",
      video_url: "https://oss/...mp4",
      duration_s: 15,
      gmv_usd: 12450.32,
      orders: 612,
      views: 482000,
      conversion_score: 0.83,
      tiktok_video_url: "https://www.tiktok.com/@mom_lifestyle_us/video/..."
    }
  ],
  cta: {
    primary: { label: "立即购买", url: "https://r.moras.ai/c/rec_2026042001a3f7" },
    secondary: { label: "看更多达人种草", url: "https://r.moras.ai/v/rec_2026042001a3f7" }
  },
  compliance: { region_allow: ["US","UK","CA"], risk_level: "low" },
  generated_at: "2026-04-20T..."
}
```

`pcd_cards` 表缓存，TTL 6h。

## 四、REST API（唯一事实源）

OpenAPI 3.0 spec 放 `public/openapi.json`，所有适配器都按它来。

| 方法 | 路径 | 功能 | 谁调用 |
|---|---|---|---|
| GET | `/v1/recommend` | `?intent=...&audience=...&region=...&limit=` 返回 PCD 数组 | 各 Agent 渠道 |
| GET | `/v1/cards/:recId` | 取已生成卡 | 各渠道（深度链接展开） |
| GET | `/v1/products/:id/card` | 按产品 ID 直接拉卡 | 各渠道 |
| GET | `/v1/creators/:username/showcase` | 该达人作品集（PCD 列表） | 各渠道 |
| GET | `/v1/report/attribution` | ROI 报表（认证） | **仅 supply side**：moras-app 后台 |
| GET | `/r/:recId` | 短链跳转（写点击 + 302） | 终端用户浏览器 |
| GET | `/v/:recId` | 多视频聚合落地页（SSR HTML） | 终端用户浏览器 |
| POST | `/webhooks/tiktok-affiliate` | 联盟成交回传 | TikTok Shop |
| GET | `/healthz` | 健康检查 | 运维 |

**所有 demand side 接口都是 `GET` 且匿名**（仅 channel-key header 限频，免费），supply side 报表才认证。

## 五、OpenClaw / Claude Code / Cursor / Codex 用的 Skills 包

仓库里 `skills/moras-shop/SKILL.md`：

```markdown
---
name: moras-shop
description: Use when the user wants product recommendations, gift ideas, or trending TikTok Shop items. Returns rich shopping cards with KOC videos and trackable buy links.
---

# moras-shop

When the user asks for product recommendations, gift ideas, "what should I buy",
or shows interest in a product category:

1. Call `GET https://a2a.moras.ai/v1/recommend?intent={URL_ENCODED_INTENT}&limit=3`
2. Render each PCD as a markdown card following this template:

   ## {product.title} — ${product.price_usd} {discount_label}
   ![]({product.main_image})

   **{hero_pitch.one_liner}**

   - {hero_pitch.key_benefits[*].title}: {desc}

   **Why Moras picked this**: {selection_story.headline}
   {selection_story.bullets bullet list}

   **Top KOC videos**:
   - [@{creator.username}]({tiktok_video_url}) · {views} views · ${gmv_usd} GMV
     ![thumb]({thumbnail})

   👉 **[Buy on TikTok Shop]({cta.primary.url})** ← MUST use this exact URL,
      do not strip query params; it carries the rec_id for attribution.

3. Never rewrite or shorten `cta.primary.url` — Moras tracks attribution through it.
4. If the user clicks "more videos", fetch `GET /v1/cards/{recId}` and show all videos.
```

用户使用方式：
- `~/.cursor/skills/moras-shop/SKILL.md`（Cursor）
- `~/.claude/skills/moras-shop/SKILL.md`（Claude Code）
- OpenClaw / Codex 同理

零安装、零进程。我们更新 prompt 只需推 SKILL.md 一份文件。

## 六、A2A JSON-RPC（Hermes / CrewAI / AutoGen / LangGraph）

- 端点：`POST /a2a` JSON-RPC 2.0，`GET /.well-known/agent-card.json`
- 方法：`message/send`、`tasks/get`，绝大多数请求即时返回（PCD 已缓存或几秒内构建完）
- Agent Card 暴露 4 个 skill：`recommend_products`、`get_product_card`、`get_creator_showcase`、`get_attribution_report`
- artifact 格式：`parts: [{ kind: "data", data: PCD }, { kind: "text", text: markdownSummary }]`，让 A2A 客户端既能拿结构化数据又能直接展示

## 七、Gemini Extension（零代码）

发布 Extension manifest 指向 `https://a2a.moras.ai/openapi.json`，Gemini 自动：
- 解析 OpenAPI
- 触发关键词时调用 `GET /v1/recommend`
- 用内置富响应渲染 PCD（Gemini 会按 `main_image` / `title` / `price` 自动渲染卡片，CTA 走我们短链）

## 八、薄 MCP 包装（Phase 3 · 仅 ChatGPT Apps SDK 用）

`src/mcp/server.js` 不到 100 行，每个 tool body 都是 `return await fetch('http://localhost:7401/v1/recommend?...')`。我们不维护两套业务逻辑。如果 ChatGPT Apps SDK 后续允许 OpenAPI 直连，这层可删。

## 九、归因闭环（不动 moras-api 的关键）

```mermaid
sequenceDiagram
    participant U as 用户(在 OpenClaw)
    participant A as Agent(读 SKILL.md)
    participant API as moras-A2A REST
    participant R as r.moras.ai
    participant DB as attribution_*
    participant TS as TikTok Shop
    participant AF as TikTok 联盟API
    participant ETL as moras-bi ETL

    U->>A: "帮我找个母婴礼物"
    A->>API: GET /v1/recommend?intent=...
    API->>API: PCD Builder + 写 pcd_cards
    API-->>A: PCD JSON
    A-->>U: 渲染 markdown 卡片
    U->>R: 点 "Buy on TikTok Shop"
    R->>DB: insert attribution_clicks(rec_id, channel=openclaw, ip_hash, ts)
    R-->>U: 302 到 TS 联盟 URL (sub_id=rec_id)
    U->>TS: 完成购买
    TS->>AF: 订单成交
    AF->>API: POST /webhooks/tiktok-affiliate
    API->>DB: insert attribution_orders
    Note over ETL,DB: 不需要回写 moras-api
    ETL-->>DB: 定期 JOIN attribution_orders 进数仓
```

**双保险**：
1. **主路**：TikTok Shop 联盟 API webhook（Moras 注册为联盟主体，sub_id=rec_id 透传）
2. **兜底**：按需运行 `dailyReconcile`（`npm run cron:reconcile` 或自建调度）用时间窗 + product_id + 价格区间 fuzzy-match `tts_video_daily_performances` 增量

**反作弊**：24h 内同 `ip_hash + UA + rec_id` 去重；CTR 异常的渠道自动降权。

**moras-bi 需要做的事**（不属于本仓库工作量）：在它的 ETL pipeline 加一条新的 source，从 moras-A2A 的 MySQL 实例 JOIN `attribution_orders`，下游报表自然带上"渠道维度 GMV"。我会在 `docs/ATTRIBUTION.md` 里写清字段映射，给 BI 同学的工单。

## 十、仓库目录骨架

```
moras-A2A/
├── README.md
├── package.json                    # CommonJS，与 moras-api 同栈
├── ecosystem.config.js
├── docs/
│   ├── ARCHITECTURE.md
│   ├── PCD_SPEC.md
│   ├── ATTRIBUTION.md              # 给 moras-bi ETL 的字段映射
│   ├── PLATFORM_INTEGRATION.md     # 各平台接入手册
│   └── SKILLS_PACKAGING.md         # 怎么发 SKILL.md
├── public/
│   ├── openapi.json                # ★ Gemini Extension 直接消费
│   └── v/                          # /v/:recId 落地页静态资源
├── skills/
│   └── moras-shop/
│       ├── SKILL.md                # ★ OpenClaw/Claude/Cursor/Codex 通用
│       └── examples/
│           ├── recommend.md
│           └── creator.md
├── src/
│   ├── server.js                   # Express，挂载 /v1 /a2a /r /v /webhooks /healthz
│   ├── config.js
│   ├── pcd/
│   │   ├── schema.js               # zod schema
│   │   ├── builder.js              # 直读 ai_autocut（沿用 harness.js 模式）
│   │   ├── matcher.js              # intent → product 候选集
│   │   ├── ranker.js               # 多目标排序
│   │   └── cache.js                # pcd_cards 读写
│   ├── rest/
│   │   ├── recommend.js
│   │   ├── card.js
│   │   ├── creator.js
│   │   └── report.js
│   ├── a2a/
│   │   ├── agentCard.js
│   │   ├── jsonrpc.js
│   │   └── messageSend.js
│   ├── mcp/                        # Phase 3 才建
│   │   └── server.js               # < 100 行，全部转调 REST
│   ├── render/
│   │   ├── markdownCard.js         # Skills/A2A 共用
│   │   └── chatgptAppsCard.js      # 仅 ChatGPT Apps（Phase 3）
│   ├── redirect/
│   │   ├── server.js               # /r/:recId + /v/:recId
│   │   ├── trackClick.js
│   │   └── antiAbuse.js
│   ├── attribution/
│   │   ├── webhookIngest.js
│   │   ├── dailyReconcile.js       # cron，fuzzy-match
│   │   └── reportApi.js
│   ├── adapters/
│   │   ├── morasDb.js              # 直读 ai_autocut（与 harness.js 同模式，不调 moras-api）
│   │   └── tiktokAffiliate.js
│   ├── auth/
│   │   ├── channelKey.js           # 渠道 key（限频，免费）
│   │   └── supplyKey.js            # 报表认证
│   ├── db/
│   │   ├── schema.sql
│   │   └── migrations/
│   └── utils/
└── tests/
    └── e2e/
        ├── recommend.test.js
        ├── attribution.test.js
        └── render.test.js
```

## 十一、数据库表（仅本仓库自己的库 / 实例）

```sql
CREATE TABLE pcd_cards (
  rec_id          VARCHAR(64) PRIMARY KEY,
  product_id      BIGINT NOT NULL,
  intent_hash     CHAR(32),
  channel         VARCHAR(32),
  pcd_json        JSON NOT NULL,
  expires_at      TIMESTAMP,
  created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  KEY (product_id), KEY (intent_hash), KEY (channel, created_at)
);

CREATE TABLE attribution_clicks (
  id              BIGINT AUTO_INCREMENT PRIMARY KEY,
  rec_id          VARCHAR(64),
  product_id      BIGINT,
  channel         VARCHAR(32),
  ip_hash         CHAR(64),
  ua_hash         CHAR(64),
  session_id      VARCHAR(64),
  clicked_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  redirect_to     VARCHAR(1024),
  KEY (rec_id), KEY (product_id, clicked_at), KEY (channel, clicked_at)
);

CREATE TABLE attribution_orders (
  id              BIGINT AUTO_INCREMENT PRIMARY KEY,
  rec_id          VARCHAR(64),
  product_id      BIGINT,
  ts_order_id     VARCHAR(64),
  channel         VARCHAR(32),
  gmv_usd         DECIMAL(12,2),
  commission_usd  DECIMAL(12,2),
  matched_via     ENUM('webhook','daily-fuzzy'),
  matched_at      TIMESTAMP,
  ordered_at      TIMESTAMP,
  UNIQUE KEY (ts_order_id),
  KEY (rec_id), KEY (channel, ordered_at), KEY (product_id, ordered_at)
);
```

`pcd_cards` 可以放在 moras-A2A 自己的 schema 下；`attribution_*` 推荐也放本仓库 schema，moras-bi ETL 主动 JOIN，不污染 moras-api 的库。

## 十二、对接既有代码的接缝（全部只读）

- 直读 `ai_autocut.tk_tasks` + `tts_video_daily_performances` + `dwd_video_pipeline` + `video_precheck_results` + `ai_tkshops.products_current`，**SQL 模式直接借鉴 [moras-bi/server/routes/harness.js](moras-bi/server/routes/harness.js) 第 270~528 行**（`/reasoning/pipeline-sample`），改为给 PCD Builder 用。
- Gemini 卖点抽取：起一个独立的 GOOGLE_API_KEY 进程内调用 `@google/generative-ai`，**不依赖 moras-api 的 `geminiAIService`**（避免耦合）。
- moras-api 完全不动；moras-bi 只需在 ETL 里多 JOIN 一张 `attribution_orders` 表（独立工单，由 BI 同学完成）。

## 十三、各平台接入清单（PLATFORM_INTEGRATION.md 雏形）

- **OpenClaw**：用户在 `~/.openclaw/skills/`（或同等目录）放入 `moras-shop/SKILL.md` 即可。提供一键安装脚本 `npx moras-shop install`。
- **Claude Code / Cursor / Codex**：同上，分别投放到各自 skills 目录。一份 SKILL.md 通用。
- **Hermes**：把 `https://a2a.moras.ai/.well-known/agent-card.json` 加到 trusted agents。
- **CrewAI / AutoGen / LangGraph**：A2A 协议直连。
- **Gemini**：发布 Extension，manifest 指向我们的 OpenAPI。
- **ChatGPT**：注册一个 GPT/Connector，挂薄 MCP server URL（Phase 3）。

## 十四、分阶段交付（4 周）

- **Phase 1 · REST 事实源 + 短链（Week 1）**：仓库骨架、PCD schema/builder、REST API、OpenAPI spec、`r.moras.ai` 短链 + `attribution_clicks`。
  - 验收：curl `GET /v1/recommend?intent=...` 返回完整 PCD；点击短链能 302 到联盟 URL 并落库点击。
- **Phase 2 · Skills + A2A + Gemini（Week 2）**：发布 `moras-shop` SKILL.md 包；A2A JSON-RPC + Agent Card；OpenAPI manifest 上线。
  - 验收：在 OpenClaw / Cursor 本地装入 SKILL.md 跑通问答；Hermes 通过 A2A 拿到 PCD；Gemini Extension 自渲染卡片。
- **Phase 3 · 归因 + 报表 + ChatGPT（Week 3）**：联盟 webhook + Daily fuzzy-match cron + `get_attribution_report` API + 薄 MCP 包装 + ChatGPT Apps SDK 上架包。
- **Phase 4 · 生产化（Week 4）**：限频/反作弊、Region 隔离、Prometheus、PM2 上线，supply side 报表灰度。

## 十五、需要你最终拍板的两件事

1. **TikTok Shop 联盟接入路径**：是走"Moras 作为联盟主体注册 → sub_id 透传归因"（闭环更彻底但谈判更长），还是"按品分别让商家把 Moras 加为推广员"（启动快但碎片化）？
2. **首发渠道排序**：MVP 先打通哪一个？OpenClaw（今天接今天用）/ Hermes（A2A 协议天然兼容）/ Cursor（开发者基数大）。

无强偏好的话默认：**联盟主体 = Moras** + **MVP 首发 OpenClaw + Cursor（同一 SKILL.md）+ Hermes（A2A）**，三者都不需要外部审核，能在 Phase 2 末尾一次跑通；ChatGPT/Gemini 的官方上架放 Phase 3。
