# InsightReply 实施计划 (Implementation Plan)

> 本文档记录了 InsightReply 各模块的具体技术实现方案，是 `DEVELOPMENT_PLAN.md`（做什么）的落地补充（怎么做）。
> 功能完善度的产品策略分析见 → [`PRODUCT_ROADMAP.md`](./PRODUCT_ROADMAP.md)

---

## 一、数据抓取策略 (Data Acquisition Strategy)

InsightReply 采用 **"前端主动感知 + 后端规则引擎"** 的双模式来实现数据抓取：

### 1. 插件端交互式抓取 (User-Driven)
*   **触发场景**：用户在 X (Twitter) 上浏览推文时，想针对某条推文生成评论。
*   **实现逻辑**：Content Script 直接从浏览器 DOM 中提取当前选定推文的作者、正文、互动数 (Like/Reply)。
*   **优点**：零延迟，无需后端预先抓取该推文，即看即得。

### 2. 后端雷达监控抓取 (System-Driven / Radar)
*   **方案选型对比**：

| 方案 | 代表 | 优点 | 缺点 | 月费 |
|------|------|------|------|------|
| 付费 SaaS | Apify / RapidAPI | 即插即用，免维护 | 按次计费 | $30-$100+ |
| 自建开源 | **Nitter + Camoufox** | 几乎免费 | 技术门槛高 | 仅服务器费 |

*   **最终选型**：自建 Nitter 开源方案。
*   **Nitter 实例地址**：`https://x.beenglish.eu.org/`

---

## 二、浏览器插件技术实现 (Extension Architecture)

### 1. Content Script 与 UI 注入
*   **Content Script (`src/content/index.ts`)**：监听页面 URL 变化，使用 `MutationObserver` 实时检测新加载的推文 DOM 节点。
*   **Shadow DOM 注入**：在推文操作栏注入"Insight"入口按钮。点击后展示主面板，面板挂载在 Shadow DOM 内，确保与 Twitter 原生样式完全隔离。
*   **Background Service Worker**：作为中转站，处理与后端 Go API 的通信和认证。

### 2. UI 设计规范 (ui-ux-pro-max)
*   **暗黑毛玻璃风格**：主色调 `#0A0A0A` ~ `#171717`，浮动面板使用 `backdrop-filter: blur()`。
*   **品牌渐变色**：紫蓝渐变 `#8B5CF6` → `#3B82F6`，用于 CTA 按钮和高亮标签。
*   **字体**：优先使用 `Inter` / `Geist` / `SF Pro` 等现代无衬线字体。
*   **微动画**：所有状态切换配 `ease-in-out` 过渡（`200ms-300ms`）。

### 3. 技术栈规范
*   **Tailwind CSS v4**：所有间距、颜色从 Design Tokens 读取，禁止硬编码内联样式。
*   **Vue 3 Composition API**：高频 UI 元素（`Button`, `Card`, `Badge`）封装为基础组件。
*   **`cn()` 工具函数**：基于 `clsx` + `tailwind-merge` 统一处理动态 Class 冲突。
*   **轻量化打包**：禁止引入完整组件库，采用按需加载。

### 4. Content Script 性能优化策略 *(新增)*

> 在 Twitter 重型 SPA 中，粗暴监听全文档 DOM 变更会产生严重性能问题。

*   **收缩监听范围**：将 `MutationObserver` 的 `observe` 目标从 `document.body` 收缩到 `main[role="main"]` 或 Timeline 容器。
*   **防抖扫描**：`scanTweets()` 加入 `requestIdleCallback` 或 `debounce(200ms)`，避免高频回调。
*   **已处理标记**：通过 `WeakSet<HTMLElement>` 记录已注入按钮的推文节点，避免重复扫描。
*   **多选择器 Fallback**：Twitter 的 `data-testid` 属性随时可能变更，已处理推文选择器应支持多层 Fallback：
    ```typescript
    const TWEET_SELECTORS = [
      'article[data-testid="tweet"]',
      'article[role="article"]',
      'div[data-testid="cellInnerDiv"] article'
    ];
    ```

