geoGo/internal/collect
renzhiyuan aa4c7a09a9 3232 2026-04-26 15:56:15 +08:00
..
README.md 3232 2026-04-26 15:56:15 +08:00
base.go 3232 2026-04-26 15:56:15 +08:00
deepseek.go 3232 2026-04-26 15:56:15 +08:00
doubao.go 3232 2026-04-26 15:56:15 +08:00
interface.go 3232 2026-04-26 15:56:15 +08:00
manager.go 3232 2026-04-26 15:56:15 +08:00
qianwen.go 3232 2026-04-26 15:56:15 +08:00
utils.go 3232 2026-04-26 00:48:50 +08:00
utils_test.go 3232 2026-04-26 00:48:50 +08:00
wenxin.go 3232 2026-04-26 15:56:15 +08:00

README.md

AI平台收集功能使用说明

概述

internal/collect 模块提供了访问多个AI平台并进行问答的功能目前支持以下平台

  • 文心一言 (wenxin) - 百度AI助手
  • DeepSeek (deepseek) - 深度求索AI
  • 豆包 (doubao) - 字节跳动AI助手
  • 通义千问 (qianwen) - 阿里云AI助手

架构设计

核心组件

  1. CollectorInterface - 收集器接口

    • WaitLogin() (bool, string) - 等待登录
    • AskQuestion(question string) (string, error) - 提问并获取答案
  2. BaseCollector - 基础收集器

    • 浏览器驱动管理
    • Cookie管理保存/加载)
    • 页面操作工具方法
  3. CollectManager - 收集管理器

    • 统一管理不同平台的收集器
    • 提供便捷的API调用
  4. 平台实现

    • WenxinCollector - 文心一言实现
    • DeepseekCollector - DeepSeek实现
    • DoubaoCollector - 豆包实现
    • QianwenCollector - 通义千问实现

快速开始

1. 基本使用

``go package main

import ( "context" "fmt" "geo/internal/collect" "geo/internal/config" "log" "os" )

func main() { // 加载配置 cfg := &config.Config{ Sys: config.SysConfig{ ChromePath: "chrome/chrome.exe", // Chrome浏览器路径 ChromeDataDir: "chrome_data", // Chrome数据目录 CookiesDir: "cookies", // Cookie存储目录 LogsDir: "logs", // 日志目录 }, }

ctx := context.Background()
logger := log.New(os.Stdout, "", log.LstdFlags)

// 创建管理器
manager := collect.NewCollectManager(ctx, cfg, logger)

// 设置参数
params := &collect.CollectParams{
    Headless:  false,      // 是否无头模式false显示浏览器窗口
    UserIndex: "user_001", // 用户索引
    PlatIndex: "wenxin",   // 平台索引
    RequestID: "req_001",  // 请求ID
    Platform:  "wenxin",   // 平台类型
}

// 向文心一言提问
question := "请介绍一下Go语言的特点"
answer, err := manager.AskQuestion("wenxin", params, question)
if err != nil {
    fmt.Printf("错误: %v\n", err)
    return
}

fmt.Printf("问题: %s\n", question)
fmt.Printf("答案: %s\n", answer)

}


### 2. 多平台对比

``go
// 向多个AI平台提问同一个问题
platforms := []string{"wenxin", "deepseek", "doubao", "qianwen"}
question := "什么是人工智能?"

for _, platform := range platforms {
    params := &collect.CollectParams{
        Headless:  true,
        UserIndex: "user_001",
        PlatIndex: platform,
        RequestID: fmt.Sprintf("req_%s", platform),
        Platform:  platform,
    }

    answer, err := manager.AskQuestion(platform, params, question)
    if err != nil {
        fmt.Printf("[%s] 错误: %v\n", platform, err)
        continue
    }

    fmt.Printf("[%s] 答案: %s\n\n", platform, answer)
}

3. 登录管理

``go // 首次使用时需要登录 params := &collect.CollectParams{ Headless: false, // 显示浏览器窗口以便扫码登录 UserIndex: "user_001", PlatIndex: "wenxin", RequestID: "login_req", Platform: "wenxin", }

// 等待登录(会打开浏览器窗口,需要手动扫码或输入账号密码) success, msg := manager.WaitLogin("wenxin", params) if success { fmt.Println("登录成功Cookie已保存") } else { fmt.Printf("登录失败: %s\n", msg) }

