utils.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. """
  2. Agent 工具函数
  3. 提供便捷的封装方法,简化 Agent 的使用。
  4. """
  5. from typing import Dict, Any, Optional
  6. from pathlib import Path
  7. from agent.core.runner import AgentRunner, RunConfig
  8. from agent.llm.openrouter import create_openrouter_llm_call
  9. from agent.trace.models import Trace, Message
  10. from agent.trace.store import FileSystemTraceStore
  11. DEFAULT_MODEL = "google/gemini-3-flash-preview"
  12. async def run_agent(
  13. prompt: str,
  14. model: str = DEFAULT_MODEL,
  15. **run_config_kwargs
  16. ) -> str:
  17. """
  18. 通用 Agent 执行方法
  19. 传入 prompt,返回 agent 执行的返回结果(summary 字符串)。
  20. Args:
  21. prompt: 用户输入的提示词
  22. model: 模型名称,默认为 "google/gemini-3-flash-preview"
  23. **run_config_kwargs: 其他 RunConfig 参数,如:
  24. - temperature: float = 0.3
  25. - max_iterations: int = 200
  26. - tools: Optional[List[str]] = None
  27. - system_prompt: Optional[str] = None
  28. - enable_memory: bool = True
  29. - auto_execute_tools: bool = True
  30. - name: Optional[str] = None
  31. 等等
  32. Returns:
  33. str: Agent 的最终回复文本(summary)
  34. Example:
  35. ```python
  36. result = await run_agent("帮我分析一下这个代码")
  37. print(result)
  38. ```
  39. """
  40. # 创建 AgentRunner(需要 trace_store 以支持 goal 工具)
  41. trace_dir = Path(".trace")
  42. trace_dir.mkdir(exist_ok=True)
  43. trace_store = FileSystemTraceStore(base_path=str(trace_dir))
  44. runner = AgentRunner(
  45. trace_store=trace_store,
  46. llm_call=create_openrouter_llm_call(model=model),
  47. )
  48. # 构建消息
  49. messages = [{"role": "user", "content": prompt}]
  50. # 构建 RunConfig
  51. config = RunConfig(
  52. model=model,
  53. **run_config_kwargs
  54. )
  55. # 执行并输出过程日志
  56. last_assistant_text = ""
  57. final_trace: Optional[Trace] = None
  58. async for item in runner.run(messages=messages, config=config):
  59. if isinstance(item, Trace):
  60. final_trace = item
  61. if item.status == "running":
  62. print(f"\n{'='*60}")
  63. print(f"[Trace] 🚀 开始执行")
  64. print(f" Trace ID: {item.trace_id[:8]}...")
  65. print(f"{'='*60}")
  66. elif item.status == "completed":
  67. print(f"\n{'='*60}")
  68. print(f"[Trace] ✅ 执行完成")
  69. print(f" - 总消息数: {item.total_messages}")
  70. print(f" - 总 Token 数: {item.total_tokens}")
  71. print(f" - 总成本: ${item.total_cost:.4f}")
  72. print(f"{'='*60}")
  73. elif item.status == "failed":
  74. print(f"\n{'='*60}")
  75. print(f"[Trace] ❌ 执行失败")
  76. print(f" 错误信息: {item.error_message}")
  77. print(f"{'='*60}")
  78. elif item.status == "stopped":
  79. print(f"\n{'='*60}")
  80. print(f"[Trace] ⏸️ 执行已停止")
  81. print(f"{'='*60}")
  82. elif isinstance(item, Message):
  83. # 输出消息序列号和时间信息
  84. seq_info = f"[序列 {item.sequence}]"
  85. if item.parent_sequence:
  86. seq_info += f" (父序列: {item.parent_sequence})"
  87. if item.role == "assistant":
  88. content = item.content
  89. if isinstance(content, dict):
  90. text = content.get("text", "")
  91. tool_calls = content.get("tool_calls")
  92. # 输出思考过程和文本内容
  93. if text:
  94. if not tool_calls:
  95. # 最终回复
  96. last_assistant_text = text
  97. print(f"\n{seq_info} [思考过程] 🤔 Agent 思考与回复:")
  98. print(f"{'-'*60}")
  99. print(text)
  100. print(f"{'-'*60}")
  101. else:
  102. # 有工具调用的思考过程
  103. print(f"\n{seq_info} [思考过程] 🤔 Agent 思考:")
  104. print(f"{'-'*60}")
  105. print(text)
  106. print(f"{'-'*60}")
  107. # 输出工具调用详细信息
  108. if tool_calls:
  109. print(f"\n{seq_info} [工具调用] 🛠️ 调用 {len(tool_calls)} 个工具:")
  110. for idx, tc in enumerate(tool_calls, 1):
  111. func_info = tc.get("function", {})
  112. tool_name = func_info.get("name", "unknown")
  113. tool_id = tc.get("id", "unknown")
  114. tool_args = func_info.get("arguments", "")
  115. print(f" [{idx}] 工具名称: {tool_name}")
  116. print(f" 调用 ID: {tool_id[:16]}...")
  117. try:
  118. import json
  119. args_dict = json.loads(tool_args) if isinstance(tool_args, str) else tool_args
  120. args_str = json.dumps(args_dict, ensure_ascii=False, indent=2)
  121. if len(args_str) > 500:
  122. args_str = args_str[:500] + "..."
  123. print(f" 参数: {args_str}")
  124. except:
  125. args_preview = str(tool_args)[:200] + "..." if len(str(tool_args)) > 200 else str(tool_args)
  126. print(f" 参数: {args_preview}")
  127. print()
  128. elif isinstance(content, str):
  129. # 纯文本内容
  130. last_assistant_text = content
  131. print(f"\n{seq_info} [思考过程] 🤔 Agent 回复:")
  132. print(f"{'-'*60}")
  133. print(content)
  134. print(f"{'-'*60}")
  135. elif item.role == "tool":
  136. # 输出工具执行结果详细信息
  137. content = item.content
  138. tool_name = "unknown"
  139. tool_result = ""
  140. if isinstance(content, dict):
  141. tool_name = content.get("tool_name", "unknown")
  142. result = content.get("result", "")
  143. if isinstance(result, (dict, list)):
  144. import json
  145. tool_result = json.dumps(result, ensure_ascii=False, indent=2)
  146. else:
  147. tool_result = str(result)
  148. else:
  149. tool_result = str(content) if content else ""
  150. print(f"\n{seq_info} [工具结果] ✅ {tool_name} 执行完成")
  151. if item.tool_call_id:
  152. print(f" 关联调用 ID: {item.tool_call_id[:16]}...")
  153. print(f"{'-'*60}")
  154. if tool_result:
  155. if len(tool_result) > 1000:
  156. print(tool_result[:1000])
  157. print(f"\n... (结果已截断,共 {len(tool_result)} 字符)")
  158. else:
  159. print(tool_result)
  160. else:
  161. print(" (无返回结果)")
  162. print(f"{'-'*60}")
  163. # 输出工具描述和元数据(如果有)
  164. if item.description:
  165. print(f" 描述: {item.description}")
  166. if item.duration_ms:
  167. print(f" 执行耗时: {item.duration_ms}ms")
  168. if item.cost:
  169. print(f" 成本: ${item.cost:.6f}")
  170. elif item.role == "user":
  171. # 输出用户输入(可选,用于调试)
  172. if isinstance(item.content, str) and len(item.content) > 0:
  173. preview = item.content[:200] + "..." if len(item.content) > 200 else item.content
  174. print(f"\n{seq_info} [用户输入] 👤 {preview}")
  175. # 如果没有获取到最终 trace,尝试从 store 获取
  176. if not final_trace and config.trace_id and runner.trace_store:
  177. final_trace = await runner.trace_store.get_trace(config.trace_id)
  178. # 如果没有 summary,返回错误信息
  179. if not last_assistant_text:
  180. error_msg = final_trace.error_message if final_trace else "Agent 没有产生 assistant 文本结果"
  181. print(f"\n[Error] {error_msg}")
  182. return error_msg
  183. return last_assistant_text