---

## 三、高可用后台抓取系统 (Industry Radar - Enterprise Architecture)

考虑到系统的**稳定性、安全性和反风控 (Anti-Ban)**，自建 Nitter 之上的抓取系统设计为一套健壮的企业级爬虫架构。

### 1. 终极反风控与防封禁策略 (Anti-Ban Mechanisms)

| 机制 | 说明 |
|------|------|
| **请求抖动 (Jitter)** | 每次请求间加入 `1s-5s` 强随机延迟，严禁整点并发 |
| **熔断降级 (Circuit Breaker)** | Nitter 连续返回 429/503 时自动暂停 30 分钟 |
| **Fallback 节点池** | 主节点熔断时自动切换到备用 Nitter 实例 |
| **指纹轮换 (Fingerprint Rotation)** | 同步轮换 UA + `Sec-Ch-Ua` + `Accept-Language` 等全套 Header |
| **会话隔离 (Session Isolation)** | 每个采集任务独立 Cookie Jar，避免跨任务身份串联 |

### 2. 高可用采集架构 (Scraper & Queue)
*   **异步任务队列**：基于 Redis 的 `Asynq`，解耦"触发"与"执行"，控制最大并发数。
*   **指数退避重试 (Exponential Backoff)**：失败后按 1min → 3min → 10min 递增重试间隔。
*   **采集器健壮性**：
    *   `goquery` 解析找不到元素时记录 Warn，返回部分数据，**绝不 Panic**。
    *   关键报错时 **Dump HTML 快照**到日志表，方便排查 Nitter DOM 结构变更。

### 3. 数据计算与存储层

*   **批量 Upsert**：使用 `ON CONFLICT DO UPDATE` 策略，降低 IO 压力，防止死锁。
*   **热度公式**：
    ```
    heat_score = (Like增量 × 0.4) + (RT增量 × 0.3) + (Reply增量 × 0.3)
    ```
*   **动态智能抓取频率 (Smart Crawling)**：
    *   🔥 热度飙升的推文 → **高频队列**（每 15 分钟抓一次）
    *   🧊 24 小时无新动态 → **低频队列**（每 4 小时抓一次）
    *   极大节省抓取资源，显著降低被封概率。

---

## 四、后端安全与认证体系 *(新增)*

### 1. JWT 认证方案

所有 `/api/v1` 路由（除 `/auth/*` 外）必须经过 JWT 认证中间件。

*   **技术选型**：`go-chi/jwtauth` + HMAC-SHA256 签名。
*   **Token 结构**：
    ```json
    {
      "sub": "user-uuid",
      "tier": "Pro",
      "exp": 1709164800
    }
    ```
*   **刷新策略**：Access Token 有效期 24h，Refresh Token 有效期 30d，存于 `chrome.storage.local`。

### 2. Rate Limiting（分级限流）

| Tier | 评论生成 | 监控关键词 | 监控账号 |
|------|---------|-----------|---------|
| Free | 10 次/天 | 3 个 | 3 个 |
| Pro | 无限 | 20 个 | 20 个 |
| Premium | 无限 | 50 个 | 50 个 |

*   **实现方案**：基于 Redis 的滑动窗口计数器，Key 格式：`ratelimit:{user_id}:{action}:{window}`。
*   **中间件挂载**：在 chi Router 中通过 `r.Use(RateLimitMiddleware)` 全局或分路由挂载。

### 3. CORS 配置

```go
r.Use(cors.Handler(cors.Options{
    AllowedOrigins:   []string{"chrome-extension://*", "https://x.com", "https://twitter.com"},
    AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
    AllowedHeaders:   []string{"Authorization", "Content-Type"},
    AllowCredentials: true,
    MaxAge:           300,
}))
```

---

## 五、LLM 服务韧性设计 *(新增)*

### 1. 调用链路防护

```
请求 → Timeout Guard (30s) → Circuit Breaker → Retry (max 2) → OpenAI API
```

