fix(agents): 移除人工 per-agent timeout,LLM 必須等完整回應
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled

原設計 asyncio.wait_for(timeout_sec=25s) 是任意截斷,
只要 LLM 超過時限就降級為 confidence=20%,根本沒有分析。

正確做法:
- 移除所有 4 個 agent 的 asyncio.wait_for() 包裝
- 只留 except Exception 捕真實異常(連線失敗、模型崩潰)
- 全流程由 Orchestrator GLOBAL_TIMEOUT_SEC=90s 防掛死
- _PER_AGENT_TIMEOUT_SEC 常數廢棄移除

影響:LLM 推理多久就等多久,不再人工截斷,
      deepseek-r1:14b 等模型得以完整輸出分析結果。

2026-04-16 ogt + Claude Sonnet 4.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-16 02:54:34 +08:00
parent 5a3a649f8a
commit 7e3cc8b3b0
5 changed files with 26 additions and 60 deletions

View File

@@ -69,9 +69,9 @@ logger = structlog.get_logger(__name__)
# 調整: 每 Agent 25s, 3個序列+1組並行 = 最差 75s + buffer = 90s
GLOBAL_TIMEOUT_SEC = 90.0
# 每個 Agent 個別超時(預設 5s 是開發機測試值,生產需對應 LLM 延遲)
# deepseek-r1:14b avg 10.6s, 99th percentile ~30s
_PER_AGENT_TIMEOUT_SEC = 25.0
# 2026-04-16 ogt + Claude Sonnet 4.6: 移除 _PER_AGENT_TIMEOUT_SEC
# LLM 必須等到完整回應,不得人工截斷。降級只在真正異常(連線失敗、模型崩潰)觸發。
# 全流程由 GLOBAL_TIMEOUT_SEC 防掛死即可。
# Redis Stream key
STREAM_KEY = "aiops:p2:events"
@@ -180,7 +180,7 @@ async def _debate(
# ── Step 1: Diagnostician ──────────────────────────────────────────────
diagnostician = get_diagnostician_agent()
diagnosis = await diagnostician.run(snapshot, timeout_sec=_PER_AGENT_TIMEOUT_SEC)
diagnosis = await diagnostician.run(snapshot)
await _record_turn(
session_id=session_id,
incident_id=incident_id,
@@ -194,7 +194,7 @@ async def _debate(
# ── Step 2: Solver ─────────────────────────────────────────────────────
solver = get_solver_agent()
plan = await solver.run(diagnosis, timeout_sec=_PER_AGENT_TIMEOUT_SEC)
plan = await solver.run(diagnosis)
await _record_turn(
session_id=session_id,
incident_id=incident_id,
@@ -211,8 +211,8 @@ async def _debate(
critic = get_critic_agent()
verdict, critic_report = await asyncio.gather(
reviewer.run(plan, timeout_sec=_PER_AGENT_TIMEOUT_SEC),
critic.run(diagnosis, plan, timeout_sec=_PER_AGENT_TIMEOUT_SEC),
reviewer.run(plan),
critic.run(diagnosis, plan),
)
await asyncio.gather(