run.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """
  2. 浏览器调研示例 (增强版)
  3. 功能:
  4. 1. 使用 Agent 模式进行网络调研
  5. 2. 任务结束自动关闭浏览器并杀掉进程
  6. 3. 异常安全:即使程序崩溃也能清理环境
  7. """
  8. import os
  9. import sys
  10. import asyncio
  11. from pathlib import Path
  12. # 添加项目根目录到 Python 路径
  13. sys.path.insert(0, str(Path(__file__).parent.parent.parent))
  14. from dotenv import load_dotenv
  15. load_dotenv()
  16. import logging
  17. # 配置感知日志
  18. logging.basicConfig(level=logging.WARNING) # 默认 WARNING
  19. logging.getLogger("agent.core.message_manager").setLevel(logging.INFO) # 开启感知日志
  20. logging.getLogger("tools").setLevel(logging.INFO) # 开启工具日志
  21. from agent.llm.prompts import SimplePrompt
  22. from agent.core.runner import AgentRunner, RunConfig
  23. from agent.trace import (
  24. FileSystemTraceStore,
  25. Trace,
  26. Message,
  27. )
  28. from agent.llm import create_openrouter_llm_call
  29. # 导入浏览器清理工具
  30. from agent.tools.builtin.browser.baseClass import get_browser_session,kill_browser_session,init_browser_session
  31. async def main():
  32. # 路径配置
  33. base_dir = Path(__file__).parent
  34. project_root = base_dir.parent.parent
  35. trace_dir = project_root / ".trace"
  36. prompt_path = base_dir / "test.prompt"
  37. output_dir = base_dir / "output"
  38. output_dir.mkdir(exist_ok=True)
  39. # Skills 目录
  40. skills_dir = None
  41. print("=" * 60)
  42. print("🚀 浏览器调研任务 (Agent 模式)")
  43. print("=" * 60)
  44. print()
  45. # 1. 加载 prompt
  46. print("1. 加载 prompt...")
  47. prompt = SimplePrompt(prompt_path)
  48. # 提取配置
  49. system_prompt = prompt._messages.get("system", "")
  50. user_task = prompt._messages.get("user", "")
  51. model_name = prompt.config.get('model', 'gemini-2.5-flash')
  52. temperature = float(prompt.config.get('temperature', 0.3))
  53. print(f" - 任务: {user_task[:80]}...")
  54. print(f" - 模型: {model_name}")
  55. # 2. 构建消息
  56. print("2. 构建任务消息...")
  57. messages = prompt.build_messages()
  58. # 3. 创建 Agent Runner
  59. print("3. 创建 Agent Runner...")
  60. runner = AgentRunner(
  61. trace_store=FileSystemTraceStore(base_path=str(trace_dir)),
  62. llm_call=create_openrouter_llm_call(model=f"google/{model_name}"),
  63. skills_dir=skills_dir,
  64. debug=True
  65. )
  66. final_response = ""
  67. current_trace_id = None
  68. # 4. Agent 模式执行(使用 try...finally 确保清理)
  69. try:
  70. print(f"4. 初始化云浏览器...")
  71. await init_browser_session(browser_type="cloud", headless=True)
  72. print(f"5. 启动 Agent 模式执行...")
  73. print()
  74. async for item in runner.run(
  75. messages=messages,
  76. config=RunConfig(
  77. system_prompt=system_prompt,
  78. model=f"google/{model_name}",
  79. temperature=temperature,
  80. max_iterations=20,
  81. name=user_task[:50],
  82. ),
  83. ):
  84. # 处理 Trace 对象(整体状态变化)
  85. if isinstance(item, Trace):
  86. current_trace_id = item.trace_id
  87. if item.status == "running":
  88. print(f"[Trace] 开始: {item.trace_id[:8]}")
  89. elif item.status == "completed":
  90. print(f"[Trace] 完成")
  91. print(f" - Total tokens: {item.total_tokens}")
  92. print(f" - Total cost: ${item.total_cost:.4f}")
  93. elif item.status == "failed":
  94. print(f"[Trace] 失败: {item.error_message}")
  95. # 处理 Message 对象(执行过程)
  96. elif isinstance(item, Message):
  97. if item.role == "assistant":
  98. content = item.content
  99. if isinstance(content, dict):
  100. text = content.get("text", "")
  101. tool_calls = content.get("tool_calls")
  102. if text and not tool_calls:
  103. final_response = text
  104. print(f"[Response] Agent 给出最终回复")
  105. elif text:
  106. # 增加打印长度到 300,方便观察
  107. print(f"[Assistant] {text[:300]}...")
  108. if tool_calls:
  109. for tc in tool_calls:
  110. tool_name = tc.get("function", {}).get("name", "unknown")
  111. print(f"[Tool Call] 🛠️ {tool_name}")
  112. elif item.role == "tool":
  113. content = item.content
  114. if isinstance(content, dict):
  115. tool_name = content.get("tool_name", "unknown")
  116. print(f"[Tool Result] ✅ {tool_name}")
  117. if item.description:
  118. desc = item.description[:80] if len(item.description) > 80 else item.description
  119. print(f" {desc}...")
  120. # 5. 输出结果
  121. print()
  122. print("=" * 60)
  123. print("Final Agent Response:")
  124. print("=" * 60)
  125. print(final_response)
  126. print("=" * 60)
  127. print()
  128. # 6. 保存结果
  129. output_file = output_dir / "research_result.txt"
  130. with open(output_file, 'w', encoding='utf-8') as f:
  131. f.write(final_response)
  132. print(f"✓ 结果已保存到: {output_file}")
  133. except Exception as e:
  134. print(f"\n❌ 程序运行崩溃: {str(e)}")
  135. import traceback
  136. traceback.print_exc()
  137. finally:
  138. # --- 核心逻辑:无论成功失败,必须关闭浏览器进程 ---
  139. print("\n" + "·" * 40)
  140. print("🧹 正在清理浏览器环境,关闭 CDP 会话并终止进程...")
  141. try:
  142. # 强制杀掉浏览器进程,释放容器或本地端口
  143. await kill_browser_session()
  144. print("✅ 浏览器已安全关闭。")
  145. except Exception as cleanup_err:
  146. print(f"⚠️ 清理浏览器时出现错误: {cleanup_err}")
  147. print("·" * 40 + "\n")
  148. # 7. 可视化提示
  149. if current_trace_id:
  150. print("=" * 60)
  151. print("可视化 Step Tree:")
  152. print("=" * 60)
  153. print("1. 启动 API Server: python3 api_server.py")
  154. print(f"2. 访问: http://localhost:8000/api/traces")
  155. print(f"3. Trace ID: {current_trace_id}")
  156. print("=" * 60)
  157. if __name__ == "__main__":
  158. try:
  159. asyncio.run(main())
  160. except KeyboardInterrupt:
  161. print("\n🛑 用户手动终止 (KeyboardInterrupt),正在强制退出...")