// 后续使用会自动加载Cookie无需重复登录 params.Headless = true // 可以切换到无头模式 answer, _ := manager.AskQuestion("wenxin", params, "你好")


### 4. 列出支持的平台

``go
platforms := manager.ListPlatforms()
fmt.Printf("支持的平台: %v\n", platforms)
// 输出: 支持的平台: [wenxin deepseek doubao qianwen]

配置说明

必需的配置项

``go type SysConfig struct { ChromePath string // Chrome浏览器可执行文件路径 ChromeDataDir string // Chrome用户数据目录 CookiesDir string // Cookie存储目录 LogsDir string // 日志文件目录 }


### 示例配置

``go
cfg := &config.Config{
    Sys: config.SysConfig{
        ChromePath:    "/usr/bin/google-chrome",  // Linux
        // ChromePath:    "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",  // Windows
        ChromeDataDir: "./chrome_data",
        CookiesDir:    "./cookies",
        LogsDir:       "./logs",
    },
}

工作流程

  1. 初始化浏览器 - 启动Chrome浏览器实例
  2. 加载Cookie - 从本地文件加载之前的登录状态
  3. 检查登录 - 验证是否已登录
  4. 导航到聊天页面 - 打开AI平台的对话界面
  5. 输入问题 - 在输入框中输入问题
  6. 点击发送 - 触发AI回答
  7. 等待回答 - 等待AI生成完整答案
  8. 提取答案 - 从页面中提取回答内容
  9. 返回结果 - 将答案返回给调用者

注意事项

1. 首次使用需要登录

每个平台首次使用时需要手动登录:

  • 设置 Headless: false 显示浏览器窗口
  • 调用 WaitLogin() 方法
  • 在浏览器中完成登录操作(扫码或输入账号密码)
  • 登录成功后Cookie会自动保存

2. Cookie管理

  • Cookie保存在 cookies/{UserIndex}/{PlatIndex}.json
  • 下次使用会自动加载Cookie无需重复登录
  • 如果登录失效,重新调用 WaitLogin() 即可

3. 选择器适配

由于AI平台的页面结构可能会更新如果遇到问题可能需要调整CSS选择器

  • 在对应的Collector文件中修改 inputSelectorssendSelectorsanswerSelectors
  • 可以通过浏览器的开发者工具查看最新的元素选择器

4. 超时设置

  • 登录超时: 180-300秒
  • 回答超时: 120秒
  • 可根据实际情况在代码中调整

5. 无头模式

  • 开发调试时建议设置 Headless: false
  • 生产环境可以设置 Headless: true 节省资源

扩展新平台

如果要添加新的AI平台需要

  1. 创建新的Collector文件newplatform.go
  2. 实现 CollectorInterface 接口
  3. 继承 BaseCollector 基础结构
  4. interface.goCollectorMap 中注册

示例:

``go package collect

import ( "context" "geo/internal/config" "log" )

type NewPlatformCollector struct { *BaseCollector }

func NewNewPlatformCollector(ctx context.Context, params *CollectParams, cfg *config.Config, logger *log.Logger) CollectorInterface { collector := &NewPlatformCollector{ BaseCollector: NewBaseCollector(ctx, params, cfg, logger), } collector.LoginURL = "https://example.com/login" collector.ChatURL = "https://example.com/chat" return collector }

// 实现 CheckLoginStatus、WaitLogin、AskQuestion 等方法 // ...


然后在 `interface.go` 中注册:

``go
var CollectorMap = map[string]*CollectorValue{
    // ... 其他平台
    "newplatform": {
        Name:       "新平台",
        InitMethod: NewNewPlatformCollector,
        Platform:   "newplatform",
    },
}

故障排查

1. 浏览器启动失败

  • 检查 ChromePath 是否正确
  • 确认Chrome版本是否兼容
  • 查看日志文件了解详细错误

2. 找不到输入框或发送按钮

  • 页面结构可能已更新
  • 打开浏览器Headless: false查看实际DOM结构
  • 更新对应的选择器

3. 登录状态失效

  • 删除对应的Cookie文件
  • 重新调用 WaitLogin() 登录
  • 检查账号是否正常

4. 获取不到答案

  • 增加超时时间
  • 检查网络连接
  • 查看页面是否有验证码或其他拦截

技术栈

  • go-rod: Chrome DevTools Protocol的Go语言封装
  • Chrome/Chromium: 浏览器引擎
  • Context: Go上下文管理
  • JSON: Cookie序列化

许可证

与项目主许可证保持一致。

Collect 模块 - 单浏览器多Page架构

架构说明

本模块采用单浏览器多Page模式,服务启动时创建一个全局浏览器实例,并为每个平台打开一个常驻 Page。

核心特性

  1. 单一浏览器:所有平台共用同一个浏览器实例
  2. 启动时预创建:服务启动时立即创建浏览器并打开所有平台的页面
  3. Page 常驻:每个平台的 Page 在整个服务生命周期内保持活跃
  4. 强制有头模式:便于调试和人工干预(如扫码登录)
  5. 统一管理Browser 和 Page 都由 Manager 统一管理和关闭

使用示例

基本用法

``go package main

import ( "context" "geo/internal/collect" "geo/internal/config" "github.com/gofiber/fiber/v2/log" )

func main() { cfg, _ := config.LoadConfig() ctx := context.Background()

// 创建管理器(会自动:启动浏览器 + 打开所有平台页面)
manager := collect.NewCollectManager(ctx, cfg, log.DefaultLogger())

// ⚠️ 重要:确保程序退出时关闭所有资源
defer manager.Close()

// 每次调用都使用对应的常驻 Page
params := &collect.CollectParams{
    RequestID: "req_001",
    Platform:  "wenxin",
    KeyWords:  []string{"AI", "人工智能"},
}

// 提问(使用 wenxin 的常驻 Page
result, err := manager.AskQuestion("wenxin", params, "什么是人工智能?")
if err != nil {
    log.Errorf("提问失败: %v", err)
    return
}

log.Infof("答案长度: %d", len(result.Answer))
log.Infof("分享链接: %s", result.ShareLink)

// 可以切换到其他平台(使用对应的常驻 Page
result2, _ := manager.AskQuestion("deepseek", params, "第二个问题")

}


### 登录测试示例

``go
func loginTest(manager *collect.CollectManager) {
    params := &collect.CollectParams{
        RequestID: "login_test_001",
        Platform:  "deepseek",
    }
    
    // 等待登录(使用 deepseek 的常驻 Page
    // 浏览器窗口已经打开,可以直接看到页面并进行扫码登录
    success, msg := manager.WaitLogin("deepseek", params)
    if !success {
        log.Errorf("登录失败: %s", msg)
        return
    }
    
    log.Infof("登录成功: %s", msg)
    // Cookie 已自动保存,下次可以直接使用
}

并发操作示例

``go func concurrentExample(manager *collect.CollectManager) { // 可以安全地并发调用不同平台 go func() { params := &collect.CollectParams{ RequestID: "req_001", Platform: "wenxin", } result, _ := manager.AskQuestion("wenxin", params, "问题1") log.Infof("文心一言回答: %s", result.Answer) }()

go func() {
    params := &collect.CollectParams{
        RequestID: "req_002",
        Platform:  "deepseek",
    }
    result, _ := manager.AskQuestion("deepseek", params, "问题2")
    log.Infof("DeepSeek回答: %s", result.Answer)
}()

}


## 架构细节

### 浏览器管理

- **数量**:全局只有一个浏览器实例
- **创建时机**`NewCollectManager()` 调用时立即创建
- **存储方式**`manager.browser` 字段
- **线程安全**browser 只读访问,无竞态条件
- **生命周期**:从服务启动到 `manager.Close()` 调用
- **关闭方式**:调用 `manager.Close()` 关闭浏览器和所有 Page

### Page 管理

- **数量**:每个平台一个 Page共 4 个wenxin, deepseek, doubao, qianwen
- **创建时机**`NewCollectManager()` 调用时为所有平台打开页面
- **存储方式**`map[string]*rod.Page`key 为平台名称
- **线程安全**:使用 `sync.RWMutex` 保护并发访问
- **生命周期**:从服务启动到 `manager.Close()` 调用
- **特点**Page 常驻,不会在操作后关闭

### 数据流

服务启动 ↓ NewCollectManager() ↓ 创建全局浏览器1个 ↓ 为每个平台打开 Page4个 ├─ wenxin Page → https://yiyan.baidu.com/ ├─ deepseek Page → https://chat.deepseek.com/ ├─ doubao Page → https://www.doubao.com/chat/ └─ qianwen Page → https://tongyi.aliyun.com/qianwen/ ↓ 所有 Page 保持活跃(可看到浏览器窗口)

每次请求: ↓ AskQuestion(platform, ...) ↓ 获取对应平台的常驻 Page ↓ 执行操作(输入、点击等) ↓ 返回结果Page 保持活跃,不关闭)

