run.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. """
  2. 广告调控 Agent — auto_put_ad_mini 入口
  3. 运行方式:
  4. cd /Users/liulidong/project/agent/Agent
  5. python examples/auto_put_ad_mini/run.py
  6. """
  7. import asyncio
  8. import os
  9. import sys
  10. from pathlib import Path
  11. # 代理设置
  12. os.environ.setdefault("HTTP_PROXY", "http://127.0.0.1:29758")
  13. os.environ.setdefault("HTTPS_PROXY", "http://127.0.0.1:29758")
  14. # 添加项目根目录到 Python 路径
  15. sys.path.insert(0, str(Path(__file__).parent.parent.parent))
  16. from dotenv import load_dotenv
  17. load_dotenv()
  18. from agent.core.runner import AgentRunner
  19. from agent.trace import FileSystemTraceStore, Trace, Message
  20. from agent.llm import create_openrouter_llm_call
  21. from agent.utils import setup_logging
  22. # 导入配置
  23. from examples.auto_put_ad_mini.config import (
  24. MAIN_CONFIG, SKILLS_DIR, TRACE_STORE_PATH, LOG_LEVEL, LOG_FILE,
  25. )
  26. # 导入自定义工具(触发 @tool 注册)
  27. from examples.auto_put_ad_mini.tools.data_query import fetch_creative_data, merge_creative_data
  28. from examples.auto_put_ad_mini.tools.roi_calculator import calculate_roi_metrics
  29. from examples.auto_put_ad_mini.tools.portfolio_metrics import calculate_portfolio_summary
  30. from examples.auto_put_ad_mini.tools.ad_decision import (
  31. get_ads_for_review, apply_decisions,
  32. query_ad_detail, modify_decisions,
  33. )
  34. from examples.auto_put_ad_mini.tools.report_generator import generate_report
  35. from examples.auto_put_ad_mini.tools.guardrails import validate_decisions
  36. from examples.auto_put_ad_mini.tools.execution_engine import execute_decisions, check_execution_feedback
  37. from examples.auto_put_ad_mini.tools.im_approval import send_approval_request, check_approval_status, send_feishu_text_message
  38. async def init_project_env(messages=None):
  39. """供 api_server 可视化调用:返回 (runner, messages, config)"""
  40. base_dir = Path(__file__).parent
  41. system_prompt = _load_system_prompt(base_dir)
  42. _load_presets(base_dir)
  43. store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
  44. runner = AgentRunner(
  45. trace_store=store,
  46. llm_call=create_openrouter_llm_call(model=MAIN_CONFIG.model),
  47. skills_dir=SKILLS_DIR if Path(SKILLS_DIR).exists() else None,
  48. logger_name="agents.auto_put_ad_mini",
  49. )
  50. config = MAIN_CONFIG
  51. if system_prompt:
  52. config.system_prompt = system_prompt
  53. if not messages:
  54. messages = [{"role": "user", "content": "分析广告"}]
  55. if system_prompt:
  56. has_system = any(m.get("role") == "system" for m in messages)
  57. if not has_system:
  58. messages = [{"role": "system", "content": system_prompt}] + messages
  59. return runner, messages, config
  60. def _load_system_prompt(base_dir: Path) -> str:
  61. prompt_path = base_dir / "prompts" / "system.prompt"
  62. if prompt_path.exists():
  63. return prompt_path.read_text(encoding="utf-8")
  64. return ""
  65. def _load_presets(base_dir: Path):
  66. presets_path = base_dir / "presets.json"
  67. if presets_path.exists():
  68. from agent.core.presets import load_presets_from_json
  69. load_presets_from_json(str(presets_path))
  70. async def main():
  71. base_dir = Path(__file__).parent
  72. setup_logging(level=LOG_LEVEL, file=LOG_FILE)
  73. system_prompt = _load_system_prompt(base_dir)
  74. _load_presets(base_dir)
  75. store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
  76. runner = AgentRunner(
  77. trace_store=store,
  78. llm_call=create_openrouter_llm_call(model=MAIN_CONFIG.model),
  79. skills_dir=SKILLS_DIR if Path(SKILLS_DIR).exists() else None,
  80. logger_name="agents.auto_put_ad_mini",
  81. )
  82. config = MAIN_CONFIG
  83. if system_prompt:
  84. config.system_prompt = system_prompt
  85. print("=" * 50)
  86. print(" 广告智能调控助手已启动")
  87. print("=" * 50)
  88. print("请输入指令(输入 'exit' 退出):")
  89. print("指令示例:")
  90. print(" - 分析广告 → 全量分析")
  91. print(" - 广告 XXXXX 降价10% → 定向操作")
  92. print(" - 广告 XXXXX 不要暂停 → 修改决策")
  93. print()
  94. step_count = 0
  95. session_trace_id = None # 会话级 trace_id,保持多轮对话记忆
  96. while True:
  97. try:
  98. user_input = input("\n> ").strip()
  99. if not user_input:
  100. continue
  101. if user_input.lower() in ("exit", "quit", "q"):
  102. print("退出系统")
  103. break
  104. messages = [{"role": "user", "content": user_input}]
  105. config.trace_id = session_trace_id
  106. print(f"\n🚀 执行: {user_input}")
  107. print("=" * 70)
  108. print(" 流程:数据拉取 → ROI计算 → 人群包基线 → 候选筛选 → AI推理 → 保存决策 → 护栏验证 → 生成报告")
  109. print("=" * 70)
  110. print()
  111. step_count = 0
  112. async for item in runner.run(messages=messages, config=config):
  113. if isinstance(item, Trace):
  114. if session_trace_id is None:
  115. session_trace_id = item.trace_id
  116. if item.status == "completed":
  117. print(f"\n✅ [Trace] 完成")
  118. elif item.status == "failed":
  119. print(f"\n❌ [Trace] 失败")
  120. session_trace_id = None # 失败后重置,下次开新会话
  121. elif isinstance(item, Message):
  122. if item.role == "assistant" and item.content:
  123. content = item.content
  124. text = content.get("text", "") if isinstance(content, dict) else content
  125. if text and text.strip():
  126. print(f"\n💭 {text}\n")
  127. elif item.role == "tool" and item.content:
  128. content = item.content
  129. if isinstance(content, dict):
  130. tool_name = content.get("tool_name", "unknown")
  131. result = content.get("result", content.get("text", str(content)))
  132. # 识别关键步骤
  133. if tool_name == "fetch_creative_data":
  134. step_count += 1
  135. print(f"\n{'='*70}")
  136. print(f"📌 步骤 {step_count}: 数据拉取")
  137. print(f"{'='*70}")
  138. elif tool_name == "calculate_roi_metrics":
  139. step_count += 1
  140. print(f"\n{'='*70}")
  141. print(f"📌 步骤 {step_count}: ROI 计算")
  142. print(f"{'='*70}")
  143. elif tool_name == "calculate_portfolio_summary":
  144. step_count += 1
  145. print(f"\n{'='*70}")
  146. print(f"📌 步骤 {step_count}: 人群包基线计算")
  147. print(f"{'='*70}")
  148. elif tool_name == "get_ads_for_review":
  149. step_count += 1
  150. print(f"\n{'='*70}")
  151. print(f"📌 步骤 {step_count}: 候选筛选(零消耗/待评估/正常运行)")
  152. print(f"{'='*70}")
  153. elif tool_name == "query_ad_detail":
  154. step_count += 1
  155. print(f"\n{'='*70}")
  156. print(f"📌 步骤 {step_count}: 查询广告详情")
  157. print(f"{'='*70}")
  158. elif tool_name == "apply_decisions":
  159. step_count += 1
  160. print(f"\n{'='*70}")
  161. print(f"📌 步骤 {step_count}: 保存智能引擎决策")
  162. print(f"{'='*70}")
  163. elif tool_name == "modify_decisions":
  164. step_count += 1
  165. print(f"\n{'='*70}")
  166. print(f"📌 步骤 {step_count}: 修改已有决策")
  167. print(f"{'='*70}")
  168. elif tool_name == "validate_decisions":
  169. step_count += 1
  170. print(f"\n{'='*70}")
  171. print(f"📌 步骤 {step_count}: 安全护栏验证")
  172. print(f"{'='*70}")
  173. elif tool_name == "execute_decisions":
  174. step_count += 1
  175. print(f"\n{'='*70}")
  176. print(f"📌 步骤 {step_count}: 分级执行")
  177. print(f"{'='*70}")
  178. elif tool_name == "send_approval_request":
  179. step_count += 1
  180. print(f"\n{'='*70}")
  181. print(f"📌 步骤 {step_count}: IM 审批请求")
  182. print(f"{'='*70}")
  183. elif tool_name == "generate_report":
  184. step_count += 1
  185. print(f"\n{'='*70}")
  186. print(f"📌 步骤 {step_count}: 生成最终报告")
  187. print(f"{'='*70}")
  188. elif tool_name == "check_execution_feedback":
  189. step_count += 1
  190. print(f"\n{'='*70}")
  191. print(f"📌 步骤 {step_count}: 执行效果检查")
  192. print(f"{'='*70}")
  193. # 打印简化结果
  194. if isinstance(result, str):
  195. text = result
  196. else:
  197. text = str(result)
  198. if len(text) > 500:
  199. text = text[:500] + "..."
  200. print(f" {text}")
  201. else:
  202. text = str(content)
  203. if len(text) > 300:
  204. text = text[:300] + "..."
  205. print(f" [工具] {text}")
  206. print("\n" + "=" * 50)
  207. print("✅ 完成")
  208. print("=" * 50)
  209. except KeyboardInterrupt:
  210. print("\n用户中断,退出")
  211. break
  212. except Exception as e:
  213. print(f"\n❌ 失败: {e}")
  214. import traceback
  215. traceback.print_exc()
  216. if __name__ == "__main__":
  217. asyncio.run(main())