Skip to content

M5.2 — Q&A WebSocket endpoint #31

Description

@zestones

Note

Milestone: M5 — Work Order Generator + Q&A
Planning doc: docs/planning/M5-workorder-qa/issues.md

Scope. backend/agents/qa_agent.py + route dans main.py :

  • WS /api/v1/agent/chat
  • Stateful per connection : maintient messages: list côté serveur
  • Tools : tools = await mcp_client.get_tools_schema() + UI_TOOLS + [ASK_INVESTIGATOR_TOOL]
    (M5.2 est le fallback impl des M5.4 Managed Agents — même toolset)
  • Sur message client {type: "user", content: str} :
    • Append au history
    • Lance agent loop avec tools + Anthropic streaming
    • Pour chaque event reçu de l'API Anthropic :
      • Si content_block_delta text → envoie {type: "text_delta", content: ...} au client
      • Si tool_use block avec render_* → broadcast ui_render + envoie {type: "ui_render", component, props} au client
      • Si tool_use block avec ask_investigator → broadcast agent_handoff + spawn mini-session Investigator
        • envoie {type: "agent_handoff", from: "qa", to: "investigator", reason} au client
      • Si tool_use autre → envoie {type: "tool_call", name, args} + appelle mcp_client.call_tool()
        • envoie {type: "tool_result", name, ...}
    • À end_turn → envoie {type: "done"}

System prompt. "Tu es ARIA, assistant maintenance. Réponds aux questions de
l'opérateur sur ses équipements en utilisant les tools. Cite toujours les sources
(KB, logbook, signaux, RCA passés)."

Note

Decided — frontend gère le multi-tour.
Le ChatPanel (M6.5) affiche les
blocs dans l'ordre reçu : text_delta accumule dans une bulle assistant en
cours, tool_call crée une carte collapsable inline, tool_result enrichit la
carte, puis nouveau text_delta crée une nouvelle bulle. Événement done libre
l'input. Pattern éprouvé (Claude.ai, Cursor) — pas de risque.

Note

Decided — auth via cookie + decode manuel.
Au handshake :

token = ws.cookies.get("access_token")
try:
    payload = decode_jwt(token)
    user_id = payload["sub"]
except Exception:
    await ws.close(code=4401)
    return
await ws.accept()

Helper réutilisable core/security/ws_auth.py (même fonction utilisée par
WS /api/v1/events en M4.1).

Acceptance.

  • wscat -c ws://localhost:8000/api/v1/agent/chat (avec cookie auth) → question "OEE de la pompe P-02 ce mois ?" → stream texte cohérent + tool_call get_oee visible — unit path fully covered by test_mcp_tool_call_streams_tool_call_and_result_summary (drives a fake messages.stream through the get_oee tool and asserts the client receives tool_calltool_resulttext_delta in order). Live E2E requires docker compose up + Sonnet key.
  • (added) Auth handshake: missing/invalid cookie closes with code 4401 — test_unauth_handshake_closes_without_calling_run_turn confirms the agent loop never runs unauthenticated.
  • (added) Per-connection messages: list state grows across user turns — verified in router test by passing the same list to two consecutive turns.
  • (added) ChatMap.tool_result.summary is a short string — _summarise_tool_result + test_summarise_tool_result_handles_list_dict_scalar_error pin the contract; raw tool JSON never reaches the chat channel.
  • (added) Dual-channel broadcast for ui_render and agent_handoff — events-bus uses from_agent/to_agent, chat channel uses from/to per ws.types.ts. test_render_tool_dual_channel_broadcast + test_ask_investigator_dual_channel_handoff assert both shapes.
  • (added) ask_investigator handoff does NOT spawn a 120s run_investigator — it calls the lightweight answer_investigator_question (Sonnet one-shot over recent RCAs + failure_history). Mirrors answer_kb_question (M3.5 — KB Builder en mode "agent appelé" (ask_kb_builder handler) #21) contract: pure, never raises, returns fallback dict on any error. Per audit §5 (M5.4).
  • (added) ASK_INVESTIGATOR_TOOL schema declared here (not M5.4 — Q&A migration vers Claude Managed Agents #33) so Managed Agents migration in M5.4 — Q&A migration vers Claude Managed Agents #33 just imports it.
  • (added) Turn isolation: each user frame sets current_turn_id ContextVar then resets on exit — every nested ws_manager.broadcast auto-tags with the correct turn_id for the Activity Feed / Agent Inspector.

Metadata

Metadata

Assignees

Labels

agentTouches an Anthropic agent loop or orchestrationbackendChangement on back sidedemo-blockerRequired for one of the 5 demo scenesstreamingWebSocket / SSE streaming work

Projects

  • Status
    ✅ Done

Relationships

None yet

Development

No branches or pull requests

Issue actions