服务关闭: ↓ manager.Close() ↓ 关闭所有 Page ↓ 关闭浏览器


### 数据存储

ChromeDataDir/ └── global/ └── main/ # 全局浏览器用户数据(所有平台共用)

CookiesDir/ ├── wenxin/ │ └── wenxin.json # Cookie 文件(按平台隔离) ├── deepseek/ │ └── deepseek.json └── doubao/ └── doubao.json


## 关键优势

### 1. 资源占用最小化
- ✅ 只启动一个浏览器进程
- ✅ 内存占用最低
- ✅ 系统资源消耗最少

### 2. 启动速度快
- ✅ 浏览器在服务启动时已就绪
- ✅ 所有平台页面已打开
- ✅ 无需等待页面加载

### 3. 调试友好
- ✅ 有头模式,可实时观察所有平台
- ✅ 支持人工干预(扫码、验证码等)
- ✅ 便于问题排查

### 4. 会话保持
- ✅ Page 常驻,登录状态持续有效
- ✅ Cookie 自动保存和加载
- ✅ 无需重复登录

## 注意事项

### ⚠️ 必须调用 Close()

``go
manager := collect.NewCollectManager(ctx, cfg, logger)
defer manager.Close() // 确保程序退出时关闭所有资源

如果不调用 Close(),浏览器进程会残留。