*   **Timeout**：`context.WithTimeout(ctx, 30*time.Second)` 包裹每次 LLM 调用。
*   **Circuit Breaker**：使用 `sony/gobreaker`，连续 5 次失败后半开，每 60s 试探一次恢复。
*   **指数退避重试**：仅对 5xx / Timeout 重试，4xx（如 quota exceeded）立即失败。

### 2. 多模型动态路由与 Fallback

系统重构为 **多 Provider 支持 (OpenAI / Anthropic / DeepSeek / Gemini)**，以应对不同用户的专属模型偏好和单一 Provider 宕机时的容灾。同时支持**代理接口与自定义模型**。

**路由与配置设计**：
1. **环境变量驱动 (ENV)**：管理员可通过配置 `OPENAI_BASE_URL` 改写接口地址（完美兼容 Groq、vLLM、Ollama 等任意 OpenAI 兼容代理）。
2. **可用模型下发**：通过 `OPENAI_AVAILABLE_MODELS="gpt-4o,gpt-4o-mini"` 配置白名单，后端通过 `GET /sys/config/llms` 统一向前端下发。
3. **前端交互 (`default_llm_model`)**：前端展示为**「可输入的下拉列表 (Combobox)」**，用户既可从系统下发的列表中点选，也支持手动输入任意字符串，实现完全自定义。
4. **故障转移 (Fallback)**：当前尝试的 Provider 返回 5xx 时，按兜底顺序切换：`Anthropic → OpenAI → Gemini → DeepSeek`。

**统一适配器抽象**：
建立统一的 `LLMProvider` 接口，抹平各平台 API 结构差异，内部集成对应 SDK：
- OpenAI (含兼容代理): `sashabaranov/go-openai` (支持通过 Builder 传入 BaseURL)
- Anthropic: 直接构造 REST API 请求
- DeepSeek: 兼容 OpenAI SDK，基于 `DEEPSEEK_BASE_URL` 重连
- Gemini: `google.golang.org/api/generativelanguage/v1beta`

### 3. Token 成本审计

每次 LLM 调用记录 `prompt_tokens + completion_tokens`，写入 `api_usage_logs` 表：
```sql
INSERT INTO api_usage_logs (user_id, provider, model, prompt_tokens, completion_tokens, cost_usd)
VALUES ($1, $2, $3, $4, $5, $6);
```

---

## 六、Prompt 工程规范 *(新增)*

### 1. 策略模板结构

每个评论策略维护独立的 Prompt 模板文件，存放于 `server/prompts/` 目录：

```
server/prompts/
├── system.txt            # 全局 System Prompt
├── cognitive_upgrade.txt  # 认知升级型
├── contrarian.txt         # 反向观点型
├── data_supplement.txt    # 数据补充型
├── empathy.txt            # 共鸣型
└── founder_exp.txt        # 创始人经验型
```

### 2. Prompt 结构公式 (必须遵守)

```
[System Prompt]
- 角色定义：专业社交媒体表达顾问
- 输出约束：字数、语言、格式

[User Prompt]
- 用户身份标签：{identity}
- 目标推文：{tweet_content}
- 策略指令：{strategy_specific_instruction}
- 结构公式：Hook + Position + Insight + Brevity
- Few-shot 示例：2-3 条高质量参考

[Constraints]
- 不超过 280 字符 (或用户指定长度)
- 语言：与原推文一致，或用户指定
- 语气与身份匹配
- 禁止引号包裹
```

### 3. 批量生成规范

API 单次返回 5 种策略 × 2 条备选 = **10 条候选评论**：
*  方案 A：单次 LLM 调用，Prompt 中要求 JSON 数组输出
*  方案 B：5 组并发调用，每组 n=2
*  **推荐方案 A**，减少 API 调用次数

---

## 七、后端 Go 接口规范

*   采用 RESTful 标准。
*   所有接口返回统一 JSON 结构：
    ```json
    {
      "code": 200,
      "message": "success",
      "data": {}
    }
    ```
