Просмотр исходного кода

fixed: 确保可以使用云浏览器cookie登录

guantao 1 неделя назад
Родитель
Сommit
7a7f631e82

+ 87 - 5
agent/tools/builtin/browser/baseClass.py

@@ -62,6 +62,9 @@ from ....llm.openrouter import openrouter_llm_call
 # 将项目根目录添加到 Python 路径
 # 将项目根目录添加到 Python 路径
 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 
+# 配置日志
+logger = logging.getLogger(__name__)
+
 # 导入框架的工具装饰器和结果类
 # 导入框架的工具装饰器和结果类
 from agent.tools import tool, ToolResult
 from agent.tools import tool, ToolResult
 from agent.tools.builtin.browser.sync_mysql_help import mysql
 from agent.tools.builtin.browser.sync_mysql_help import mysql
@@ -1997,14 +2000,17 @@ async def browser_export_cookies(name: str = "", account: str = "") -> ToolResul
 
 
 
 
 @tool()
 @tool()
-async def browser_load_cookies(url: str, name: str = "") -> ToolResult:
+async def browser_load_cookies(url: str, name: str = "", auto_navigate: bool = True) -> ToolResult:
     """
     """
     根据目标 URL 自动查找本地 Cookie 文件,注入浏览器并导航到目标页面恢复登录态。
     根据目标 URL 自动查找本地 Cookie 文件,注入浏览器并导航到目标页面恢复登录态。
+    如果找不到 Cookie 文件,会根据 auto_navigate 参数决定是否直接导航到目标页面。
+
     重要:此工具会自动完成导航,调用前不需要先调用 browser_navigate_to_url。
     重要:此工具会自动完成导航,调用前不需要先调用 browser_navigate_to_url。
 
 
     Args:
     Args:
         url: 目标 URL(必须提供,同时用于自动匹配 Cookie 文件)
         url: 目标 URL(必须提供,同时用于自动匹配 Cookie 文件)
         name: Cookie 文件名(可选,不传则根据 URL 域名自动查找)
         name: Cookie 文件名(可选,不传则根据 URL 域名自动查找)
+        auto_navigate: 找不到 Cookie 时是否自动导航到目标页面(默认 True)
     """
     """
     try:
     try:
         browser, tools = await get_browser_session()
         browser, tools = await get_browser_session()
@@ -2017,19 +2023,95 @@ async def browser_load_cookies(url: str, name: str = "") -> ToolResult:
             from urllib.parse import urlparse
             from urllib.parse import urlparse
             domain = urlparse(url).netloc.replace("www.", "")
             domain = urlparse(url).netloc.replace("www.", "")
             if _COOKIES_DIR.exists():
             if _COOKIES_DIR.exists():
-                matches = list(_COOKIES_DIR.glob(f"{domain}*.json"))
+                # 尝试多种匹配模式
+                matches = []
+
+                # 1. 精确匹配完整域名(如 xiaohongshu.com.json)
+                exact_match = _COOKIES_DIR / f"{domain}.json"
+                if exact_match.exists():
+                    matches.append(exact_match)
+                    logger.info(f"Cookie 精确匹配成功: {exact_match.name}")
+
+                # 2. 匹配域名前缀(如 xiaohongshu.com*.json)
+                if not matches:
+                    prefix_matches = list(_COOKIES_DIR.glob(f"{domain}*.json"))
+                    if prefix_matches:
+                        matches = prefix_matches
+                        logger.info(f"Cookie 前缀匹配成功: {[m.name for m in matches]}")
+
+                # 3. 模糊匹配:提取主域名(如 xiaohongshu)
+                if not matches:
+                    main_domain = domain.split('.')[0]  # 提取第一部分
+                    fuzzy_matches = list(_COOKIES_DIR.glob(f"{main_domain}*.json"))
+                    if fuzzy_matches:
+                        matches = fuzzy_matches
+                        logger.info(f"Cookie 模糊匹配成功: {[m.name for m in matches]} (主域名: {main_domain})")
+
                 if matches:
                 if matches:
                     cookie_file = matches[0]  # 取第一个匹配的
                     cookie_file = matches[0]  # 取第一个匹配的
