geoGo/internal/collect/README.md

620 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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文件中修改 `inputSelectors`、`sendSelectors`、`answerSelectors`
- 可以通过浏览器的开发者工具查看最新的元素选择器
### 4. 超时设置
- 登录超时: 180-300秒
- 回答超时: 120秒
- 可根据实际情况在代码中调整
### 5. 无头模式
- 开发调试时建议设置 `Headless: false`
- 生产环境可以设置 `Headless: true` 节省资源
## 扩展新平台
如果要添加新的AI平台需要
1. 创建新的Collector文件`newplatform.go`
2. 实现 `CollectorInterface` 接口
3. 继承 `BaseCollector` 基础结构
4.`interface.go``CollectorMap` 中注册
示例:
``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 隔离
虽然浏览器是共用的,但 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. **服务启动时初始化**
```go
func initService() {
manager = collect.NewCollectManager(ctx, cfg, logger)
}
```
2. **服务关闭时清理**
```go
func shutdownService() {
manager.Close()
}
```
3. **异常处理**
```go
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 ││
│ └───────────────┘ └─────────┘│
└─────────────────────────────────────┘
```