diff --git a/README.md b/README.md index 3fb68d5..ccafb92 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,42 @@ trade-offs documented in [`docs/SAFETY_WRITEUP.md`](docs/SAFETY_WRITEUP.md). ## Architecture -Three narrow agents behind a deterministic supervisor -- the supervisor is -plain Rust control flow, not another LLM call, so *what's allowed through* -is never itself something the model can be talked out of: +Three narrow agents behind a deterministic supervisor: +```mermaid +flowchart TD + Q["Customer Query"] --> PRE{{"Guardrail: Pre-check
regex injection scan"}} + PRE -- allow --> RET["Retrieval Agent
vector search + LLM relevance filter"] + RET <--> QD[("Qdrant Vector Store
chunked KB docs")] + RET --> DRAFT["Drafting Agent
tool-calling loop"] + DRAFT -- lookup_shipment --> SHIP[("Shipment Repository")] + DRAFT -- escalate_to_human --> ESC[("Escalation Desk")] + DRAFT --> POL["Policy-Compliance Agent"] + POL --> DOCS[("acceptable_use.md +
tracking_policy.md")] + POL --> POST{{"Guardrail: Post-check
PII/injection scan + policy verdict"}} + POST -- allow / escalate --> FINAL["Final Answer + Citations -> Customer"] + PRE -- block --> FINAL + PRE -.-> AUDIT[("Audit Log
JSONL, append-only")] + POST -.-> AUDIT + + classDef agent fill:#eef2ff,stroke:#4f46e5,color:#312e81 + classDef guardrail fill:#fff7ed,stroke:#ea580c,color:#7c2d12 + classDef store fill:#f1f5f9,stroke:#64748b,color:#334155 + classDef audit fill:#f0fdf4,stroke:#16a34a,color:#14532d + classDef io fill:#f8fafc,stroke:#334155,color:#0f172a + + class RET,DRAFT,POL agent + class PRE,POST guardrail + class QD,SHIP,ESC,DOCS store + class AUDIT audit + class Q,FINAL io ``` -query -> guardrail.pre_check (rule-based, blocks prompt injection) - -> RetrievalAgent (Qdrant vector search -> LLM relevance filter) - -> DraftingAgent (tool-calling: lookup_shipment, escalate_to_human) - -> PolicyAgent (LLM compliance check against acceptable_use.md) - -> guardrail.post_check (rule-based PII/injection scan + policy verdict) - -> answer, audit record -``` + +The supervisor (`src/agents/supervisor.rs`) is what runs this graph -- plain +Rust control flow, not another LLM call, so *what's allowed through* is +never itself something the model can be talked out of. The dashed edges are +the audit trail: every guardrail decision is logged, not just the blocked +ones. Every LLM call asks for a schema-validated structured output (via a forced tool-call, see `src/llm/client.rs`) rather than free text -- there's no