dogfood_agentpack.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #!/usr/bin/env python3
  2. """
  3. Phase I dogfood — AgentPack end-to-end script with real LLM.
  4. Usage:
  5. # Start the server in another terminal first:
  6. AGENTPAAS_DEPLOYMENT_MODE=desktop uvicorn agentpaas.api.app:app --port 8000
  7. # Run the dogfood script:
  8. python scripts/dogfood_agentpack.py
  9. # Or point at a non-default host/key:
  10. python scripts/dogfood_agentpack.py --url http://127.0.0.1:8000 --key ap_xxxx
  11. The script:
  12. 1. Zips the built-in research.top-journal-reviewer pack from the repo
  13. 2. Installs it via POST /api/v1/agentpacks/install
  14. 3. Creates an agent via POST /api/v1/agentpacks/{id}/create-agent
  15. 4. Runs the agent with a short test abstract
  16. 5. Prints the workspace path and verifies review_report.md exists
  17. Requirements:
  18. - Server running with ANTHROPIC_API_KEY or DASHSCOPE_API_KEY set
  19. - pip install requests (standard, usually present)
  20. """
  21. from __future__ import annotations
  22. import argparse
  23. import json
  24. import os
  25. import subprocess
  26. import sys
  27. import tempfile
  28. import time
  29. import zipfile
  30. from pathlib import Path
  31. # ── Config ────────────────────────────────────────────────────────────────────
  32. REVIEWER_PACK_ID = "research.top-journal-reviewer"
  33. TEST_ABSTRACT = """\
  34. Title: Quantum Speedup for Dense Matrix Multiplication via Phase Estimation.
  35. Abstract: We present a quantum algorithm that computes dense n×n matrix products
  36. in O(n^{1.5} log n) time using quantum phase estimation and amplitude amplification,
  37. outperforming the classical Strassen barrier O(n^{2.37}). We provide a gate-level
  38. circuit of depth O(n^{0.75} polylog n) with error probability δ < 0.01. Experiments
  39. on 16×16 matrices on a simulated 40-qubit processor confirm a 3.2× speedup.
  40. 请审阅这篇论文的核心贡献声明、技术正确性和实验充分性,生成完整的审稿报告。
  41. """
  42. # ── Helpers ───────────────────────────────────────────────────────────────────
  43. def _print(label: str, msg: str = "", ok: bool = True) -> None:
  44. status = "✓" if ok else "✗"
  45. color = "\033[32m" if ok else "\033[31m"
  46. reset = "\033[0m"
  47. print(f" {color}{status}{reset} {label}: {msg}")
  48. def _zip_pack(repo_root: Path, pack_id: str, dest_dir: Path) -> str:
  49. pack_dir = repo_root / "agentexample" / "agentpacks" / pack_id
  50. if not pack_dir.is_dir():
  51. sys.exit(f"Pack not found on disk: {pack_dir}")
  52. zip_path = str(dest_dir / f"{pack_id}.zip")
  53. with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
  54. for p in pack_dir.rglob("*"):
  55. if p.is_file():
  56. zf.write(p, p.relative_to(pack_dir))
  57. return zip_path
  58. def _read_api_key_from_config() -> str:
  59. """Read the auto-bootstrapped API key from ~/.agentpaas/config.json."""
  60. cfg_path = Path.home() / ".agentpaas" / "config.json"
  61. try:
  62. with open(cfg_path) as f:
  63. return (json.load(f) or {}).get("api_key", "")
  64. except Exception:
  65. return ""
  66. # ── Main flow ─────────────────────────────────────────────────────────────────
  67. def run_dogfood(base_url: str, api_key: str, verbose: bool = False) -> bool:
  68. try:
  69. import requests
  70. except ImportError:
  71. sys.exit("Missing dependency: pip install requests")
  72. session = requests.Session()
  73. session.headers.update({"Authorization": f"Bearer {api_key}"})
  74. repo_root = Path(__file__).parent.parent.resolve()
  75. ok = True
  76. print(f"\n{'─'*60}")
  77. print(f" AgentPack Dogfood — {base_url}")
  78. print(f"{'─'*60}")
  79. # Step 0: health check
  80. print("\n[0] 服务健康检查")
  81. try:
  82. r = session.get(f"{base_url}/health", timeout=5)
  83. r.raise_for_status()
  84. _print("health", r.json().get("status", "?"))
  85. except Exception as e:
  86. _print("health", str(e), ok=False)
  87. print("\n 服务未响应。请先启动:\n uvicorn agentpaas.api.app:app --port 8000\n")
  88. return False
  89. # Step 1: zip + install pack
  90. print(f"\n[1] 安装内置 Pack: {REVIEWER_PACK_ID}")
  91. with tempfile.TemporaryDirectory() as td:
  92. zip_path = _zip_pack(repo_root, REVIEWER_PACK_ID, Path(td))
  93. _print("zip", f"{Path(zip_path).stat().st_size // 1024} KB")
  94. # install is loopback-only — works when server is on localhost
  95. r = session.post(
  96. f"{base_url}/api/v1/agentpacks/install",
  97. json={"zip_path": zip_path},
  98. timeout=30,
  99. )
  100. if r.status_code == 200:
  101. pack = r.json()["installed"]
  102. _print("install", f"{pack['id']} v{pack['version']}")
  103. elif r.status_code == 400 and "already installed" in r.text.lower():
  104. _print("install", "已安装,跳过")
  105. else:
  106. _print("install", f"HTTP {r.status_code}: {r.text[:200]}", ok=False)
  107. ok = False
  108. # Step 2: verify pack is listed
  109. print(f"\n[2] 列出已安装 packs")
  110. r = session.get(f"{base_url}/api/v1/agentpacks", timeout=10)
  111. packs = r.json().get("agentpacks", [])
  112. found = any(p["id"] == REVIEWER_PACK_ID for p in packs)
  113. _print("list", f"{len(packs)} pack(s),reviewer={'✓' if found else '✗'}", ok=found)
  114. if not found:
  115. ok = False
  116. # Step 3: create agent from pack
  117. print(f"\n[3] 从 Pack 创建智能体")
  118. r = session.post(
  119. f"{base_url}/api/v1/agentpacks/{REVIEWER_PACK_ID}/create-agent",
  120. json={"name": "Dogfood 审稿助手", "description": "Phase I dogfood"},
  121. timeout=15,
  122. )
  123. if r.status_code == 201:
  124. body = r.json()
  125. agent_id = body["agent_id"]
  126. _print("create-agent", f"id={agent_id}")
  127. else:
  128. _print("create-agent", f"HTTP {r.status_code}: {r.text[:200]}", ok=False)
  129. ok = False
  130. return ok
  131. # Step 4: verify agent in list
  132. print(f"\n[4] 验证智能体出现在 /agents")
  133. r = session.get(f"{base_url}/api/v1/agents", timeout=10)
  134. agent_ids = [a["id"] for a in r.json().get("agents", [])]
  135. found = agent_id in agent_ids
  136. _print("agents list", f"agent_id in list: {'✓' if found else '✗'}", ok=found)
  137. if not found:
  138. ok = False
  139. # Step 5: verify config._config_dir
  140. print(f"\n[5] 验证 agent config._config_dir")
  141. r = session.get(f"{base_url}/api/v1/agents/{agent_id}", timeout=10)
  142. agent = r.json()
  143. config_dir = agent.get("config", {}).get("_config_dir", "")
  144. agent_dir = agent.get("agent_dir", "")
  145. match = config_dir == agent_dir
  146. _print("_config_dir", f"{'matches agent_dir ✓' if match else f'MISMATCH: {config_dir!r} != {agent_dir!r}'}", ok=match)
  147. if not match:
  148. ok = False
  149. # Step 6: run agent
  150. print(f"\n[6] 运行审稿智能体(LLM 调用,可能需要 1-3 分钟)")
  151. t0 = time.time()
  152. r = session.post(
  153. f"{base_url}/api/v1/agents/{agent_id}/run",
  154. json={"input": TEST_ABSTRACT},
  155. timeout=300,
  156. )
  157. elapsed = int(time.time() - t0)
  158. if r.status_code != 200:
  159. _print("run", f"HTTP {r.status_code}: {r.text[:300]}", ok=False)
  160. ok = False
  161. return ok
  162. run_body = r.json()
  163. workspace = run_body.get("workspace_path", "")
  164. _print("run", f"完成 ({elapsed}s), status={run_body.get('status', '?')}")
  165. _print("workspace", workspace or "(未设置)")
  166. # Step 7: verify workspace files
  167. print(f"\n[7] 验证 workspace 产出物")
  168. if not workspace:
  169. _print("workspace_path", "run response 中未包含", ok=False)
  170. ok = False
  171. else:
  172. for fname in ("review_report.md", "review_result.json"):
  173. fpath = os.path.join(workspace, fname)
  174. exists = os.path.isfile(fpath)
  175. size = os.path.getsize(fpath) if exists else 0
  176. _print(fname, f"{'存在' if exists else '不存在'} ({size} bytes)", ok=exists)
  177. if not exists:
  178. ok = False
  179. if verbose and workspace and os.path.isfile(os.path.join(workspace, "review_report.md")):
  180. print("\n ── review_report.md (前 20 行) ──")
  181. with open(os.path.join(workspace, "review_report.md"), encoding="utf-8") as f:
  182. lines = f.readlines()[:20]
  183. for line in lines:
  184. print(" ", line, end="")
  185. # Step 8: cleanup
  186. print(f"\n[8] 清理(删除 dogfood agent)")
  187. r = session.delete(f"{base_url}/api/v1/agents/{agent_id}", timeout=10)
  188. _print("delete agent", f"HTTP {r.status_code}")
  189. print(f"\n{'─'*60}")
  190. result_str = "PASS ✓" if ok else "FAIL ✗"
  191. color = "\033[32m" if ok else "\033[31m"
  192. print(f" {color}Dogfood result: {result_str}\033[0m")
  193. print(f"{'─'*60}\n")
  194. return ok
  195. # ── CLI ───────────────────────────────────────────────────────────────────────
  196. def main() -> None:
  197. parser = argparse.ArgumentParser(description="AgentPack dogfood E2E script")
  198. parser.add_argument(
  199. "--url",
  200. default="http://127.0.0.1:8000",
  201. help="Base URL of the running agentpaas server (default: http://127.0.0.1:8000)",
  202. )
  203. parser.add_argument(
  204. "--key",
  205. default="",
  206. help="API key (Bearer). Reads from ~/.agentpaas/config.json if omitted.",
  207. )
  208. parser.add_argument(
  209. "--verbose", "-v",
  210. action="store_true",
  211. help="Print first 20 lines of review_report.md",
  212. )
  213. args = parser.parse_args()
  214. api_key = args.key or _read_api_key_from_config()
  215. if not api_key:
  216. sys.exit(
  217. "No API key found. Pass --key ap_xxx or run the server in desktop mode "
  218. "to auto-generate one at ~/.agentpaas/config.json"
  219. )
  220. success = run_dogfood(args.url, api_key, verbose=args.verbose)
  221. sys.exit(0 if success else 1)
  222. if __name__ == "__main__":
  223. main()