+                    logger.info(f"使用 Cookie 文件: {cookie_file.name}")
                 else:
                 else:
                     available = [f.stem for f in _COOKIES_DIR.glob("*.json")]
                     available = [f.stem for f in _COOKIES_DIR.glob("*.json")]
-                    return ToolResult(title="未找到 Cookie", output=f"没有匹配 {domain} 的文件,可用: {available}", error=f"无 {domain} 的 Cookie 文件")
+                    logger.warning(f"未找到匹配的 Cookie 文件。域名: {domain}, 可用: {available}")
+                    hint = f"可用的 Cookie 文件: {available}" if available else "提示:首次使用需要先手动登录,然后使用 browser_export_cookies 保存 Cookie"
+
+                    # 如果启用自动导航,直接访问目标页面
+                    if auto_navigate:
+                        await tools.navigate(url=url, browser_session=browser)
+                        await tools.wait(seconds=2, browser_session=browser)
+                        return ToolResult(
+                            title="未找到 Cookie,已导航到目标页面",
+                            output=f"没有找到 {domain} 的 Cookie 文件,已自动导航到 {url}。\n\n{hint}\n\n建议:如需保持登录态,请手动登录后使用 browser_export_cookies 保存 Cookie。",
+                            error=None,
+                            long_term_memory=f"未找到 {domain} 的 Cookie,已导航到 {url}"
+                        )
+                    else:
+                        return ToolResult(
+                            title="未找到 Cookie",
+                            output=f"没有匹配 {domain} 的 Cookie 文件。{hint}\n\n建议:使用 browser_navigate_to_url 访问 {url} 并手动登录,或使用 browser_export_cookies 保存当前 Cookie。",
+                            error=None,
+                            long_term_memory=f"未找到 {domain} 的 Cookie 文件"
+                        )
             else:
             else:
-                return ToolResult(title="未找到 Cookie", output=".cookies 目录不存在", error="Cookie 目录不存在")
+                # Cookie 目录不存在
+                if auto_navigate:
+                    await tools.navigate(url=url, browser_session=browser)
+                    await tools.wait(seconds=2, browser_session=browser)
+                    return ToolResult(
+                        title="首次使用 Cookie 功能,已导航到目标页面",
+                        output=f"这是首次使用 Cookie 功能,已自动导航到 {url}。\n\n建议:手动完成登录后,使用 browser_export_cookies 保存 Cookie 供下次使用。",
+                        error=None,
+                        long_term_memory="首次使用 Cookie 功能,已导航到目标页面"
+                    )
+                else:
+                    return ToolResult(
+                        title="Cookie 目录不存在",
+                        output=f"这是首次使用 Cookie 功能。建议:\n1. 使用 browser_navigate_to_url 访问 {url}\n2. 手动完成登录\n3. 使用 browser_export_cookies 保存 Cookie 供下次使用",
+                        error=None,
+                        long_term_memory="Cookie 目录不存在,这是首次使用"
+                    )
         else:
         else:
             cookie_file = _COOKIES_DIR / f"{name}.json"
             cookie_file = _COOKIES_DIR / f"{name}.json"
             if not cookie_file.exists():
             if not cookie_file.exists():
                 available = [f.stem for f in _COOKIES_DIR.glob("*.json")] if _COOKIES_DIR.exists() else []
                 available = [f.stem for f in _COOKIES_DIR.glob("*.json")] if _COOKIES_DIR.exists() else []