*   错误码体系：`4xxx` 为客户端错误，`5xxx` 为服务端错误。
*   详细接口文档见 → [`docs/API.md`](./API.md)

---

## 八、环境变量与配置管理 *(新增)*

所有运行时配置统一通过环境变量注入，**禁止硬编码**。

| 变量名 | 说明 | 示例 |
|-------|------|------|
| `DATABASE_URL` | PostgreSQL 连接串 | `postgres://user:pass@host:5432/db` |
| `OPENAI_API_KEY` | OpenAI API Key | `sk-...` |
| `JWT_SECRET` | JWT 签名密钥 | 随机 32+ 字符串 |
| `SERVER_PORT` | 服务端口 | `8080` |
| `CORS_ORIGINS` | 允许的跨域来源 | `chrome-extension://*` |
| `LOG_LEVEL` | 日志级别 | `info` / `debug` / `warn` |
| `LLM_TIMEOUT_SEC` | LLM 调用超时秒数 | `30` |

模板文件 → [`server/.env.example`](../server/.env.example)

---

## 九、验证计划 (Verification Plan)

| # | 验证项 | 方法 |
|---|--------|------|
| 1 | 防封控链路 | 模拟 100 并发请求，验证熔断 + Jitter 能正确拦截 |
| 2 | HTML 解析容错 | 模拟账号冻结/推文被删等异常页面，确认不会 Panic |
| 3 | Shadow DOM 隔离 | 在 X 深色/浅色模式下验证插件样式不被污染 |
| 4 | 端到端生成链路 | 插件点击 → Background → Go API → LLM → 返回结果 |
| 5 | JWT 认证 *(新增)* | 无 Token / 过期 Token / 非法 Token 拒绝访问 |
| 6 | Rate Limiting *(新增)* | Free 用户超 10 次/天后返回 429 |
| 7 | LLM 韧性 *(新增)* | 模拟 OpenAI 超时 / 5xx，验证熔断降级 + 重试 |
| 8 | Graceful Shutdown *(新增)* | 发送 SIGTERM，验证在途请求完成后才退出 |
| 9 | Onboarding 流程 *(新增)* | 首次打开插件触发引导，设置存入 chrome.storage + 后端 |
| 10 | 多条备选评论 *(新增)* | 验证返回 3 条备选，Sidebar 卡片式展示正确 |
| 11 | 热度标签 *(新增)* | 在高互动推文旁正确显示 🔥/⚡ 标签 |
| 12 | 效果追踪闭环 *(新增)* | 复制评论 → 24h 后回查 → 互动数据写入 DB |

---

## 十、用户 Onboarding 实现方案 *(新增)*

### 1. 流程设计

首次安装/首次打开插件时，检测 `chrome.storage.sync` 中是否存在 `onboarding_completed` 标志。若无，显示 3 步引导：

```typescript
// background/index.ts
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.sync.set({ onboarding_completed: false });
  }
});
```

### 2. 数据存储

| 字段 | 存储位置 | 说明 |
|------|---------|------|
| `identity_label` | `chrome.storage.sync` + `users` 表 | 身份标签 |
| `language_preference` | `chrome.storage.sync` + `users` 表 | 偏好语言 |
| `tone_preference` | `chrome.storage.sync` + `user_style_profiles` 表 | 风格倾向 |
| `onboarding_completed` | `chrome.storage.sync` | 是否已完成引导 |

### 3. 与生成链路的集成

用户设置完成后，每次生成评论时自动读取 `chrome.storage.sync` 中的偏好，附加到 `GENERATE_REPLY` 消息中：

```typescript
// 改造 Sidebar.vue 的 generate()
const prefs = await chrome.storage.sync.get(['identity_label', 'language_preference', 'tone_preference']);
chrome.runtime.sendMessage({
  type: 'GENERATE_REPLY',
  payload: {
    tweetContent: props.tweetData.content,
    strategy: 'all',
    identity: prefs.identity_label,
    language: prefs.language_preference,
    tone: prefs.tone_preference
  }
});
```

