run.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. """读取 prompts/system.prompt 系统提示词,不存在则返回空字符串"""
  66. prompt_path = base_dir / "prompts" / "system.prompt"
  67. if prompt_path.exists():
  68. return prompt_path.read_text(encoding="utf-8")
  69. return ""
  70. def _load_presets(base_dir: Path):
  71. """加载 presets.json 预设参数(若存在)"""
  72. presets_path = base_dir / "presets.json"
  73. if presets_path.exists():
  74. from agent.core.presets import load_presets_from_json
  75. load_presets_from_json(str(presets_path))
  76. async def main():
  77. """交互式主入口:初始化 Agent 后循环读取用户指令并流式输出执行过程"""
  78. base_dir = Path(__file__).parent
  79. setup_logging(level=LOG_LEVEL, file=LOG_FILE)
  80. system_prompt = _load_system_prompt(base_dir)
  81. _load_presets(base_dir)
  82. store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
  83. runner = AgentRunner(
  84. trace_store=store,
  85. llm_call=create_openrouter_llm_call(model=MAIN_CONFIG.model),
  86. skills_dir=SKILLS_DIR if Path(SKILLS_DIR).exists() else None,
  87. logger_name="agents.auto_put_ad_mini",
  88. )
  89. config = MAIN_CONFIG
  90. if system_prompt:
  91. config.system_prompt = system_prompt
  92. print("=" * 50)
  93. print(" 广告智能调控助手已启动")
  94. print("=" * 50)
  95. print("请输入指令(输入 'exit' 退出):")
  96. print("指令示例:")
  97. print(" - 分析广告 → 全量分析")
  98. print(" - 广告 XXXXX 降价10% → 定向操作")
  99. print(" - 广告 XXXXX 不要暂停 → 修改决策")
  100. print()
  101. step_count = 0
  102. session_trace_id = None # 会话级 trace_id,保持多轮对话记忆
  103. while True:
  104. try:
  105. user_input = input("\n> ").strip()
  106. if not user_input:
  107. continue
  108. if user_input.lower() in ("exit", "quit", "q"):
  109. print("退出系统")
  110. break
  111. messages = [{"role": "user", "content": user_input}]
  112. config.trace_id = session_trace_id
  113. print(f"\n🚀 执行: {user_input}")
  114. print("=" * 70)
  115. print(" 流程:数据拉取 → ROI计算 → 人群包基线 → 候选筛选 → AI推理 → 保存决策 → 护栏验证 → 生成报告")
  116. print("=" * 70)
  117. print()
  118. step_count = 0
  119. async for item in runner.run(messages=messages, config=config):
  120. if isinstance(item, Trace):
  121. if session_trace_id is None:
  122. session_trace_id = item.trace_id
  123. if item.status == "completed":
  124. print(f"\n✅ [Trace] 完成")
  125. elif item.status == "failed":
  126. print(f"\n❌ [Trace] 失败")
  127. session_trace_id = None # 失败后重置,下次开新会话
  128. elif isinstance(item, Message):
  129. if item.role == "assistant" and item.content:
  130. content = item.content
  131. text = content.get("text", "") if isinstance(content, dict) else content
  132. if text and text.strip():
  133. print(f"\n💭 {text}\n")
  134. elif item.role == "tool" and item.content:
  135. content = item.content
  136. if isinstance(content, dict):
  137. tool_name = content.get("tool_name", "unknown")
  138. result = content.get("result", content.get("text", str(content)))
  139. # 识别关键步骤
  140. if tool_name == "fetch_creative_data":
  141. step_count += 1
  142. print(f"\n{'='*70}")
  143. print(f"📌 步骤 {step_count}: 数据拉取")
  144. print(f"{'='*70}")
  145. elif tool_name == "calculate_roi_metrics":
  146. step_count += 1
  147. print(f"\n{'='*70}")
  148. print(f"📌 步骤 {step_count}: ROI 计算")
  149. print(f"{'='*70}")
  150. elif tool_name == "calculate_portfolio_summary":
  151. step_count += 1
  152. print(f"\n{'='*70}")
  153. print(f"📌 步骤 {step_count}: 人群包基线计算")
  154. print(f"{'='*70}")
  155. elif tool_name == "get_ads_for_review":
  156. step_count += 1
  157. print(f"\n{'='*70}")
  158. print(f"📌 步骤 {step_count}: 候选筛选(零消耗/待评估/正常运行)")
  159. print(f"{'='*70}")
  160. elif tool_name == "query_ad_detail":
  161. step_count += 1
  162. print(f"\n{'='*70}")
  163. print(f"📌 步骤 {step_count}: 查询广告详情")
  164. print(f"{'='*70}")
  165. elif tool_name == "apply_decisions":
  166. step_count += 1
  167. print(f"\n{'='*70}")
  168. print(f"📌 步骤 {step_count}: 保存智能引擎决策")
  169. print(f"{'='*70}")
  170. elif tool_name == "modify_decisions":
  171. step_count += 1
  172. print(f"\n{'='*70}")
  173. print(f"📌 步骤 {step_count}: 修改已有决策")
  174. print(f"{'='*70}")
  175. elif tool_name == "validate_decisions":
  176. step_count += 1
  177. print(f"\n{'='*70}")
  178. print(f"📌 步骤 {step_count}: 安全护栏验证")
  179. print(f"{'='*70}")
  180. elif tool_name == "execute_decisions":
  181. step_count += 1
  182. print(f"\n{'='*70}")
  183. print(f"📌 步骤 {step_count}: 分级执行")
  184. print(f"{'='*70}")
  185. elif tool_name == "send_approval_request":
  186. step_count += 1
  187. print(f"\n{'='*70}")
  188. print(f"📌 步骤 {step_count}: IM 审批请求")
  189. print(f"{'='*70}")
  190. elif tool_name == "generate_report":
  191. step_count += 1
  192. print(f"\n{'='*70}")
  193. print(f"📌 步骤 {step_count}: 生成最终报告")
  194. print(f"{'='*70}")
  195. elif tool_name == "check_execution_feedback":
  196. step_count += 1
  197. print(f"\n{'='*70}")
  198. print(f"📌 步骤 {step_count}: 执行效果检查")
  199. print(f"{'='*70}")
  200. # 打印简化结果
  201. if isinstance(result, str):
  202. text = result
  203. else:
  204. text = str(result)
  205. if len(text) > 500:
  206. text = text[:500] + "..."
  207. print(f" {text}")
  208. else:
  209. text = str(content)
  210. if len(text) > 300:
  211. text = text[:300] + "..."
  212. print(f" [工具] {text}")
  213. print("\n" + "=" * 50)
  214. print("✅ 完成")
  215. print("=" * 50)
  216. except KeyboardInterrupt:
  217. print("\n用户中断,退出")
  218. break
  219. except Exception as e:
  220. print(f"\n❌ 失败: {e}")
  221. import traceback
  222. traceback.print_exc()
  223. if __name__ == "__main__":
  224. asyncio.run(main())