-                return ToolResult(title="文件不存在", output=f"可用: {available}", error=f"未找到 .cookies/{name}.json")
+                hint = f"可用的 Cookie 文件: {available}" if available else "提示:使用 browser_export_cookies 保存 Cookie"
+
+                if auto_navigate:
+                    await tools.navigate(url=url, browser_session=browser)
+                    await tools.wait(seconds=2, browser_session=browser)
+                    return ToolResult(
+                        title="Cookie 文件不存在,已导航到目标页面",
+                        output=f"未找到 .cookies/{name}.json,已自动导航到 {url}。\n\n{hint}",
+                        error=None,
+                        long_term_memory=f"未找到 {name}.json,已导航到目标页面"
+                    )
+                else:
+                    return ToolResult(
+                        title="Cookie 文件不存在",
+                        output=f"未找到 .cookies/{name}.json。{hint}",
+                        error=None,
+                        long_term_memory=f"未找到 {name}.json Cookie 文件"
+                    )
 
 
         cookies = json.loads(cookie_file.read_text(encoding="utf-8"))
         cookies = json.loads(cookie_file.read_text(encoding="utf-8"))
 
 

+ 106 - 0
examples/research/README.md

@@ -0,0 +1,106 @@
+# 浏览器调研示例
+
+支持云浏览器和本地浏览器两种模式的 Agent 自动化调研工具。
+
+## 功能特性
+
+1. **Agent 自动化调研** - 使用 LLM 驱动的 Agent 自动执行浏览器操作
+2. **手动接管模式** - 运行中随时按 [Enter] 键暂停 Agent,手动操作浏览器
+3. **自动清理** - 无论成功或崩溃,均安全关闭浏览器进程
+4. **灵活切换** - 支持云浏览器和本地浏览器模式切换
+
+## 浏览器模式配置
+
+### 切换方法
+
+编辑 `run.py` 文件顶部的配置变量:
+
+```python
+# ===== 浏览器模式配置 =====
+BROWSER_TYPE = "cloud"  # 可选: "cloud" 或 "local"
+HEADLESS = False        # 是否无头模式运行
+```
+
+### 模式说明
+
+#### 云浏览器模式 (`"cloud"`)
+- ✅ 不占用本地资源
+- ✅ 适合生产环境
+- ✅ 可在无 GUI 的服务器上运行
+- ⚠️ 需要配置 browser-use 云服务
+- ⚠️ 可能需要 API 密钥
+
+#### 本地浏览器模式 (`"local"`)
+- ✅ 速度更快
+- ✅ 支持可视化调试
+- ✅ 无需额外配置
+- ⚠️ 需要本地安装 Chrome
+- ⚠️ 占用本地资源
+
+## 使用方法
+
+### 1. 准备环境
+
+```bash
+# 安装依赖
+pip install -r requirements.txt
+
+# 配置环境变量(复制 .env.example 为 .env)
+cp .env.example .env
+# 编辑 .env 文件,配置 OPENROUTER_API_KEY 等
+```
+
+### 2. 配置任务
+
+编辑 `test.prompt` 文件,设置调研任务:
+
+```
+---
+model: gemini-3-flash-preview
+temperature: 0.3
+---
+
+[system]
+你是一个专业的网络调研助手...
+
+[user]
+请帮我调研...
+```
+
+### 3. 运行
+
+```bash
+python run.py
+```
+
+### 4. 手动接管(可选)
+
+运行过程中,如需手动操作浏览器(如登录、验证码等):
+
+1. 按下 **[Enter]** 键
+2. Agent 会在完成当前动作后暂停
+3. 在浏览器窗口完成必要操作
+4. 再次按 **[Enter]** 或点击页面交互按钮继续
+
+## 输出结果
+
+- 调研结果保存在 `output/` 目录
+- Trace 数据保存在项目根目录的 `.trace/` 目录
+- 可通过可视化面板查看详细执行过程
+
+## 故障排除
+
+### 云浏览器连接失败
+- 检查 browser-use 云服务配置
+- 确认 API 密钥正确
+- 检查网络连接
+
+### 本地浏览器启动失败
+- 确认已安装 Chrome 浏览器
+- 检查 Chrome 路径是否正确
+- 尝试关闭其他 Chrome 实例
+
+### Agent 执行异常
+- 查看终端日志输出
+- 检查 `.trace/` 目录中的 trace 数据
+- 调整 `test.prompt` 中的任务描述