---

## 十一、前端热度标签实现方案 *(新增)*

### 1. 判断逻辑

```typescript
interface HeatLevel {
  label: string;
  emoji: string;
  className: string;
}

const getHeatLevel = (likes: number, minutesAgo: number): HeatLevel | null => {
  if (likes > 1000 && minutesAgo < 120) {
    return { label: 'Trending', emoji: '🔥', className: 'heat-trending' };
  }
  if (likes > 100 && minutesAgo < 60) {
    return { label: 'Rising', emoji: '⚡', className: 'heat-rising' };
  }
  return null;
};
```

### 2. 注入位置

在 `injectInsightButton()` 函数中，紧挨 Insight 按钮旁边注入热度标签：
*   🔥 Trending：红色渐变 Badge (`background: linear-gradient(135deg, #ef4444, #f97316)`)
*   ⚡ Rising：黄色渐变 Badge (`background: linear-gradient(135deg, #f59e0b, #eab308)`)

### 3. 时间计算

从推文 DOM 中的 `<time datetime="...">` 元素提取发帖时间，计算与当前时间的差值：
```typescript
const timeEl = tweetElement.querySelector('time');
const postedAt = new Date(timeEl?.getAttribute('datetime') || '');
const minutesAgo = (Date.now() - postedAt.getTime()) / 60000;
```

---

## 十二、数据飞轮 — 效果追踪与风格学习 *(新增)*

### 1. 效果追踪数据流

```
用户复制评论
    │ copyToClipboard() → 同时记录 hash(reply_text) 到 chrome.storage.local
    ▼
chrome.storage.local:
  tracked_replies: [{
    hash: "a1b2c3",
    text: "Most people miss...",
    strategy: "contrarian",
    copied_at: 1709164800,
    tweet_url: "https://x.com/.../status/..."
  }]
    │
    ▼
Content Script 延迟触发 (当用户访问自己 Profile 页时)
    │ 条件: URL 匹配 x.com/{current_user}
    ▼
扫描用户近期 Tweets/Replies
    │ 对比 tracked_replies 中的 text hash
    ▼
匹配成功 → 提取当前 Likes / Replies 数
    │
    ▼
chrome.runtime.sendMessage → Background → POST /api/v1/replies/{id}/performance
    │
    ▼
写入 reply_performance 表
```

### 2. 风格学习 Prompt 注入

当 `user_style_profiles` 表有足够数据（≥ 10 条追踪记录）后，生成评论时自动注入风格画像：

```
[附加上下文 - 个人风格画像]
基于你过去 30 天的高互动评论分析:
- 最有效策略: {top_strategies} (平均互动率 {avg_rate}%)
- 风格特征: {style_description}
- 高频高互动关键词: {keywords}
- 偏好评论长度: {avg_length} 字符

请参考以上风格特征生成评论。
```

### 3. 风格特征提取 (后端定时任务)

每周对每个用户的 `reply_performance` 数据运行一次 LLM 分析：

```
System: 你是社交媒体互动分析专家。
User: 以下是该用户最近 30 天互动率最高的 10 条评论:
{replies_list}

请总结其写作风格特征, 包括:
1. 最常用的句式结构
2. 高频关键词或短语
3. 语气倾向 (专业/幽默/犀利)
4. 平均有效长度

输出为 JSON 格式。
```

---

## 十三、用户可配置系统设计 *(新增)*

> **设计原则**：系统不硬编码任何特定产品/领域/策略。所有与用户场景相关的能力均通过用户自定义设置实现，确保对任意行业、任意产品类型的可扩展性。

### 1. 架构思想：零硬编码 (Zero Hardcoding)

```
传统做法 (❌):
  Prompt 里写死 "你是一个 AI 创始人，正在推广 SwiftBiu..."

可扩展做法 (✅):
  Prompt 运行时从 user_product_profiles 表 + user_custom_strategies 表
  动态拼装上下文，适用于任何用户、任何产品
```