⚠️ 有头模式限制

  • 所有浏览器都以有头模式运行
  • 无法切换到无头模式
  • 适合开发和调试环境

⚠️ 并发访问注意

虽然使用了 sync.RWMutex 保护 pages map但 rod 的 Page 本身不是线程安全的。建议:

  • 同一平台的请求串行执行
  • 不同平台可以并发执行
  • 避免在同一 Page 上同时执行多个操作

虽然浏览器是共用的,但 Cookie 文件按平台隔离存储,确保各平台的会话独立。

迁移指南

变化点

  1. NewCollectManager() 会立即启动浏览器并打开所有平台页面
  2. Page 是常驻的,不会在操作后关闭
  3. Collector.Close() 不再关闭 Page空实现
  4. Headless 参数被忽略,强制为 false

无需修改

  • Manager 的创建方式不变
  • 业务代码调用方式不变
  • Collector 的业务逻辑无需修改

性能对比

指标 旧架构 新架构
浏览器进程数 4个每平台1个 1个全局共用
首次启动 ~10秒 ~5秒
后续请求 ~0.1秒 ~0.1秒
内存占用
并发能力 需注意Page线程安全
资源泄漏风险

最佳实践

  1. 服务启动时初始化

    func initService() {
        manager = collect.NewCollectManager(ctx, cfg, logger)
    }
    
  2. 服务关闭时清理

    func shutdownService() {
        manager.Close()
    }
    
  3. 异常处理

    result, err := manager.AskQuestion(platform, params, question)
    if err != nil {
        log.Errorf("操作失败: %v", err)
        // Page 仍然可用,可以继续尝试
    }
    
  4. 监控建议

    • 监控浏览器进程数量应该只有1个
    • 监控内存使用情况
    • 记录每次操作的耗时
    • 监控各平台 Page 的健康状态

架构图

┌─────────────────────────────────────┐
│         CollectManager              │
│                                     │
│  ┌───────────────────────────────┐  │
│  │   Global Browser (1个)        │  │
│  │   Headless: false             │  │
│  │   Window: 1920x1080           │  │
│  └───────────────────────────────┘  │
│              │                      │
│              ├──────────────────┐   │
│              │                  │   │
│      ┌───────▼───────┐  ┌──────▼──┐│
│      │ Wenxin Page   │  │Deepseek ││
│      │ (常驻)        │  │Page     ││
│      └───────────────┘  └─────────┘│
│              │                  │   │
│      ┌───────▼───────┐  ┌──────▼──┐│
│      │ Doubao Page   │  │Qianwen  ││
│      │ (常驻)        │  │Page     ││
│      └───────────────┘  └─────────┘│
└─────────────────────────────────────┘