Files
deerflow-code/offline-backend-20260512/backend/scripts/probe_roundtable_job.py
T
2026-06-04 18:50:31 +08:00

101 lines
3.8 KiB
Python

"""圆桌后台作业真网关 —— 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())