21 KiB
InsightReply 实施计划 (Implementation Plan)
本文档记录了 InsightReply 各模块的具体技术实现方案,是
DEVELOPMENT_PLAN.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: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 结构:
{ "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 配置
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 宕机时的容灾。同时支持代理接口与自定义模型。
路由与配置设计:
- 环境变量驱动 (ENV):管理员可通过配置
OPENAI_BASE_URL改写接口地址(完美兼容 Groq、vLLM、Ollama 等任意 OpenAI 兼容代理)。 - 可用模型下发:通过
OPENAI_AVAILABLE_MODELS="gpt-4o,gpt-4o-mini"配置白名单,后端通过GET /sys/config/llms统一向前端下发。 - 前端交互 (
default_llm_model):前端展示为**「可输入的下拉列表 (Combobox)」**,用户既可从系统下发的列表中点选,也支持手动输入任意字符串,实现完全自定义。 - 故障转移 (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 表:
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 结构:
{ "code": 200, "message": "success", "data": {} } - 错误码体系:
4xxx为客户端错误,5xxx为服务端错误。 - 详细接口文档见 →
docs/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
九、验证计划 (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 步引导:
// 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 消息中:
// 改造 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. 判断逻辑
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="..."> 元素提取发帖时间,计算与当前时间的差值:
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} |
产品档案 | 自定义上下文 |
后端处理流程:
// 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 对当前浏览的推文做实时相关性判断:
// 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 策略选择器同时展示系统内置策略和用户自定义策略:
// 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 (字数/语言/格式) │
└─────────────────────────────────────────────┘