You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Milestone: M3 — KB Builder Agent Planning doc:docs/planning/M3-kb-builder/issues.md Status: IMPLEMENTED on branch 19-m33-onboarding-session-multi-turn (will move to its own branch / PR before merge).
This handler is called by the M4.6 orchestrator when Investigator calls the ask_kb_builder tool. It is a pure async function: no WebSocket broadcasts,
no session state, no DB writes.
Note
Depends on M3.1 (#17).anthropic_client.py exists with model_for, parse_json_response, and the anthropic singleton — verified.
1. Implementation
Lives in backend/agents/kb_builder/qa.py (split-by-concern, matching pdf_extraction.py and onboarding/). Re-exported from agents.kb_builder for from agents.kb_builder import answer_kb_question.
Body matches the issue's reference snippet, with two minor adjustments:
import logging lives at module top (not inside the function) — single logger per module is the project pattern.
# noqa: BLE001 kept on the bare except Exception — the safe-fallback contract is explicit.
2. Model choice
Uses model_for("chat") — always Sonnet, regardless of ARIA_MODEL. Verified by test_returns_parsed_json_on_happy_path asserting the captured model kwarg is claude-sonnet-4-5.
3. Pure handler contract
Static guard test test_module_does_not_import_ws_manager_or_db parses the module AST, strips docstrings, and asserts ws_manager, broadcast, from core import database, and import asyncpg are absent from the executable code. Fails fast if anyone reintroduces a duplicate broadcast path.
4. Error contract
Three failure paths return the documented fallback dict:
Failure
Returned dict
MCP is_error=True
{answer: "KB not available for cell {n}", source: null, confidence: 0.0}
await answer_kb_question(2, "What is the max bolt torque on the turbine?") returns {answer, source, confidence}
Latency < 5 s (Sonnet) — single messages.create with max_tokens=1024
If info absent in KB: answer contains "unknown", not a hallucinated value (system prompt enforces it; test_unknown_answer_is_passed_through_not_hallucinated verifies pass-through)
If cell has no KB row: returns {answer: "KB not available...", confidence: 0.0} — does NOT raise
Function performs no DB writes and emits no WS broadcasts (static guard test)
Note
Milestone: M3 — KB Builder Agent
Planning doc:
docs/planning/M3-kb-builder/issues.mdStatus: IMPLEMENTED on branch
19-m33-onboarding-session-multi-turn(will move to its own branch / PR before merge).Scope.
backend/agents/kb_builder/qa.py—answer_kb_question(cell_id, question).This handler is called by the M4.6 orchestrator when Investigator calls the
ask_kb_buildertool. It is a pure async function: no WebSocket broadcasts,no session state, no DB writes.
Note
Depends on M3.1 (#17).
anthropic_client.pyexists withmodel_for,parse_json_response, and theanthropicsingleton — verified.1. Implementation
Lives in
backend/agents/kb_builder/qa.py(split-by-concern, matchingpdf_extraction.pyandonboarding/). Re-exported fromagents.kb_builderforfrom agents.kb_builder import answer_kb_question.Body matches the issue's reference snippet, with two minor adjustments:
import logginglives at module top (not inside the function) — single logger per module is the project pattern.# noqa: BLE001kept on the bareexcept Exception— the safe-fallback contract is explicit.2. Model choice
Uses
model_for("chat")— always Sonnet, regardless ofARIA_MODEL. Verified bytest_returns_parsed_json_on_happy_pathasserting the capturedmodelkwarg isclaude-sonnet-4-5.3. Pure handler contract
Static guard test
test_module_does_not_import_ws_manager_or_dbparses the module AST, strips docstrings, and assertsws_manager,broadcast,from core import database, andimport asyncpgare absent from the executable code. Fails fast if anyone reintroduces a duplicate broadcast path.4. Error contract
Three failure paths return the documented fallback dict:
is_error=True{answer: "KB not available for cell {n}", source: null, confidence: 0.0}anthropic.messages.createraises{answer: "KB query failed — information unavailable", source: null, confidence: 0.0}parse_json_responseraisesValueErrorexcept)All three are covered by dedicated tests.
5. Dependency chain
flowchart LR M31["M3.1 anthropic_client<br/>(anthropic, model_for,<br/>parse_json_response)"] M25["M2.5 update_equipment_kb<br/>get_equipment_kb (DONE)"] M35["M3.5 answer_kb_question<br/>(this issue)"] M46["M4.6 ask_kb_builder tool<br/>in Investigator orchestrator"] M31 --> M35 M25 --> M35 M35 --> M466. Acceptance
await answer_kb_question(2, "What is the max bolt torque on the turbine?")returns{answer, source, confidence}messages.createwithmax_tokens=1024answercontains "unknown", not a hallucinated value (system prompt enforces it;test_unknown_answer_is_passed_through_not_hallucinatedverifies pass-through){answer: "KB not available...", confidence: 0.0}— does NOT raise