deerflow-code/offline-backend-20260512/backend/scripts/probe_roundtable_job.py
2026-09-07 18:24:55 +08:00

101 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""圆桌后台作业真网关 —— headless 验证脚本(Phase 3b)。
用法(在能起完整后端栈 + 配好真模型的环境):
1) 用真网关启动后端:
ROUNDTABLE_JOB_GATEWAY=inprocess make gateway
(或 docker 里给 gateway 容器加该环境变量)
2) 跑本脚本,它会:POST 启动一个作业 → 轮询 GET 打印进度 → 跑到 done 打印报告长度。
PYTHONPATH=. uv run python scripts/probe_roundtable_job.py
环境变量:
GATEWAY_URL 默认 http://127.0.0.1:8001
AUTH_COOKIE 若后端开启鉴权,传 "session=xxx" 之类的 Cookie 头
PROBE_INTENT 自定义任务意图
PROBE_MODE recommend(默认)| chain
说明:鉴权关闭的 dev 模式无需 AUTH_COOKIE。真网关会进程内驱动 roundtable-coordinator /
各席位 / roundtable-report 真模型,耗时取决于模型,可能数分钟。
"""
from __future__ import annotations
import asyncio
import os
import time
import httpx
GATEWAY_URL = os.getenv("GATEWAY_URL", "http://127.0.0.1:8001").rstrip("/")
AUTH_COOKIE = os.getenv("AUTH_COOKIE", "")
INTENT = os.getenv("PROBE_INTENT", "为一家中型制造企业设计一套数字化转型的整体方案")
MODE = os.getenv("PROBE_MODE", "recommend")
# 用 3 个内置圆桌席位(确保可见、已 seed)。
AGENTS = [
{"agentId": "roundtable-intelligence", "name": "情报收集"},
{"agentId": "roundtable-solution-design", "name": "方案设计"},
{"agentId": "roundtable-summary", "name": "总结陈述"},
]
TERMINAL = {"done", "error", "cancelled"}
def _headers() -> dict[str, str]:
h = {"Content-Type": "application/json"}
if AUTH_COOKIE:
h["Cookie"] = AUTH_COOKIE
return h
async def main() -> None:
async with httpx.AsyncClient(timeout=30.0, trust_env=False) as client:
print(f"[probe] POST {GATEWAY_URL}/api/roundtable-jobs mode={MODE}")
resp = await client.post(
f"{GATEWAY_URL}/api/roundtable-jobs",
headers=_headers(),
json={"intent": INTENT, "agents": AGENTS, "orchestrationMode": MODE},
)
if resp.status_code != 201:
print(f"[probe] start failed: HTTP {resp.status_code}: {resp.text[:500]}")
return
job = resp.json()
job_id = job["id"]
print(f"[probe] started job {job_id}")
last = ""
t0 = time.perf_counter()
while True:
await asyncio.sleep(2.0)
r = await client.get(f"{GATEWAY_URL}/api/roundtable-jobs/{job_id}", headers=_headers())
if r.status_code != 200:
print(f"[probe] get failed: HTTP {r.status_code}: {r.text[:300]}")
return
j = r.json()
line = (
f"status={j['status']} phase={j['phase']} cycle={j['cycle']} "
f"active={j.get('activeAgentId')} consensus={j['consensusPercentage']}%"
)
if line != last:
print(f"[probe] +{time.perf_counter() - t0:5.0f}s {line}")
last = line
if j["status"] in TERMINAL:
print(f"[probe] terminal: {j['status']}")
if j["status"] == "error":
print(f"[probe] error detail: {j.get('error')}")
# 读草稿 step3 验证报告已落库
if j.get("draftId"):
dr = await client.get(
f"{GATEWAY_URL}/api/roundtable-drafts/{j['draftId']}", headers=_headers()
)
if dr.status_code == 200:
step3 = dr.json().get("step3")
html_len = len((step3 or {}).get("html") or "")
print(f"[probe] draft.step3 html length = {html_len}")
return
if __name__ == "__main__":
asyncio.run(main())