+ 191 - 0
examples/research/TROUBLESHOOTING.md

@@ -0,0 +1,191 @@
+# 故障排除指南
+
+## Cookie 文件相关问题
+
+### 问题:提示"没有 Cookie 文件"或"Cookie 目录不存在"
+
+#### 原因
+这不是云浏览器特有的问题,而是首次使用时的正常情况:
+
+1. Agent 在执行某些任务时可能会尝试加载之前保存的 Cookie(用于保持登录态)
+2. 如果这是第一次运行,`.cache/.cookies` 目录不存在或没有对应的 Cookie 文件
+3. 工具会给出友好提示,并自动导航到目标页面
+
+#### 解决方案
+
+**方案 1:让 Agent 自动处理(推荐)**
+
+从 v2.0 开始,`browser_load_cookies` 工具已经优化:
+- 找不到 Cookie 时会自动导航到目标页面
+- Agent 可以继续执行任务,不会中断
+- 你可以手动登录后,Agent 会继续后续操作
+
+**方案 2:预先保存 Cookie**
+
+如果你需要频繁访问需要登录的网站:
+
+1. 首次运行时手动登录
+2. 在终端按 `[Enter]` 暂停 Agent
+3. 在浏览器中完成登录
+4. 让 Agent 继续,它会自动调用 `browser_export_cookies` 保存 Cookie
+5. 下次运行时会自动加载 Cookie,无需重复登录
+
+**方案 3:手动保存 Cookie**
+
+```bash
+# 1. 启动浏览器并访问目标网站
+# 2. 手动登录
+# 3. 在 Python 中执行:
+
+from agent.tools.builtin.browser.baseClass import browser_export_cookies
+
+# 保存当前页面的 Cookie
+await browser_export_cookies(name="example.com")
+
+# Cookie 会保存到 .cache/.cookies/example.com.json
+```
+
+### 问题:云浏览器和本地浏览器的 Cookie 是否共享?
+
+**是的**,Cookie 文件存储在本地文件系统(`.cache/.cookies/`),与浏览器类型无关:
+
+- 云浏览器保存的 Cookie 可以在本地浏览器中使用
+- 本地浏览器保存的 Cookie 可以在云浏览器中使用
+- 切换浏览器模式不会丢失已保存的 Cookie
+
+### Cookie 文件位置
+
+```
+项目根目录/
+  └── .cache/
+      └── .cookies/
+          ├── example.com.json
+          ├── github.com.json
+          └── ...
+```
+
+### Cookie 文件格式
+
+Cookie 文件使用 JSON 格式,符合 Chrome DevTools Protocol (CDP) 规范:
+
+```json
+[
+  {
+    "name": "session_id",
+    "value": "abc123...",
+    "domain": ".example.com",
+    "path": "/",
+    "expires": 1234567890,
+    "httpOnly": true,
+    "secure": true
+  }
+]
+```
+
+## 云浏览器特定问题
+
+### 问题:云浏览器连接失败
+
+#### 可能原因
+1. browser-use 云服务未配置
+2. API 密钥错误或过期
+3. 网络连接问题
+4. 云服务配额用尽
+
+#### 解决方案
+
+1. **检查配置**
+   ```bash
+   # 查看 .env 文件
+   cat .env
+
+   # 确认包含必要的配置(如果需要)
+   # BROWSER_USE_API_KEY=your_key_here
+   ```
+
+2. **切换到本地浏览器**
+   ```python
+   # 编辑 run.py
+   BROWSER_TYPE = "local"  # 改为 local
+   ```
+
+3. **查看详细日志**
+   ```python
+   # 在 run.py 中启用调试日志
+   logging.basicConfig(level=logging.DEBUG)
+   ```
+
+### 问题:云浏览器速度慢
+
+#### 原因
+- 网络延迟
+- 云服务器负载高
+- 需要传输大量数据(如图片、视频)
+
+#### 解决方案
+
+1. **使用本地浏览器**(如果可以)
+   ```python
+   BROWSER_TYPE = "local"
+   ```
+
+2. **启用无头模式**(减少渲染开销)
+   ```python
+   HEADLESS = True
+   ```
+
+3. **优化任务**
+   - 减少不必要的页面导航
+   - 使用 API 代替浏览器操作(如果可能)
+
+## 其他常见问题
+
+### 问题:Agent 卡住不动
+
+#### 可能原因
+1. 等待页面加载超时
+2. 等待元素出现超时
+3. 网络请求阻塞
+
+#### 解决方案
+
+1. **手动接管**
+   - 按 `[Enter]` 键暂停 Agent
+   - 检查浏览器状态
+   - 手动完成操作后继续
+
+2. **调整超时设置**
+   ```python
+   # 在 test.prompt 中添加
+   [system]
+   如果页面加载超过 30 秒,请跳过并继续下一步
+   ```
+
+### 问题:浏览器进程未正确关闭
+
+#### 解决方案
+
+```bash
+# Windows
+taskkill /F /IM chrome.exe
+
+# Linux/Mac
+pkill -9 chrome
+```
+
+或者在代码中确保清理:
+
+```python
+from agent.tools.builtin.browser.baseClass import kill_browser_session
+
+# 在 finally 块中调用
+await kill_browser_session()
+```
+
+## 获取帮助
+
+如果以上方案都无法解决问题:
+
+1. 查看完整日志输出
+2. 检查 `.trace/` 目录中的 trace 数据
+3. 在 GitHub 提交 issue:https://github.com/anthropics/claude-code/issues

