run.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #!/usr/bin/env python3
  2. """find_agent 本地入口。
  3. 支持两种模式:
  4. 1. 按需求 id(demand_grade.id)跑完整找视频链路;
  5. 2. 无参数时进入交互式手动输入(调试 Prompt / 工具用)。
  6. """
  7. from __future__ import annotations
  8. import argparse
  9. import json
  10. import logging
  11. import sys
  12. from typing import Any
  13. from agents.find_agent import create_find_agent, run_find_agent
  14. from agents.find_agent.demand_run import (
  15. discover_videos_for_demand,
  16. load_find_demand_context_by_id,
  17. serialize_find_demand_context,
  18. )
  19. def _configure_logging(verbose: bool) -> None:
  20. level = logging.DEBUG if verbose else logging.INFO
  21. logging.basicConfig(
  22. level=level,
  23. format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
  24. )
  25. def _print_json(payload: dict[str, Any]) -> None:
  26. print(json.dumps(payload, ensure_ascii=False, indent=2))
  27. def run_by_demand_id(
  28. demand_grade_id: int,
  29. *,
  30. biz_dt: str | None = None,
  31. force: bool = False,
  32. dry_run: bool = False,
  33. ) -> dict[str, Any]:
  34. """按 demand_grade.id 加载上下文并执行 find_agent。"""
  35. ctx = load_find_demand_context_by_id(demand_grade_id, biz_dt=biz_dt)
  36. if ctx is None:
  37. raise SystemExit(
  38. f"未找到可执行上下文:demand_grade_id={demand_grade_id}"
  39. + (f", biz_dt={biz_dt}" if biz_dt else "")
  40. + "(记录不存在,或无有效拓展点位)"
  41. )
  42. summary = serialize_find_demand_context(ctx)
  43. print("=== find_agent 任务上下文 ===")
  44. _print_json(summary)
  45. if dry_run:
  46. print("\n[dry-run] 已跳过 Agent 执行")
  47. return {"dry_run": True, "context": summary}
  48. print(
  49. f"\n开始执行 find_agent:"
  50. f"demand_grade_id={ctx.demand_grade_id} "
  51. f"demand={ctx.demand_name} grade={ctx.grade} force={force}"
  52. )
  53. execution = discover_videos_for_demand(ctx, force=force)
  54. result: dict[str, Any] = {
  55. "context": summary,
  56. "run_id": execution.run_id,
  57. "skipped": execution.skipped,
  58. "skip_reason": execution.skip_reason,
  59. "succeeded": execution.succeeded,
  60. "business_outcome": execution.business_outcome,
  61. "goal_met": execution.goal_met,
  62. "valid_primary_count": execution.valid_primary_count,
  63. "failure_reason": execution.failure_reason,
  64. }
  65. if execution.agent_result is not None:
  66. result["agent"] = {
  67. "iterations": execution.agent_result.iterations,
  68. "tool_calls_made": execution.agent_result.tool_calls_made,
  69. "content": execution.agent_result.content,
  70. }
  71. print("\n=== 执行结果 ===")
  72. _print_json(result)
  73. return result
  74. def run_interactive() -> None:
  75. """交互式手动输入 run_id + 用户消息。"""
  76. agent = create_find_agent()
  77. print(f"find_agent ready | model={agent.model}")
  78. print(f"tools: {agent.tools.list_tools()}")
  79. print()
  80. while True:
  81. try:
  82. run_id = input("run_id> ").strip()
  83. if not run_id or run_id.lower() in ("exit", "quit", "q"):
  84. break
  85. user_input = input("You> ").strip()
  86. except (EOFError, KeyboardInterrupt):
  87. print("\nBye.")
  88. break
  89. if not user_input or user_input.lower() in ("exit", "quit", "q"):
  90. break
  91. result = run_find_agent(user_input, run_id=run_id)
  92. print(f"\nAgent> {result.content}\n")
  93. def build_parser() -> argparse.ArgumentParser:
  94. parser = argparse.ArgumentParser(
  95. description="find_agent 本地测试入口:可直接传需求 id(demand_grade.id)执行",
  96. )
  97. parser.add_argument(
  98. "--demand-id",
  99. "--demand-grade-id",
  100. dest="demand_id",
  101. type=int,
  102. help="demand_grade 表主键 id(需求 id)",
  103. )
  104. parser.add_argument(
  105. "--biz-dt",
  106. default=None,
  107. help="业务日 YYYYMMDD;默认使用该需求记录自身的 biz_dt",
  108. )
  109. parser.add_argument(
  110. "--force",
  111. action="store_true",
  112. help="强制重跑:复用已有 run_id 并重置输入(忽略已完成/已尝试跳过)",
  113. )
  114. parser.add_argument(
  115. "--dry-run",
  116. action="store_true",
  117. help="只加载并打印上下文,不调用 Agent",
  118. )
  119. parser.add_argument(
  120. "-v",
  121. "--verbose",
  122. action="store_true",
  123. help="输出 DEBUG 日志",
  124. )
  125. return parser
  126. def main(argv: list[str] | None = None) -> None:
  127. parser = build_parser()
  128. args = parser.parse_args(argv)
  129. _configure_logging(args.verbose)
  130. if args.demand_id is not None:
  131. run_by_demand_id(
  132. args.demand_id,
  133. biz_dt=args.biz_dt,
  134. force=args.force,
  135. dry_run=args.dry_run,
  136. )
  137. return
  138. if args.biz_dt or args.force or args.dry_run:
  139. parser.error("未指定 --demand-id 时,不能单独使用 --biz-dt / --force / --dry-run")
  140. run_interactive()
  141. if __name__ == "__main__":
  142. main(sys.argv[1:])