Skip to content

    AI Architect

    Diagramming AI Systems: C4, Sequence, State, ER

    Draw one AI assistant at every level: context, container, component, sequence, deployment, state, data model and trust boundaries, plus a checklist.

    By Published 29 min read

    Share

    This lesson is part of AI Architect: From AI Engineer to Expert. Enroll for free, then sign in, to read it in full.

    What you'll learn: how to explain an AI system with the right diagram for each question: C4-style context, container and component views, sequence diagrams for the happy path and the failure paths, a deployment view, a state diagram for the agent loop, a data-model view, and a trust-boundary view. One teaching system is drawn at every level, and the page ends with a checklist you can apply to any diagram in a design review.

    A diagram is an argument. A good one answers one question for one audience and makes the weak points of the design easy to see. A bad one is a pile of boxes that everybody nods at and nobody can challenge. This page teaches the first kind, using a single system drawn many times so the views can be compared with each other.

    The real-world problem

    Composite scenario, not a real company. A team presents the design of a customer-facing assistant that answers questions from policy documents and can open a support ticket. The review deck has one slide with eleven boxes, arrows in three styles and a legend nobody can find. The reviewers ask four questions. Where does the customer's identity go after the gateway? What happens if the search step returns nothing? Who retries when the ticket service is slow? Which parts run in the same network as the documents? The slide answers none of them, because it mixes structure, runtime order and deployment in one picture. The team then spends the meeting explaining verbally what the picture should have shown.

    The fix is not a prettier slide. It is a small set of diagrams, each answering one of those questions, drawn from the same component names so they can be read together.

    After this page you will be able to

    • Choose the diagram type (context, container, component, sequence, deployment, state, ER, trust boundary) that answers a given design-review question.
    • Draw a system as C4-style context, container and component views using Mermaid flowcharts with subgraphs, with every element named consistently.
    • Draw a sequence diagram for a normal request and for two failure paths, and check each message against the prose that describes it.
    • Mark trust boundaries on a diagram and name the control that applies where a data flow crosses one.
    • Apply the diagram checklist (caption, legend, no colour-only meaning, about 15 nodes at most, consistent names, failure paths shown) and reject a diagram that fails it.

    Read the AI architect mindset first: it explains why a design review starts from requirements and constraints, which is where each diagram question comes from. Background that this page does not repeat:

    Terms used on this page: C4 model, trust boundary, sequence diagram, idempotency key and data flow diagram.

    Core concepts

    One system, drawn at every level

    Every diagram on this page shows the same invented teaching system, the policy Q&A assistant. A customer asks a question about a policy (for example a returns or warranty policy). The assistant answers only from approved policy documents and cites the passages it used. If the customer asks, it can also create a support ticket. It is not a real product, and every number attached to it is illustrative.

    The course uses fixed component names so that every diagram on every page can be read against the others. This page uses these:

    Name What it does in the teaching system
    Client app The web or mobile front end the customer uses
    API gateway Authenticates the caller, rate-limits, forwards the request with a verified identity
    Orchestrator Runs the agent loop: builds the prompt, calls the model, decides about tool calls, checks the output
    Model gateway The single path to model providers: keys, retries on the provider side, token accounting
    LLM provider An external service that generates text
    Retriever Turns the question into a search and returns the allowed passages
    Vector index Stores embeddings of policy passages for similarity search
    Document store Stores the approved policy documents and their versions
    Tool server Exposes the ticket-creation tool to the Orchestrator
    Policy engine Decides allow or deny for a tool call, for a given user
    Event bus Carries audit and trace events asynchronously
    Worker Consumes events and writes them to storage
    Trace store Keeps traces and audit events
    Eval service Replays saved cases against a new prompt or model version

    The Approval service from the course-wide list does not appear: creating a ticket on the customer's own behalf is reversible and customer-initiated, so a Policy engine decision is enough in this teaching system. An action such as issuing a refund would add it.

    The ticketing system and the identity provider are outside the system and are shown as external.

    C4 in one page

    The C4 model is a way of describing software architecture at four zoom levels, created by Simon Brown and documented at c4model.com. The site describes it as "an easy to learn, developer friendly approach to software architecture diagramming", built from hierarchical abstractions (software systems, containers, components and code) and hierarchical diagrams of the same names (system context, containers, components, code). It also describes it as notation independent and tooling independent, and it says you do not need all four diagram levels, "only those that add value". It names the system context and container diagrams as sufficient for most software development teams.

    Its definitions are short. A software system is made up of one or more containers (applications and data stores), each containing one or more components, which are implemented by code elements. A container is, in the site's words, "an application or a data store", something that "needs to be running in order for the overall software system to work". The site stresses that this is not about Docker. A component is "a grouping of related functionality encapsulated behind a well-defined interface."

    Three notation rules from the site are worth adopting even if you use no C4 tooling. Every diagram should have a title describing the diagram type and scope. All diagrams should have a key or legend that makes the notation explicit. And each diagram should be able to stand alone and be mostly understood without a narrative. The same page asks that every element carry a type and a short description, that every line be labelled and represent one direction, and that relationships between containers name a technology or protocol.

    On this site the C4-style views are drawn as Mermaid flowcharts with subgraphs. They follow the C4 abstractions and the rules above, but they are not C4 tool output and they do not use Mermaid's own C4 diagram type. That type exists, and its documentation calls it "an experimental diagram for now" whose syntax and properties can change in future releases. A plain flowchart is something every reviewer can edit in a pull request and render in the same pipeline as the rest of the site. The cost is that nothing enforces C4 discipline for you, which is what the checklist later on this page is for.

    The context view

    The context view puts the system in the middle and shows only the people and external systems around it. According to c4model.com its scope is a single software system, its primary element is the system in scope, and its supporting elements are the people and external systems directly connected to it. The intended audience is everybody, technical and non-technical, and the site recommends it for all teams.

    Figure 1. Context view of the policy Q&A assistant

    flowchart LR accTitle: Context view of the policy Q&A assistant accDescr: The policy Q&A assistant sits between customers and policy owners on one side and three external systems on the other. Customers ask questions, policy owners publish documents, and support agents work the tickets that the assistant creates. Customer(["Customer: asks policy questions, may ask for a ticket"]) Owner(["Policy owner: publishes and retires policy documents"]) Agent(["Support agent: works the tickets"]) Sys["Policy Q&A assistant: answers from approved policy documents with citations, can create a support ticket"] LLM["LLM provider (external): generates answer text"] Tick["Ticketing system (external): stores support tickets"] IdP["Identity provider (external): proves who the customer is"] Customer -->|"asks questions over HTTPS"| Sys Owner -->|"publishes documents"| Sys Sys -->|"prompt text and passages"| LLM Sys -->|"create ticket request"| Tick Sys -->|"verify session"| IdP Tick -->|"ticket appears in queue"| Agent

    How to read this: boxes with rounded ends are people, square boxes are software, "(external)" marks systems the team does not run, and each arrow is one direction with a label saying what travels.

    Notice what the context view deliberately leaves out: no databases, no queues, no model gateway. Its one job is to make the boundary and the dependencies visible. One decision it already exposes is that customer question text leaves the system for an LLM provider. That is a fact a privacy reviewer needs on the first diagram, not the tenth.

    The container view

    The container view zooms into the system. c4model.com says its scope is a single software system and its primary elements are the containers within it, with people and directly connected systems as supporting elements. The audience is technical people inside and outside the team. The site also notes that it says very little about deployment such as clustering and failover, which belongs in separate deployment diagrams, one per environment.

    For an AI system, the useful containers are the ones that fail, scale and get secured differently: the Orchestrator, the Model gateway, the Retriever and its data stores, the Tool server, the Policy engine, and the asynchronous audit path.

    Figure 2. Container view of the policy Q&A assistant

    flowchart LR accTitle: Container view of the policy Q&A assistant accDescr: The Client app calls the API gateway, which calls the Orchestrator. The Orchestrator uses the Retriever, the Model gateway, the Policy engine and the Tool server. The Model gateway reaches the external LLM provider and the Tool server reaches the external ticketing system. Audit events flow through the Event bus to a Worker and the Trace store, which the Eval service reads. Client["Client app"] subgraph SYS["Policy Q&A assistant (system boundary)"] GW["API gateway"] Orch["Orchestrator"] MG["Model gateway"] Ret["Retriever"] VI[("Vector index")] DS[("Document store")] TS["Tool server"] PE["Policy engine"] EB["Event bus"] Wk["Worker"] TR[("Trace store")] Ev["Eval service"] end LLM["LLM provider (external)"] Tick["Ticketing system (external)"] Client -->|"HTTPS"| GW GW -->|"question plus verified identity"| Orch Orch -->|"search with user entitlements"| Ret Ret -->|"similarity search"| VI Ret -->|"fetch approved passages"| DS Orch -->|"prompt"| MG MG -->|"HTTPS"| LLM Orch -->|"may this call run?"| PE Orch -->|"tool call with idempotency key"| TS TS -->|"HTTPS"| Tick Orch -.->|"audit event (async)"| EB EB -.->|"consume events"| Wk Wk -->|"write"| TR Ev -->|"read saved cases"| TR

    How to read this: solid arrows are synchronous calls that the customer waits for, dotted arrows are asynchronous, cylinders are stores, and the large box is the system boundary.

    Two details are deliberate. The Orchestrator is the only container that talks to the Retriever, the Model gateway, the Policy engine and the Tool server, so the next view zooms into it. And the audit path is asynchronous: the customer does not wait for the Trace store, but the design must then say what happens when the Event bus is down. That belongs in the failure sequences, not here. Two things are left out to stay near the node limit: the identity provider from Figure 1 (the API gateway's "verify session" call) and the ingestion path that fills the Document store and Vector index. A real container view would show both.

    The component view

    A component view decomposes one container. c4model.com defines its scope as a single container and its primary elements as the components inside, for architects and developers, and says to create it only "if you feel they add value". For the Orchestrator it adds value because the agent loop is where most of the AI-specific decisions live.

    Figure 3. Component view of the Orchestrator

    flowchart LR accTitle: Component view of the Orchestrator accDescr: Inside the Orchestrator a request passes through input checks, the context builder, the agent loop and output checks. The agent loop calls the Model gateway and hands tool requests to the tool dispatcher, which asks the Policy engine before calling the Tool server and returns the result or a refusal to the agent loop. An audit emitter publishes events to the Event bus. GW["API gateway"] subgraph ORCH["Orchestrator (container)"] In["Input checks"] Ctx["Context builder"] Loop["Agent loop"] Disp["Tool dispatcher"] Out["Output checks"] Aud["Audit emitter"] end Ret["Retriever"] MG["Model gateway"] PE["Policy engine"] TS["Tool server"] EB["Event bus"] GW -->|"question plus identity"| In In -->|"accepted request"| Ctx Ctx -->|"search"| Ret Ctx -->|"prompt context"| Loop Loop -->|"prompt"| MG Loop -->|"tool request"| Disp Disp -->|"allow or deny?"| PE Disp -->|"approved call"| TS Disp -->|"tool result or refusal"| Loop Loop -->|"draft answer"| Out Out -->|"answer"| GW In -.->|"event"| Aud Disp -.->|"event"| Aud Out -.->|"event"| Aud Aud -.->|"async"| EB

    How to read this: the large box is one container, the boxes inside it are components with one responsibility each, and everything outside the box is another container from Figure 2.

    Each component has one responsibility and one place to put a rule. The input checks reject oversize or malformed requests before any model spend. The context builder decides which passages and which parts of the conversation go in the prompt. The agent loop owns the step budget. The tool dispatcher is the only code that may call the Tool server, which makes it the one place to put the Policy engine check. The output checks verify, for example, that every citation in the answer refers to a passage that was actually retrieved. The audit emitter is written once and used by the others, so no path forgets to log.

    Sequence diagrams for runtime behaviour

    Structure diagrams do not say what happens in what order. A sequence diagram shows participants as vertical lifelines and messages as arrows read from top to bottom. c4model.com's own supporting diagram for this, the dynamic diagram, is described as showing "how elements in the static model collaborate at runtime to implement a user story, use case, feature", and the site says its collaboration and sequence styles show the same information, so either may be used.

    Mermaid's sequence syntax gives you what the design needs: participant X as Name for aliases; solid arrows with a head (->>) for requests and dotted arrows (-->>) for replies; an open-headed arrow (-)) for an asynchronous message; Note over A,B: text; blocks such as alt, else, opt, loop and par; and autonumber. The Mermaid documentation warns that the word "end" can break a diagram, that a semicolon acts as a line break, and that # needs an entity code. The house rule on this site is to avoid those characters in message text.

    Rule of thumb: draw one sequence per scenario, with at most about eight participants, and put the decision in a note at the point where the system makes it.

    Figure 4. Normal question, answered from the policy documents

    sequenceDiagram accTitle: Normal question sequence accDescr: The Client app sends a question through the API gateway to the Orchestrator. The Orchestrator searches with the Retriever, asks the LLM provider through the Model gateway, checks the answer, publishes an audit event and returns the answer. autonumber participant C as Client app participant G as API gateway participant O as Orchestrator participant R as Retriever participant M as Model gateway participant L as LLM provider participant E as Event bus C->>G: question over HTTPS with session token G->>O: question plus verified user identity O->>R: search(question, user entitlements) R-->>O: top passages with document ids and versions O->>M: prompt with question and passages M->>L: model request L-->>M: draft answer with citations M-->>O: draft answer plus token counts Note over O: Output check - every cited id must be among the retrieved passages O-)E: audit event outcome=answered O-->>G: answer with citations G-->>C: answer with citations

    How to read this: time runs downward, solid arrows are requests, dotted arrows are replies, the open-headed arrow is asynchronous, and the note marks a decision the Orchestrator makes.

    Read against the numbers: in steps 1 and 2 the gateway authenticates the session and passes the verified identity on, so the Orchestrator never trusts an identity claim from the Client app directly. Step 3 sends the user's entitlements with the search so the Retriever can restrict results to documents this customer may see. Step 4 returns passages with ids and versions, and those are what the citations can refer to. Steps 5 to 8 are the model call through the Model gateway, which is the only participant that talks to the LLM provider. The note after step 8 is the output check from Figure 3: an answer that cites a passage the Retriever never returned is rejected. Step 9 is asynchronous, so the audit write is off the customer's critical path. Steps 10 and 11 return the answer.

    The happy path is the least informative diagram you can draw. The next two show what the design does when things go wrong.

    Figure 5. Failure path: the retrieval step returns nothing or times out

    sequenceDiagram accTitle: Retrieval failure sequence accDescr: The Orchestrator searches with the Retriever. If the Retriever replies in time with no relevant passages, or does not reply within the retrieval timeout, the Orchestrator does not call the Model gateway. It records the outcome, tells the customer it found nothing, and offers to create a ticket. autonumber participant C as Client app participant G as API gateway participant O as Orchestrator participant R as Retriever participant E as Event bus C->>G: question G->>O: question plus verified user identity O->>R: search(question, user entitlements) with 1 s timeout alt Retriever replies in time R-->>O: 0 passages above the relevance threshold Note over O: Decision - no evidence, so no model call O-)E: audit event outcome=no_evidence else No reply within the timeout Note over O: Decision - retrieval unavailable, so no model call O-)E: audit event outcome=retrieval_unavailable end O-->>G: no answer found plus an offer to create a ticket G-->>C: no answer found plus an offer to create a ticket

    How to read this: the alt block shows two alternatives that end in the same customer message, and the notes mark the decisions.

    The point of this diagram is a decision that is easy to skip: with no evidence, the Orchestrator does not call the model at all. A model asked a policy question with no passages can still produce a fluent answer from its training data, which is exactly what the system promised not to do. The diagram also shows a single retry-free timeout on the Retriever and an honest user-facing outcome (an offer of a ticket instead of a guess). Whether to retry the Retriever once is a design choice; if you choose to, the diagram must show it and the timeout budget must pay for it.

    Figure 6. Failure path: the ticket tool times out

    sequenceDiagram accTitle: Ticket tool timeout sequence accDescr: The Orchestrator asks the Policy engine whether the ticket call is allowed, then calls the Tool server with an idempotency key. The first call times out. The Orchestrator retries once with the same key. If the second call succeeds the ticket is confirmed. If it also times out, the Orchestrator makes no third attempt, reports honestly that the ticket was not confirmed, and records the failure. autonumber participant O as Orchestrator participant P as Policy engine participant T as Tool server participant K as Ticketing system participant E as Event bus O->>P: may this customer create a ticket of this type? P-->>O: allow O->>T: create_ticket(summary, key k1) with 3 s timeout T->>K: create ticket with key k1 Note over O,T: No reply within 3 s - the first attempt is abandoned Note over O: For this tool call the Orchestrator is the only layer that retries O->>T: create_ticket(summary, key k1) after 0.5 s backoff T->>K: create ticket with key k1 alt Second attempt replies in time T-->>O: ticket id for key k1 O-)E: audit event outcome=ticket_created else Second attempt also times out Note over O: No third attempt - report honestly O-)E: audit event outcome=ticket_unconfirmed end

    How to read this: the two notes explain the timeout and who owns retries, and the alt block shows the success and failure endings of the single retry.

    This diagram carries three decisions. First, the Policy engine is asked before the call, not after, and a deny would end the sequence at step 2. Second, the retry reuses the same idempotency key k1: the first attempt may have reached the ticketing system even though its reply was lost, so a second call without the key could create a duplicate ticket. An idempotency key is a value the caller supplies so that repeating the same request has the effect of doing it once; somewhere on that path (the Tool server, or a ticketing system that honours keys) a store of keys must return the existing ticket id when it sees a repeat. Third, exactly one layer retries. Multi-layer retries are drawn in detail in the billing-dispute case file; here it is enough to note that the Google SRE book warns against "amplifying retries by issuing retries at multiple levels", with a worked case of three layers each making 4 attempts producing 4^3 = 64 attempts on the database for one user action.

    The deployment view

    The deployment view answers where each container runs and who controls the network between them. c4model.com's definition: its scope is one or more software systems in a single deployment environment (production, staging and so on), its primary elements are deployment nodes, software system instances and container instances, deployment nodes can be nested, and infrastructure nodes such as DNS services, load balancers and firewalls may be included. It also says any icons you use must be in the diagram key. This page uses no icons.

    Figure 7. Deployment view of the production environment (instance counts are illustrative)

    flowchart TB accTitle: Production deployment view accDescr: In production the API gateway sits in a public zone. The Orchestrator, Retriever, Model gateway, Policy engine, Tool server and Worker run in a private application zone. The stores and the Event bus run in a data zone. The LLM provider and the ticketing system are third-party services reached from the application zone. Client["Client app on the customer device"] subgraph PROD["Production environment"] subgraph PUB["Public zone"] GW["API gateway: 2 instances"] end subgraph APP["Private application zone"] Orch["Orchestrator: 3 instances"] Ret["Retriever: 2 instances"] MG["Model gateway: 2 instances"] PE["Policy engine: 2 instances"] TS["Tool server: 2 instances"] Wk["Worker: 2 instances"] end subgraph DATA["Data zone"] VI[("Vector index")] DS[("Document store")] TR[("Trace store")] EB["Event bus"] end end LLM["LLM provider: third-party service"] Tick["Ticketing system: third-party service"] Client -->|"HTTPS"| GW GW -->|"question plus identity"| Orch Orch -->|"search"| Ret Orch -->|"prompt"| MG Orch -->|"may this call run?"| PE Orch -->|"tool call"| TS Ret -->|"similarity search"| VI Ret -->|"fetch passages"| DS Orch -.->|"audit event"| EB EB -.->|"consume events"| Wk Wk -->|"write"| TR MG -->|"HTTPS"| LLM TS -->|"HTTPS"| Tick

    How to read this: nested boxes are deployment zones, each component box says how many instances run, and arrows follow the same directions as Figure 2.

    The deployment view makes questions concrete that the container view could not: the Model gateway and the Tool server are the only containers with a path out to third parties, so egress rules attach to those two. The data zone has no inbound path from the public zone. The Eval service is left out of the figure to keep it small. A second environment (staging) needs its own diagram, because instance counts, network rules and even which third-party service is real will differ.

    State diagrams for agents and workflows

    When a component can be in one of several modes and the rules for moving between modes matter, draw a state diagram. An agent loop is the standard case: the request moves between retrieving, reasoning, deciding, checking policy and calling a tool, and it may loop. Mermaid's stateDiagram-v2 supports [*] for start and end, labelled transitions, composite states and a <<choice>> node for a branch, which is the construct used here.

    Figure 8. State of one request in the Orchestrator's agent loop

    stateDiagram-v2 accTitle: Agent loop states for one request accDescr: A request is received, then retrieves evidence, then reasons with the model and reaches a decision. The decision is either a final answer, a tool request that goes through a policy check, or a stop because the step budget is used up. Tool calls return to reasoning. Every path ends in a terminal outcome. state Decide <> [*] --> Received Received --> Retrieving: identity verified Received --> Rejected: input check fails Retrieving --> NoEvidence: nothing found or timeout Retrieving --> Reasoning: passages found Reasoning --> Decide: model reply received Reasoning --> Failed: model call fails after retry Decide --> Answering: final text Decide --> PolicyCheck: tool request and steps left Decide --> StepLimit: tool request and no steps left PolicyCheck --> CallingTool: allow PolicyCheck --> Refused: deny CallingTool --> Reasoning: tool result returned CallingTool --> ToolFailed: tool call fails after retry Answering --> [*] Rejected --> [*] NoEvidence --> [*] Failed --> [*] StepLimit --> [*] Refused --> [*] ToolFailed --> [*]

    How to read this: each rounded box is a state, each arrow is a transition with the condition that triggers it, the diamond is a choice made in one instant, and the black dot and ringed dot are start and end.

    The value of the state diagram is exhaustiveness. Count the exits: seven terminal outcomes (answered, rejected, no evidence, model failure, step limit, refused, tool failure), and the rule that every one of them emits exactly one audit event. If a reviewer can name a situation that has no outgoing arrow, the design has a gap. The loop CallingTool to Reasoning is the only cycle, and the choice node is the only place that tests the step budget, so an agent that keeps asking for tools ends in StepLimit instead of running up cost indefinitely. A state diagram does not replace the sequence: it says which moves are legal, not what the messages are.

    Data-model views

    A data-model view is an entity-relationship diagram. Use it for the data that has to survive a request: audit records, the evaluation dataset, anything a regulator, an on-call engineer or the Eval service will query later. Mermaid's erDiagram writes a relationship as A ||--o{ B : label. Each side has two characters, the outer one the maximum and the inner one the minimum: || exactly one, o| zero or one, }o/o{ zero or more, }|/|{ one or more. A solid line is an identifying relationship (the child cannot exist without the parent) and a dashed line is non-identifying. Attributes are written as type name and may be marked PK, FK or UK.

    Figure 9. Data model of the audit log and the eval dataset

    erDiagram accTitle: Audit log and eval dataset data model accDescr: A conversation has many requests. Each request has retrieved passages, tool calls and audit events. An eval case may be promoted from a real request. An eval run produces one result per eval case. CONVERSATION ||--o{ REQUEST : has REQUEST ||--o{ RETRIEVED_PASSAGE : "used as evidence" REQUEST ||--o{ TOOL_CALL : triggers REQUEST ||--o{ AUDIT_EVENT : emits REQUEST |o--o{ EVAL_CASE : "promoted into" EVAL_RUN ||--o{ EVAL_RESULT : produces EVAL_CASE ||--o{ EVAL_RESULT : "scored in" CONVERSATION { string conversation_id PK string user_ref datetime started_at } REQUEST { string request_id PK string conversation_id FK datetime received_at string outcome string prompt_version string model_id } RETRIEVED_PASSAGE { string request_id FK string doc_id string doc_version int rank float score } TOOL_CALL { string tool_call_id PK string request_id FK string tool_name string idempotency_key string status int attempts } AUDIT_EVENT { string event_id PK string request_id FK string event_type datetime recorded_at } EVAL_CASE { string case_id PK string source_request_id FK string question string expected_outcome } EVAL_RUN { string run_id PK datetime started_at string prompt_version string model_id } EVAL_RESULT { string run_id FK string case_id FK string outcome boolean grounded }

    How to read this: each box is a table, each line is a relationship whose end marks say one or many, and PK and FK mark keys.

    Four design decisions are visible in this picture. The request carries prompt_version and model_id, so an answer can be tied to the exact configuration that produced it, which is what makes a regression traceable. Retrieved passages are stored as doc_id plus doc_version, not as copied text, so the log records what evidence was used without duplicating policy text, and a later question like "which answers cited the old returns policy?" is a query. The audit events hold identifiers and outcomes, not the conversation text; that is a choice this teaching system makes to keep personal data out of the log, and a team with a different retention rule would change it. And an eval case may point back to a real request (source_request_id is nullable, so hand-written cases fit too), which closes the loop between production traffic and the Eval service. Note the tension this creates: the request record keeps no question text, but promoting a request into an eval case copies its question into the dataset, so that step needs its own redaction and consent rule.

    Trust boundaries in diagrams

    A trust boundary is drawn where control changes hands. Microsoft's Threat Modeling Tool documentation says its trust boundaries show "where different entities are in control", and threat-modelling sources draw them on a data flow diagram (DFD). The OWASP Threat Modeling Cheat Sheet says DFDs "visually model a system, its data flows, and interactions with external entities", asks for a clear view of trust boundaries, data flows, data stores, processes and the external entities that interact with the system, and says threat modelling is "ideally performed early in the SDLC, such as during the design phase" and that a model "should be maintained, updated and refined alongside the system". Microsoft's example DFD uses the same elements (an outside entity, a process, a data store). Both sources use STRIDE to ask what can go wrong at each crossing: spoofing, tampering, repudiation, information disclosure, denial of service and elevation of privilege.

    For an AI system the useful extra is that model output and retrieved text are data from the wrong side of a boundary, even though they arrive from components you run. The prompt contains text you did not write (the customer's message, retrieved passages), and that text can influence what the model produces. So the Orchestrator treats the model's reply as a request to be checked, not a command to be executed. That is why the Policy engine sits between the agent loop and the Tool server.

    Figure 10. Trust-boundary view of the policy Q&A assistant

    flowchart LR accTitle: Trust-boundary view accDescr: Five zones are shown. The customer device is untrusted. The edge zone holds the API gateway. The application zone holds the Orchestrator, Model gateway, Retriever, Policy engine and Tool server. The data zone holds the Vector index and Document store. Third parties hold the LLM provider and the ticketing system. Thick arrows cross a trust boundary and carry a numbered label naming the control applied there. subgraph Z1["Zone 1 - Customer device (untrusted)"] Client["Client app"] end subgraph Z2["Zone 2 - Edge"] GW["API gateway"] end subgraph Z3["Zone 3 - Application (our code)"] Orch["Orchestrator"] MG["Model gateway"] Ret["Retriever"] PE["Policy engine"] TS["Tool server"] end subgraph Z4["Zone 4 - Data"] VI[("Vector index")] DS[("Document store")] end subgraph Z5["Zone 5 - Third parties (not ours)"] LLM["LLM provider"] Tick["Ticketing system"] end Client ==>|"B1: TLS and session token"| GW GW ==>|"B2: verified identity, rate limit, size limit"| Orch Orch -->|"may this call run?"| PE Orch -->|"search"| Ret Orch -->|"prompt"| MG Orch -->|"tool call"| TS Ret ==>|"B3: query filtered by user entitlements"| VI Ret ==>|"B3: read approved documents only"| DS MG ==>|"B4: prompt text only, no secrets, no identity"| LLM LLM ==>|"B4: reply treated as untrusted"| MG TS ==>|"B5: service credential, ticket scope only"| Tick

    How to read this: thin arrows stay inside a zone, thick arrows cross a trust boundary, and every thick arrow has a label starting B1 to B5 that names the control at that crossing.

    Use this view as a review checklist: each thick arrow is a place to ask what an attacker or a mistake could do. B1 is spoofing and tampering from an untrusted device. B2 is where identity becomes a verified claim instead of a request header. B3 is the crossing where access control is enforced for retrieval, so one customer's documents never rank for another. B4 is a two-way crossing: outbound text leaves your control, and the reply comes back into it as untrusted data. B5 limits the Tool server's credential to the one thing it needs. The first case file below is a review in which a crossing, between the Orchestrator and a shared Tool server, was missing from the diagram, and with it the control.

    Decision trees

    A decision tree is a flowchart whose nodes are questions and whose leaves are outcomes. Use one when a runtime or design choice has several conditions and people disagree about the order, or when you want a rule a reader can follow without asking the author. Keep questions answerable with yes or no, and make every leaf an action. The tree below answers one question: which diagram to draw.

    Figure 11. Which diagram answers my question?

    flowchart TD accTitle: Choosing a diagram type accDescr: A chain of yes or no questions leads from the thing you need to explain to the diagram type that explains it. If none apply, write a short paragraph instead. Start(["What do I need to explain?"]) Q1{"Is the question who uses it and what it depends on?"} Q2{"Is it about the parts and how they connect?"} Q3{"Is it about the order in which things happen?"} Q4{"Is it about the modes it can be in and what moves it between them?"} Q5{"Is it about what data is stored and how it is related?"} Q6{"Is it about where it runs or where trust changes?"} A1["Context view"] A2["Container view, then a component view for one container"] A3["Sequence diagram, one per scenario"] A4["State diagram"] A5["ER diagram"] A6["Deployment view or trust-boundary view"] A7["Write a short paragraph"] Start --> Q1 Q1 -->|"yes"| A1 Q1 -->|"no"| Q2 Q2 -->|"yes"| A2 Q2 -->|"no"| Q3 Q3 -->|"yes"| A3 Q3 -->|"no"| Q4 Q4 -->|"yes"| A4 Q4 -->|"no"| Q5 Q5 -->|"yes"| A5 Q5 -->|"no"| Q6 Q6 -->|"yes"| A6 Q6 -->|"no"| A7

    How to read this: start at the top, follow yes or no at each diamond, and stop at the first rectangle.

    The questions run from broad to narrow, so a team that skips the context view is pointed to it first. A real design needs several leaves, not one: the tree picks the next diagram, not the only one. It also ends in prose, because sometimes a sentence is the better picture.

    The diagram checklist

    Apply this list to every diagram in a review. Several items come from c4model.com's notation guidance (title, key, labelled lines, stand-alone readability, and attention to black-and-white printing and colour blindness); the node limit and the failure-path rule are house rules for this course.

    Check Pass condition Why
    One-sentence caption A figure title and one sentence say what question the diagram answers A diagram without a question cannot be judged
    Legend Shapes, line styles and zone markers are explained next to the diagram The notation must not depend on the author being present
    No colour-only meaning Anything colour would show is also shown by shape, line style or text Colour fails in print and for colour-blind readers
    About 15 nodes at most Over that, split into an overview and zoom-in views Reviewers stop reading dense pictures
    Consistent names The same component has the same name in every diagram and in the text A renamed box looks like a new component
    Labelled arrows Each arrow has one direction and a label for what travels An unlabelled arrow hides the contract
    Failure paths shown At least one diagram shows timeout, empty result or denial Happy paths hide the real design
    Trust boundaries marked Crossings are visible and name their control This is where data leaks and privilege escalations happen
    Source in the repository The diagram is text next to the code and reviewed with it Diagrams that live elsewhere drift

    Case files

    Two composite case files show these diagrams doing real review work. Both are illustrative scenarios, not real companies.

    • Helpdesk assistant design review: a missing trust boundary. Problem: a B2B support assistant let the model choose the tenant id passed to a shared tool server that could read every tenant. Pattern: draw trust boundaries on the container view and ask what controls each crossing.
    • Billing dispute assistant: retries in three layers. Problem: three layers each retried a failing call, which would multiply load in an outage. Pattern: a runtime sequence with the retry loops visible shows who owns retries and exposes amplification the container view hides.

    Decision framework

    1. If the audience includes anyone who is not an engineer, then start with the context view, because it is the only level that everybody can read; revisit when the set of external systems changes.
    2. If a flow has three or more participants or any asynchronous step, then draw a sequence diagram instead of describing it in prose, because ordering and ownership of retries are invisible otherwise; revisit when a participant is added.
    3. If a component can loop or can end in more than one way, then add a state diagram and check that every state has an exit, because unbounded loops are a cost and safety failure; revisit when the step budget or a terminal outcome changes.
    4. If data crosses from a party you control to one you do not, or model output or retrieved text can trigger an action, then mark a trust boundary and name its control on the diagram, because the crossing is where review time is best spent; revisit when a tool, a data source or a provider is added.
    5. If a diagram passes about 15 nodes, then split it into an overview and a zoom-in, because a reviewer who cannot hold the picture cannot challenge it; revisit when a split view starts to repeat the same boxes.
    6. If the team will change the system faster than it updates slides, then keep diagrams as text in the repository and review them in pull requests, because a diagram not checked with the code is wrong within weeks; revisit if tooling cannot render the text format.

    Comparison of the main options

    Diagram Question it answers Audience Strength Weakness
    Context view Who uses it and what does it depend on? Everyone Fast agreement on scope Hides everything inside
    Container view What are the parts and how do they talk? Engineers and ops Shows what fails and scales separately Silent on order and on deployment
    Component view How is one container organised? Developers Shows where a rule lives Goes stale quickly, so draw it only where it adds value
    Sequence diagram In what order, and who owns the retry? Engineers Exposes timeouts, retries and decisions One scenario per diagram, so you need several
    Deployment view Where does it run and what is the network path? Platform and security Makes egress, zones and counts explicit One per environment
    State diagram What modes and legal transitions exist? Engineers Proves every state has an exit Says nothing about messages
    ER diagram What is stored and how is it related? Engineers, auditors Makes the audit and eval data explicit Says nothing about behaviour
    Trust-boundary view Where does trust change and what controls it? Security and architects Turns a review into a walk along crossings Needs honest zones to be useful
    Decision tree Which choice applies in which condition? Anyone following a rule Removes ambiguity Gets unreadable beyond a dozen questions

    Failure scenarios to design for

    These are failures of the diagramming practice, each with the behaviour a team should define in advance.

    1. The diagram and the system drift apart. Defined behaviour: diagrams are text files in the same repository, changed in the same pull request as the code they describe, and the design-review checklist asks whether the pull request touches a diagram.
    2. The diagram grows past what a reviewer can read. Defined behaviour: when a flowchart passes about 15 nodes it is split into an overview plus zoom-in views, and the overview names which zoom-in covers which box.
    3. Only the happy path is drawn. Defined behaviour: every sequence for a user-facing flow has at least one companion for the empty-result, timeout and denial paths, and the state diagram has no state without an exit.
    4. Names change between diagrams. Defined behaviour: a shared name list (like the table earlier on this page) is part of the design document, and a renamed component is changed everywhere in one commit.
    5. Meaning is carried by colour alone. Defined behaviour: legends describe shape and line style, and the diagram is reviewed once in greyscale before it is accepted.
    6. A trust boundary exists in the system but not in the picture. Defined behaviour: the review walks every outbound arrow from the application zone and asks which boundary it crosses and who holds the credential; an arrow with no answer is a finding.

    Worked example

    Use Figures 4 and 6 to set the timeout budget for the longest path in the system: a customer question that ends in a ticket. A budget is the maximum time each step may take, so that the sum fits inside the deadline the customer experiences.

    Assumptions: these figures are illustrative, not measurements. The customer-facing deadline is 20 s. The gateway and identity step is budgeted at 0.1 s. Retrieval has one attempt with a 1.0 s timeout. Each model call has a 6 s budget in total (including any retry the Model gateway makes), and one call precedes the tool request and one follows the tool result. The Policy engine check is budgeted at 0.1 s. The ticket call has a 3 s timeout, one retry after 0.5 s backoff and no other layer retries.

    Worst-case ticket step: first attempt 3.0 s, backoff 0.5 s, second attempt 3.0 s, so 3.0 + 0.5 + 3.0 = 6.5 s.

    Worst case for the whole request:

    gateway and identity        0.1
    retrieval (1 attempt)       1.0
    model call 1 (tool request) 6.0
    policy check                0.1
    ticket call with 1 retry    6.5
    model call 2 (final text)   6.0
    total                      19.7 s   (deadline 20 s, margin 0.3 s)

    The sum is 0.1 + 1.0 + 6.0 + 0.1 + 6.5 + 6.0 = 19.7 s, which leaves 0.3 s. The margin is the result: it says the design has no room for a second retry anywhere, and that a slower model timeout must be paid for by removing something else. Compare the failure of drawing retries in several layers. If the gateway, the Orchestrator and the Tool server each made up to 3 attempts on the same ticket call, one customer action could reach 3 x 3 x 3 = 27 attempts at the ticketing system. That is also why Figure 6 states in a note that the Orchestrator is the only layer that retries. The AWS Builders' Library makes the same point for a five-deep stack of calls with three retries at each layer, where independent retries increase the load on the database 243x (the article gives the figure without showing the arithmetic), and its best practice is to retry at a single point in the stack.

    A diagram lint for pull requests

    Diagrams are only checked if a machine checks them. The repository's own checker parses every Mermaid block, but a short script can enforce the house rules that parsing cannot: each block must have accTitle and accDescr, and a flowchart should not exceed about 15 nodes. The node count is a heuristic, because it counts ids that are declared with a shape after quoted labels are blanked out, so treat a warning as a prompt to look and not as proof.

    import re, sys
    
    MAX_NODES = 15
    FENCE = "`" * 3
    BLOCK = re.compile("^" + FENCE + r"mermaid\s*\n(.*?)^" + FENCE, re.S | re.M)
    NODE = re.compile(r"(?<![\w-])([A-Za-z]\w*)\s*(?:\(\[|\[\(|\[\[|\[|\(|\{)")
    
    def lint(path):
        text = open(path, encoding="utf-8").read()
        problems = []
        for n, m in enumerate(BLOCK.finditer(text), 1):
            body = m.group(1)
            first = next((l.strip() for l in body.splitlines() if l.strip()), "")
            if "accTitle:" not in body or "accDescr:" not in body:
                problems.append(f"diagram {n}: missing accTitle or accDescr")
            if first.startswith(("flowchart", "graph")):
                ids = set()
                for line in body.splitlines():
                    if line.strip().startswith(("accTitle", "accDescr", "subgraph")):
                        continue
                    line = re.sub(r'"[^"]*"|\|[^|]*\|', '""', line)
                    ids.update(NODE.findall(line))
                if len(ids) > MAX_NODES:
                    problems.append(f"diagram {n}: {len(ids)} nodes, over {MAX_NODES}")
        return problems
    
    if __name__ == "__main__":
        bad = {p: lint(p) for p in sys.argv[1:]}
        for p, errs in bad.items():
            for e in errs:
                print(f"{p}: {e}")
        sys.exit(1 if any(bad.values()) else 0)

    Run it as python lint_diagrams.py content/learning/*.mdx in CI. It skips subgraph lines so that zone titles are not counted as nodes, and it reads the first non-empty line to decide the diagram type.

    Common mistakes

    • Mixing levels in one picture. A box for the whole system next to a box for a single class. Each diagram should have one scope; c4model.com defines a scope for every diagram type.
    • Calling a library a container. In C4 a container is something that must be running for the system to work, an application or a data store. A client library inside the Orchestrator is a component or a code element.
    • Unlabelled arrows. "Uses" tells the reviewer nothing. Say what travels and, between containers, which protocol.
    • A sequence diagram that is only the happy path. If every alt block is missing, the diagram shows what everyone already believes.
    • Treating model output as trusted. A diagram where the arrow from the LLM provider goes straight to the Tool server hides the most important check in the design.
    • Colour as the only legend. Red boxes for "risky" mean nothing in greyscale. Use a shape, a line style or a word.

    Try it

    Time: 30 to 40 minutes. Pick one AI feature you know (or invent a small one, such as a meeting-summary assistant that posts to a chat tool).

    1. Write its one-sentence caption: who asks what, and what the system answers or does.
    2. Draw its context view with at most seven boxes, and its container view with at most 15 nodes, as Mermaid flowcharts.
    3. Draw one sequence diagram for the normal path, and one for a failure path: an empty retrieval or a tool timeout. Put a note at each decision.
    4. Mark every trust boundary on the container view with thick arrows and a label that names the control.
    5. Walk the checklist above, then run node scripts/check-diagrams.mjs <your file> if your file is in this repository, or any Mermaid parser otherwise.

    Done when each diagram has a figure title, an accessible title and description, consistent component names, and you can point to the failure path and the trust boundary for the feature.

    Key takeaways

    • A diagram answers one question for one audience. Pick the type from the question, not from habit.
    • C4 gives four zoom levels and a notation discipline (titles, keys, labelled lines, stand-alone readability). On this site its views are drawn as Mermaid flowcharts with subgraphs.
    • Sequence diagrams carry the runtime decisions: what happens on empty retrieval, who owns the retry, where the idempotency key goes.
    • A state diagram proves a loop terminates and that every outcome has an exit and an audit event.
    • A data-model view makes the audit log and the eval dataset explicit and links production requests to the Eval service.
    • Mark trust boundaries and treat model output and retrieved text as untrusted data. The Policy engine belongs between the agent loop and the tools.
    • Keep diagrams as text in the repository and check them with the code. About 15 nodes is the limit for one view.

    Self-check

    1. A reviewer asks "what happens if search returns nothing?" Which diagram answers it, and what must it show?

    A sequence diagram for the failure path. It must show the Retriever reply (or timeout), the Orchestrator's decision not to call the model, the audit outcome, and what the customer is told. A container view cannot answer it because it has no order and no decisions.

    2. In the C4 model, is a Python library used inside the Orchestrator a container? Why or why not?

    No. A container is an application or data store that has to be running for the system to work (c4model.com adds that it is not about Docker). A library inside the Orchestrator is part of a component or code element, so it appears, if at all, on a component view.

    3. The ticket call times out and the Orchestrator retries. Why does the retry reuse the same idempotency key?

    The first attempt may have reached the ticketing system even though its reply was lost. With the same key the Tool server can return the existing ticket id instead of creating a duplicate, so the retry has the effect of doing the action once.

    4. Three layers each make 3 attempts at a failing call. How many attempts reach the failing service for one user action, and what is the remedy?

    3 x 3 x 3 = 27 attempts. The remedy is to retry at a single point in the stack, with a limit per request and a retry budget, and to return an overload signal that tells other layers not to retry.

    5. Which arrows on a trust-boundary view deserve the most review time in an AI system, and why?

    The ones where data crosses to a party you do not control (outbound prompts, third-party tools) and the ones where untrusted text can trigger an action (model output heading to a tool). Each needs a named control, such as a Policy engine check, entitlement-filtered retrieval or a minimal credential.

    Further reading

    Last verified: 2026-10-21

    Next

    This is the last page of Part A. Part B starts with page 4: Models, Routing and Inference for AI Architects.

    Enroll to keep reading

    Lesson 3 of 13 · AI Architect: From AI Engineer to Expert

    Draw one AI assistant at every level: context, container, component, sequence, deployment, state, data model and trust boundaries, plus a checklist.

    In the rest of this lesson

    • Case files
    • Decision framework
    • Comparison of the main options
    • Failure scenarios to design for
    • Worked example
    • A diagram lint for pull requests

    Enrolling is free. It unlocks every lesson in AI Architect: From AI Engineer to Expert, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments