run.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. """
  2. 新搜索测试 - 测试 Research Agent 与 Knowledge Manager 的 IM 通信
  3. """
  4. import argparse
  5. import os
  6. import sys
  7. import asyncio
  8. from pathlib import Path
  9. os.environ.setdefault("no_proxy", "*")
  10. sys.path.insert(0, str(Path(__file__).parent.parent.parent))
  11. from dotenv import load_dotenv
  12. load_dotenv()
  13. from agent.llm.prompts import SimplePrompt
  14. from agent.core.runner import AgentRunner, RunConfig
  15. from agent.trace import FileSystemTraceStore, Trace, Message
  16. from agent.llm import create_qwen_llm_call
  17. from agent.cli import InteractiveController
  18. from agent.utils import setup_logging
  19. from agent.tools.builtin.browser.baseClass import init_browser_session, kill_browser_session
  20. from config import (
  21. RUN_CONFIG, SKILLS_DIR, TRACE_STORE_PATH, DEBUG, LOG_LEVEL, LOG_FILE,
  22. BROWSER_TYPE, HEADLESS, OUTPUT_DIR,
  23. IM_ENABLED, IM_CONTACT_ID, IM_SERVER_URL, IM_WINDOW_MODE, IM_NOTIFY_INTERVAL,
  24. KNOWLEDGE_MANAGER_ENABLED, KNOWLEDGE_MANAGER_CONTACT_ID,
  25. )
  26. async def main():
  27. parser = argparse.ArgumentParser(description="新搜索测试(KM 通信)")
  28. parser.add_argument("--trace", type=str, default=None, help="恢复 Trace ID")
  29. args = parser.parse_args()
  30. base_dir = Path(__file__).parent
  31. project_root = base_dir.parent.parent
  32. prompt_path = base_dir / "new_search.prompt"
  33. output_dir = project_root / OUTPUT_DIR
  34. output_dir.mkdir(parents=True, exist_ok=True)
  35. # 1. 日志
  36. setup_logging(level=LOG_LEVEL, file=LOG_FILE)
  37. # 2. Prompt
  38. print("1. 加载 prompt...")
  39. prompt = SimplePrompt(prompt_path)
  40. messages = prompt.build_messages(output_dir=str(output_dir))
  41. # 3. 浏览器
  42. print("2. 初始化浏览器...")
  43. await init_browser_session(browser_type=BROWSER_TYPE, headless=HEADLESS, url="https://www.google.com/", profile_name="")
  44. print(" ✅ 浏览器就绪\n")
  45. # 4. IM Client + Knowledge Manager
  46. km_task = None
  47. if IM_ENABLED:
  48. from agent.tools.builtin.im.chat import im_setup, im_open_window
  49. print("3. 初始化 IM Client...")
  50. print(f" - 身份: {IM_CONTACT_ID}, 服务器: {IM_SERVER_URL}")
  51. result = await im_setup(
  52. contact_id=IM_CONTACT_ID,
  53. server_url=IM_SERVER_URL,
  54. notify_interval=IM_NOTIFY_INTERVAL,
  55. )
  56. print(f" ✅ {result.output}")
  57. if IM_WINDOW_MODE:
  58. window_result = await im_open_window(contact_id=IM_CONTACT_ID)
  59. print(f" ✅ {window_result.output}\n")
  60. if KNOWLEDGE_MANAGER_ENABLED:
  61. print("4. 启动 Knowledge Manager...")
  62. print(f" - Contact ID: {KNOWLEDGE_MANAGER_CONTACT_ID}")
  63. try:
  64. sys.path.insert(0, str(Path(__file__).parent.parent.parent / "knowhub"))
  65. from agents.knowledge_manager import start_knowledge_manager
  66. km_task = asyncio.create_task(start_knowledge_manager(
  67. contact_id=KNOWLEDGE_MANAGER_CONTACT_ID,
  68. server_url=IM_SERVER_URL,
  69. chat_id="main"
  70. ))
  71. # 等待一下让 KM 连接完成
  72. await asyncio.sleep(2)
  73. print(f" ✅ Knowledge Manager 已启动\n")
  74. except Exception as e:
  75. print(f" ⚠️ 启动失败: {e}\n")
  76. # 5. Agent Runner
  77. print("5. 创建 Agent Runner...")
  78. prompt_model = prompt.config.get("model", None)
  79. model_for_llm = prompt_model or RUN_CONFIG.model
  80. print(f" - 模型: {model_for_llm}")
  81. store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
  82. runner = AgentRunner(
  83. trace_store=store,
  84. llm_call=create_qwen_llm_call(model=model_for_llm),
  85. skills_dir=SKILLS_DIR,
  86. debug=DEBUG,
  87. logger_name="agents.research_agent"
  88. )
  89. interactive = InteractiveController(runner=runner, store=store, enable_stdin_check=True)
  90. runner.stdin_check = interactive.check_stdin
  91. # 6. 执行
  92. task_name = RUN_CONFIG.name or base_dir.name
  93. print("=" * 60)
  94. print(f"{task_name}")
  95. print("=" * 60)
  96. print("💡 输入 'p' 暂停,'q' 退出")
  97. print("=" * 60)
  98. print()
  99. run_config = RUN_CONFIG
  100. current_trace_id = args.trace
  101. current_sequence = 0
  102. # 注入 IM 配置到 context(用于周期性通知检查)
  103. if IM_ENABLED:
  104. run_config.context["im_config"] = {
  105. "contact_id": IM_CONTACT_ID,
  106. "chat_id": "main"
  107. }
  108. if current_trace_id:
  109. run_config.trace_id = current_trace_id
  110. initial_messages = None
  111. else:
  112. initial_messages = messages
  113. try:
  114. async for item in runner.run(messages=initial_messages, config=run_config):
  115. cmd = interactive.check_stdin()
  116. if cmd == 'quit':
  117. print("\n🛑 停止...")
  118. if current_trace_id:
  119. await runner.stop(current_trace_id)
  120. break
  121. elif cmd == 'pause':
  122. print("\n⏸️ 暂停...")
  123. if current_trace_id:
  124. await runner.stop(current_trace_id)
  125. break
  126. if isinstance(item, Trace):
  127. current_trace_id = item.trace_id
  128. if item.status == "running":
  129. print(f"[Trace] 开始: {item.trace_id[:8]}...")
  130. elif item.status == "completed":
  131. print(f"\n[Trace] ✅ 完成 (消息: {item.total_messages}, 费用: ${item.total_cost:.4f})")
  132. elif item.status == "failed":
  133. print(f"\n[Trace] ❌ 失败: {item.error_message}")
  134. elif isinstance(item, Message):
  135. current_sequence = item.sequence
  136. if item.role == "assistant":
  137. content = item.content
  138. if isinstance(content, dict):
  139. text = content.get("text", "")
  140. tool_calls = content.get("tool_calls")
  141. if text and not tool_calls:
  142. print(f"\n[Response] {text}")
  143. elif text:
  144. preview = text[:150] + "..." if len(text) > 150 else text
  145. print(f"[Assistant] {preview}")
  146. elif item.role == "tool":
  147. content = item.content
  148. tool_name = content.get("tool_name", "unknown") if isinstance(content, dict) else "unknown"
  149. desc = item.description or ""
  150. if desc and desc != tool_name:
  151. desc = desc[:80]
  152. print(f"[Tool] ✅ {tool_name}: {desc}...")
  153. else:
  154. print(f"[Tool] ✅ {tool_name}")
  155. except KeyboardInterrupt:
  156. print("\n\n用户中断 (Ctrl+C)")
  157. if current_trace_id:
  158. await runner.stop(current_trace_id)
  159. finally:
  160. if km_task and not km_task.done():
  161. print("正在关闭 Knowledge Manager...")
  162. km_task.cancel()
  163. try:
  164. await km_task
  165. except asyncio.CancelledError:
  166. pass
  167. try:
  168. await kill_browser_session()
  169. except Exception:
  170. pass
  171. if current_trace_id:
  172. print(f"\nTrace ID: {current_trace_id}")
  173. if __name__ == "__main__":
  174. asyncio.run(main())