+ 31 - 9
examples/research/run.py

@@ -1,12 +1,23 @@
 """
 """
-浏览器调研示例 (交互增强版)
+浏览器调研示例 (支持云浏览器/本地浏览器切换)
 
 
 功能:
 功能:
 1. Agent 模式自动化调研
 1. Agent 模式自动化调研
 2. 手动接管:随时按 [Enter] 键暂停 Agent 并手动操作浏览器
 2. 手动接管:随时按 [Enter] 键暂停 Agent 并手动操作浏览器
 3. 自动清理:无论成功或崩溃,均安全关闭浏览器进程
 3. 自动清理:无论成功或崩溃,均安全关闭浏览器进程
+4. 灵活切换:通过配置变量选择云浏览器或本地浏览器
+
+浏览器模式配置:
+- 修改下方 BROWSER_TYPE 变量来切换模式
+- "cloud": 云浏览器模式,不占用本地资源,需要配置 browser-use 云服务
+- "local": 本地浏览器模式,在本地运行 Chrome,速度更快,支持可视化调试
 """
 """
 
 
+# ===== 浏览器模式配置 =====
+# 可选值: "cloud" (云浏览器) 或 "local" (本地浏览器)
+BROWSER_TYPE = "cloud"  # 修改这里来切换浏览器模式
+HEADLESS = False  # 是否无头模式运行
+
 import os
 import os
 import sys
 import sys
 import asyncio
 import asyncio
@@ -32,7 +43,7 @@ from agent.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner, RunConfig
 from agent.core.runner import AgentRunner, RunConfig
 from agent.trace import FileSystemTraceStore, Trace, Message
 from agent.trace import FileSystemTraceStore, Trace, Message
 from agent.llm import create_openrouter_llm_call
 from agent.llm import create_openrouter_llm_call
-from agent.tools.builtin.browser.baseClass import kill_browser_session
+from agent.tools.builtin.browser.baseClass import kill_browser_session, init_browser_session
 
 
 # ===== 全局交互控制 =====
 # ===== 全局交互控制 =====
 pause_event = asyncio.Event()
 pause_event = asyncio.Event()