### 2. 产品档案 (Product Profile) 注入机制

当用户配置了产品档案后，生成评论时自动注入以下上下文块：

```
[产品上下文 — 自动注入，仅当用户已配置产品档案时]
你正在帮助一位 {identity} 撰写社交评论。
该用户正在推广的产品: {product_name} — {tagline}
产品所属领域: {domain}
核心功能: {key_features}
目标用户群: {target_users}
{custom_context}

注意:
- 不要直接提及产品名称或链接 (除非用户使用了"创始人实战型"策略)
- 评论应从 {domain} 领域专家的角度出发，让读者产生"这个人很懂行"的印象
- 引发好奇心 → 点击 Profile → 发现产品，而非硬推
```

### 3. 自定义策略运行时

用户自定义的策略模板支持以下**模板变量**，运行时自动替换：

| 变量 | 来源 | 说明 |
|------|------|------|
| `{tweet_content}` | 当次请求 | 目标推文原文 |
| `{identity}` | 用户设置 | 身份标签 |
| `{product_name}` | 产品档案 | 产品名称 |
| `{domain}` | 产品档案 | 所属领域 |
| `{key_features}` | 产品档案 | 核心功能 |
| `{competitors}` | 产品档案 | 竞品列表 |
| `{language}` | 用户设置 | 输出语言 |
| `{max_length}` | 当次请求 | 最大字符数 |
| `{custom_context}` | 产品档案 | 自定义上下文 |

**后端处理流程**：
```go
// service/ai_service.go
func (s *AIService) buildPrompt(req GenerateRequest, profile *ProductProfile, strategies []Strategy) string {
    // 1. 加载 System Prompt
    // 2. 如果有产品档案 → 注入产品上下文块
    // 3. 合并内置策略 + 用户自定义策略
    // 4. 替换所有模板变量
    // 5. 如果有风格画像 (user_style_profiles) → 注入风格上下文
    // 6. 返回最终 Prompt
}
```

### 4. 前端推文相关性评分

Content Script 基于产品档案中的 `relevance_keywords` 对当前浏览的推文做实时相关性判断：

```typescript
// content/relevance.ts
const scoreRelevance = (tweetText: string, keywords: string[]): number => {
  const text = tweetText.toLowerCase();
  let score = 0;
  for (const kw of keywords) {
    if (text.includes(kw.toLowerCase())) score++;
  }
  return score;
};

// 注入标签
const relevance = scoreRelevance(tweetData.content, userProfile.relevance_keywords);
if (relevance >= 2) {
  // 注入 🎯 "High Relevance" 标签
}
```

**关键词来源**：`chrome.storage.sync` 缓存用户的 `relevance_keywords`（来自产品档案 API），定期同步。

### 5. 前端策略合并展示

Sidebar 策略选择器同时展示系统内置策略和用户自定义策略：

```typescript
// Sidebar.vue
const allStrategies = computed(() => [
  ...builtinStrategies,                // 系统内置 5 种
  ...userCustomStrategies.value        // 用户自定义 N 种 (从 API 拉取)
]);
```

自定义策略用不同背景色或 `⚙️ Custom` 标签区分，方便用户识别。

### 6. Prompt 组装顺序

```
┌─────────────────────────────────────────────┐
│ System Prompt (角色定义 + 输出约束)           │
├─────────────────────────────────────────────┤
│ [可选] 产品上下文 (如果用户配置了产品档案)     │
├─────────────────────────────────────────────┤
│ [可选] 个人风格画像 (如果有 ≥10 条追踪数据)   │
├─────────────────────────────────────────────┤
│ User Prompt:                                 │
│   - 身份标签                                 │
│   - 目标推文                                 │
│   - 策略指令 (内置 or 用户模板，变量已替换)    │
│   - 结构公式: Hook + Position + Insight       │
│   - Few-shot 示例 (系统默认 or 用户自定义)     │
├─────────────────────────────────────────────┤
│ Constraints (字数/语言/格式)                  │
└─────────────────────────────────────────────┘
```
