|
|
||
|---|---|---|
| .. | ||
| README.md | ||
| base.go | ||
| deepseek.go | ||
| doubao.go | ||
| interface.go | ||
| manager.go | ||
| qianwen.go | ||
| utils.go | ||
| utils_test.go | ||
| wenxin.go | ||
README.md
AI平台收集功能使用说明
概述
internal/collect 模块提供了访问多个AI平台并进行问答的功能,目前支持以下平台:
- 文心一言 (wenxin) - 百度AI助手
- DeepSeek (deepseek) - 深度求索AI
- 豆包 (doubao) - 字节跳动AI助手
- 通义千问 (qianwen) - 阿里云AI助手
架构设计
核心组件
-
CollectorInterface - 收集器接口
WaitLogin() (bool, string)- 等待登录AskQuestion(question string) (string, error)- 提问并获取答案
-
BaseCollector - 基础收集器
- 浏览器驱动管理
- Cookie管理(保存/加载)
- 页面操作工具方法
-
CollectManager - 收集管理器
- 统一管理不同平台的收集器
- 提供便捷的API调用
-
平台实现
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",
},
}
工作流程
- 初始化浏览器 - 启动Chrome浏览器实例
- 加载Cookie - 从本地文件加载之前的登录状态
- 检查登录 - 验证是否已登录
- 导航到聊天页面 - 打开AI平台的对话界面
- 输入问题 - 在输入框中输入问题
- 点击发送 - 触发AI回答
- 等待回答 - 等待AI生成完整答案
- 提取答案 - 从页面中提取回答内容
- 返回结果 - 将答案返回给调用者
注意事项
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平台,需要:
- 创建新的Collector文件,如
newplatform.go - 实现
CollectorInterface接口 - 继承
BaseCollector基础结构 - 在
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。
核心特性
- 单一浏览器:所有平台共用同一个浏览器实例
- 启动时预创建:服务启动时立即创建浏览器并打开所有平台的页面
- Page 常驻:每个平台的 Page 在整个服务生命周期内保持活跃
- 强制有头模式:便于调试和人工干预(如扫码登录)
- 统一管理: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个) ↓ 为每个平台打开 Page(4个) ├─ 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 文件按平台隔离存储,确保各平台的会话独立。
迁移指南
变化点
- ✅
NewCollectManager()会立即启动浏览器并打开所有平台页面 - ✅ Page 是常驻的,不会在操作后关闭
- ✅
Collector.Close()不再关闭 Page(空实现) - ✅
Headless参数被忽略,强制为false
无需修改
- ❌ Manager 的创建方式不变
- ❌ 业务代码调用方式不变
- ❌ Collector 的业务逻辑无需修改
性能对比
| 指标 | 旧架构 | 新架构 |
|---|---|---|
| 浏览器进程数 | 4个(每平台1个) | 1个(全局共用) |
| 首次启动 | ~10秒 | ~5秒 |
| 后续请求 | ~0.1秒 | ~0.1秒 |
| 内存占用 | 高 | 低 |
| 并发能力 | 中 | 中(需注意Page线程安全) |
| 资源泄漏风险 | 低 | 低 |
最佳实践
-
服务启动时初始化
func initService() { manager = collect.NewCollectManager(ctx, cfg, logger) } -
服务关闭时清理
func shutdownService() { manager.Close() } -
异常处理
result, err := manager.AskQuestion(platform, params, question) if err != nil { log.Errorf("操作失败: %v", err) // Page 仍然可用,可以继续尝试 } -
监控建议
- 监控浏览器进程数量(应该只有1个)
- 监控内存使用情况
- 记录每次操作的耗时
- 监控各平台 Page 的健康状态
架构图
┌─────────────────────────────────────┐
│ CollectManager │
│ │
│ ┌───────────────────────────────┐ │
│ │ Global Browser (1个) │ │
│ │ Headless: false │ │
│ │ Window: 1920x1080 │ │
│ └───────────────────────────────┘ │
│ │ │
│ ├──────────────────┐ │
│ │ │ │
│ ┌───────▼───────┐ ┌──────▼──┐│
│ │ Wenxin Page │ │Deepseek ││
│ │ (常驻) │ │Page ││
│ └───────────────┘ └─────────┘│
│ │ │ │
│ ┌───────▼───────┐ ┌──────▼──┐│
│ │ Doubao Page │ │Qianwen ││
│ │ (常驻) │ │Page ││
│ └───────────────┘ └─────────┘│
└─────────────────────────────────────┘