@@ -62,6 +73,7 @@ async def main():
 
 
     print("=" * 60)
     print("=" * 60)
     print("🚀 交互式浏览器调研 Agent")
     print("🚀 交互式浏览器调研 Agent")
+    print(f"🌐 浏览器模式: {'云浏览器 (Cloud)' if BROWSER_TYPE == 'cloud' else '本地浏览器 (Local)'}")
     print("👉 操作指南:")
     print("👉 操作指南:")
     print("   - 运行中随时按下 [Enter] 键进入手动接管模式")
     print("   - 运行中随时按下 [Enter] 键进入手动接管模式")
     print("   - 在浏览器完成操作后,点击页面上的 'Done' 或回车返回")
     print("   - 在浏览器完成操作后,点击页面上的 'Done' 或回车返回")
@@ -77,16 +89,26 @@ async def main():
 
 
     messages = prompt.build_messages()
     messages = prompt.build_messages()
 
 
-    # 3. 初始化 Runner
+    # 3. 初始化浏览器会话
+    browser_mode_name = "云浏览器" if BROWSER_TYPE == "cloud" else "本地浏览器"
+    print(f"🌐 正在初始化{browser_mode_name}...")
+    await init_browser_session(
+        browser_type=BROWSER_TYPE,
+        headless=HEADLESS,
+        url="about:blank"
+    )
+    print(f"✅ {browser_mode_name}初始化完成\n")
+
+    # 4. 初始化 Runner
     # 注意:确保你的 openrouter 配置正确
     # 注意:确保你的 openrouter 配置正确
     runner = AgentRunner(
     runner = AgentRunner(
         trace_store=FileSystemTraceStore(base_path=str(trace_dir)),
         trace_store=FileSystemTraceStore(base_path=str(trace_dir)),
         llm_call=create_openrouter_llm_call(model=f"google/{model_name}"),
         llm_call=create_openrouter_llm_call(model=f"google/{model_name}"),
         skills_dir=None,
         skills_dir=None,
-        debug=True 
+        debug=True
     )
     )
 
 
-    # 4. 启动监听任务
+    # 5. 启动监听任务
     interrupt_task = asyncio.create_task(listen_for_interrupt())
     interrupt_task = asyncio.create_task(listen_for_interrupt())
     
     
     final_response = ""
     final_response = ""
@@ -164,18 +186,18 @@ async def main():
     finally:
     finally:
         # 停止监听协程
         # 停止监听协程
         interrupt_task.cancel()
         interrupt_task.cancel()
-        
-        # 5. 强制清理浏览器环境
+
+        # 6. 强制清理浏览器环境
         print("\n" + "·" * 40)
         print("\n" + "·" * 40)
         print("🧹 正在执行环境清理...")
         print("🧹 正在执行环境清理...")
         try:
         try:
             await kill_browser_session()
             await kill_browser_session()
-            print("✨ 浏览器进程已安全终止。")
+            print(f"✨ {browser_mode_name}进程已安全终止。")
         except Exception as err:
         except Exception as err:
             print(f"❌ 清理失败: {err}")
             print(f"❌ 清理失败: {err}")
         print("·" * 40 + "\n")
         print("·" * 40 + "\n")
 
 
-    # 6. 结果展示
+    # 7. 结果展示
     if current_trace_id:
     if current_trace_id:
         print(f"🔍 任务 Trace ID: {current_trace_id}")
         print(f"🔍 任务 Trace ID: {current_trace_id}")
         print(f"📊 访问可视化面板查看详情。")
         print(f"📊 访问可视化面板查看详情。")

+ 1 - 1
examples/research/test.prompt

@@ -7,5 +7,5 @@ $system$
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 
 
 $user$
 $user$
-登录一下小红书,搜索一下摄影.没有cookie文件,也尝试一下
+登录一下小红书,在小红书内的搜索框搜索一下摄影.使用load_cookies来登录