run.py 11 KB

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