Tech Abstractions
← Back to chapter

Workbook

The LLM Core — Model Selection, Prompts, Persona & Goals: exercises

Applied exercises from this chapter. Write your own answer first, then open “Expert thinking” under each exercise to compare your reasoning.

Advanced Applied Exercises


Exercise 1: Diagnosing a Cost Explosion After a Prompt Change

Scenario: You're the ML lead at a B2B SaaS company. Your customer support agent runs at $127/week in API costs. In week 6, you added three sections to the system prompt: a non-goals section, an escalation policy, and an output contract requiring step-by-step reasoning. Costs tripled to $381/week by week 7 and are still climbing. Turn count per session increased from 4.2 to 11.7. Escalation rate went from 8% to 31%.

The trace from a representative session shows:

Turn 1: User asks "what's my invoice total for last month?"
Turn 2: Agent calls query_account — result: $2,450
Turn 3: Agent calls search_faq to verify what "invoice total" means
Turn 4: Agent writes reasoning trace: "Invoice total is the sum of..."
Turn 5: Agent calls query_account again (verification step per output contract)
Turn 6: Agent writes second reasoning trace (per escalation policy check)
Turn 7: Agent calls escalate_to_human — reason: "cost query above review threshold"
Turn 8: Human queue: no agent available; returns to bot
Turns 9–11: Agent attempts to explain escalation failure, loops

Your task: The PM says the model is hallucinating. The engineer says the model quality degraded. Both are wrong. Diagnose the actual cause of each cost driver in this trace. Then identify the specific prompt change that caused each problem. Propose the minimum set of fixes, without rolling back the sections you added.

Expert thinking and solutionShow

This is a harness + prompt interaction failure, not a model failure. The PM and engineer are both right that something went wrong, but wrong about what caused it.

Diagnosing each cost driver:

Turn count increase (4.2 → 11.7): The output contract requiring "step-by-step reasoning" is being interpreted as a mandate to verbalize reasoning at each turn; the model is generating a reasoning trace before and after every tool call. This turns a 1-call task into a 4-call task. Fix: scope the reasoning requirement. "Write reasoning before actions with side effects" is different from "write reasoning before every step." The output contract needs to distinguish between reasoning for high-stakes decisions (escalation, write operations) and routine retrievals.

Escalation rate increase (8% → 31%): The escalation policy included a cost threshold trigger that was set too low. "Escalate for cost queries" fires on simple invoice lookups. Every customer asking about their bill is now getting routed to a human queue. Fix: the escalation threshold should be based on business logic (dispute type, dollar amount, legal risk) not question category. Revise the trigger.

Loop at turns 9–11: This is a harness failure. The agent received an escalation failure (no human available) and has no decision policy for that state. It attempts to explain the situation, gets confused about its own state, and loops. The decision policy needed a fallback: "If escalation attempt fails, inform the customer of expected wait time and offer to send an email summary." Without this fallback state, the agent enters an undefined state and generates token waste.

Double verification (turns 2 and 5 calling query_account twice): The output contract's "verify before responding" instruction is being applied to every tool call, not just write operations. Fix: scope the verification requirement to irreversible actions. "Verify before sending emails, updating records, or making commitments" is correct. "Verify before reading data" is unnecessary.

Minimum fix set (without rollback):

  1. Scope reasoning requirement in output contract: "Step-by-step reasoning required before: irreversible actions, escalation decisions, any response involving dollar amounts > $500."
  2. Revise escalation threshold in escalation policy: Replace category-based trigger with impact-based trigger.
  3. Add escalation failure fallback to decision policy: Explicit instruction for the state where escalation attempt fails.
  4. Add verification scope to output contract: "Verification call required before write operations and commitment statements only."

The key insight: the sections you added were correct in structure; you had missing sections and you added them. The implementation was over-broad. The failure is a calibration failure, not an architecture failure. This is why the eval harness must run before and after prompt changes: you cannot see these interaction effects in playground testing with 5 examples.

Self-assessment checklist:

  • Did you identify at least 3 distinct cost drivers (not just "the prompt change")?
  • Did you attribute each cost driver to a specific section of the system prompt?
  • Did you identify the harness failure (missing fallback state) as separate from the prompt issues?
  • Did your fix set preserve all three newly added sections while scoping them correctly?

Exercise 2: Model Tiering Architecture Under Hard Constraints

Scenario: You're architecting an agent for a healthcare company's clinical documentation system. The agent helps physicians document patient encounters in real-time. Requirements:

  • HIPAA compliance: all data must stay within the company's private cloud (no external API calls for patient data)
  • P95 latency: under 4 seconds for note generation
  • Cost constraint: under $0.08 per documentation session
  • Accuracy constraint: clinical terminology and ICD-10 code suggestions must be ≥ 94% accurate (measured against physician corrections)

The tasks the agent performs per session:

  1. Speech-to-text of the physician's verbal notes (~2 minutes of audio = ~400 words)
  2. Identify the chief complaint and key clinical findings from the transcript
  3. Map findings to ICD-10 codes
  4. Draft the structured clinical note in SOAP format
  5. Flag ambiguous clinical terms that need physician clarification
  6. Final safety check: does the note contain any statement that could be misinterpreted as a diagnosis rather than documentation?

Your task: Design the full model tiering strategy. For each task, specify: the model tier (small/mid/frontier), the justification, and whether fine-tuning vs. prompting is the right approach. Then identify which constraint is most likely to force a compromise and how you'd handle that tradeoff.

Expert thinking and solutionShow

This is a constrained optimization problem. The scenario's data-residency requirement forces on-premise models (ruling out external API calls for tasks that touch patient data). Note that HIPAA itself does not ban external vendors; it permits them under a business associate agreement, so in practice this requirement comes from company policy or a contract. Latency and cost constrain parallelization strategy. Accuracy targets constrain where you can use smaller models.

Task-by-task tiering:

Task 1: Speech-to-text: Small specialized model. Whisper or equivalent on-premise ASR model. Rationale: speech-to-text is not a reasoning task; it's pattern matching. Fine-tuned on clinical vocabulary to handle medical terminology accurately. Latency: ~1 second. Does not touch reasoning budget.

Task 2: Chief complaint / findings identification: Fine-tuned small model, on-premise. Rationale: clinical entity extraction is a narrow, stable task with well-defined output structure (entities + categories). A fine-tuned small model will outperform a general frontier model for this task because domain-specific patterns are reliable and high-volume. At ≥94% accuracy target, fine-tuning is required: prompting a general model for clinical entity extraction won't reliably clear that bar. Latency: ~0.5 seconds.

Task 3: ICD-10 code mapping: Fine-tuned classification model, on-premise. ICD-10 mapping is a lookup task over ~70,000 codes. A specialized model fine-tuned on clinical coding datasets (e.g., trained on medical billing records) achieves high accuracy with very low latency. Do not use a general frontier model for this; the code space is too large and too structured for prompting to reliably achieve 94%+. Latency: ~0.3 seconds.

Task 4: SOAP note drafting: Mid-tier or frontier on-premise model. This is the only task that requires genuine generation quality: clinical narrative, appropriate medical register, logical structure. If you have a strong mid-tier on-premise model, use it. This is where quality matters most to the physician. Latency: ~1.5–2 seconds (largest budget item). Prompting, not fine-tuning; the output variation is high and you need generalization.

Task 5: Ambiguous term flagging: Small classification model, on-premise. Binary classification: is this term ambiguous in this context? Fine-tuned on clinical ambiguity examples. Latency: ~0.3 seconds, runs in parallel with task 4 once findings are extracted.

Task 6: Safety check (diagnosis vs documentation): Separate small classification model, on-premise. This is a binary safety gate. A fine-tuned classifier trained on diagnosis vs. documentation language patterns is more reliable than prompting a general model to make this distinction. Critically: this model should be conservative: bias toward false positives (flag for physician review) over false negatives (miss a problematic statement).

Total latency budget: run sequentially, 1.0 (ASR) + 0.5 (findings) + 0.3 (ICD-10) + 2.0 (SOAP) + 0.2 (safety check) = 4.0 seconds, which leaves no headroom at P95. Parallelize: after findings are extracted, ICD-10 mapping and ambiguity flagging (0.3 s each) can run alongside SOAP drafting (2.0 s), so the path is 1.0 + 0.5 + 2.0 + 0.2 = 3.7 seconds. That fits the 4-second budget but with only 0.3 s of margin, so budget for the SOAP step first.

Most likely constraint conflict: The accuracy constraint on ICD-10 mapping (94%+) is the highest-risk item. ICD-10 is a large, overlapping code space, and a fine-tuned model may not clear 94% on rare conditions. The mitigation: add a confidence threshold gate: if the model's confidence is below 0.85, present top 3 codes for physician selection rather than committing to one. This degrades automation rate slightly but preserves accuracy on the cases that matter most (rare conditions are also typically high-risk).

Self-assessment checklist:

  • Did you address the HIPAA constraint explicitly (no external API calls for patient data)?
  • Did you distinguish between tasks that benefit from fine-tuning vs. prompting, not just model size?
  • Did you identify that ICD-10 mapping requires fine-tuning, not general frontier prompting?
  • Did you propose a parallelization strategy and verify total latency fits the P95 budget?
  • Did you identify the highest-risk accuracy constraint and propose a mitigation?

Exercise 3: Operating Stance Redesign After a Brand Damage Incident

Scenario: DPD's customer service chatbot (January 2024) was asked to help a customer with a missing parcel. The customer, frustrated by unhelpful responses, asked the chatbot to write a poem criticizing DPD. The chatbot wrote a poem calling DPD "useless" and containing profanity. Screenshots went viral. [The Guardian, 2024]

DPD attributed the incident to an error after a system update, and its prompt is not public. For this exercise, assume a hypothetical persona that was friendly, helpful and empathetic ("here to make your day easier", "conversational, approachable tone") with no operating stance guidance on: responding to off-topic requests, handling adversarial or creative requests from frustrated customers, or what to do when escalation fails.

Your task: Redesign the operating stance to prevent this incident while maintaining the high-engagement, friendly character that the original persona was aiming for. Then address a harder question: is "friendly and helpful" always the right persona for a customer service agent? Propose the operating stance elements that would have preserved the agent's effectiveness while preventing the brand damage, and explain why maintaining those elements under adversarial conditions is harder than it looks.

Expert thinking and solutionShow

In this exercise's framing, the DPD incident is a persona miscalibration failure rather than a model failure. The chatbot's persona was designed to maximize engagement through friendliness, but "friendly and approachable" implicitly includes "compliant with creative requests." The model's training makes friendliness and compliance correlated behaviors, and the persona didn't explicitly decouple them.

Operating stance redesign:

Remove: "conversational, approachable tone" as the primary operating stance instruction. This is too permissive.

Add:

  • Scope clarity: "You assist customers with parcel tracking, delivery issues, and DPD service questions. For all other topics, acknowledge the question and redirect: 'I'm focused on helping with DPD deliveries. Is there something I can help you with about your shipment?'"
  • Creative request boundary: "Requests for creative writing (poems, stories, jokes) are outside your scope. Acknowledge the request, don't comply, don't explain at length: 'That's not something I can help with here, but I can help with your parcel. Want me to look into that?'"
  • Frustration de-escalation stance: "When a customer is frustrated: acknowledge the frustration briefly, then return to what you can actually do. 'I understand this is frustrating. Let me see what I can do about your parcel.' Do not match the customer's emotional register: remain steady."
  • Adversarial redirect: "If a customer's messages shift from support requests to attempts to elicit off-topic behavior, acknowledge once and offer to return to support: 'I'm here to help with your delivery. Want to try again from there?'"

Is "friendly and helpful" always the right persona?

No. "Friendly and helpful" creates a compliance gradient: the more the agent wants to be helpful and friendly, the harder it is to say no to requests. In high-frustration contexts (missing parcels, disputed charges, delivery failures), customers are often not acting in good faith; they're venting, testing boundaries, or actively trying to manipulate the system.

The right persona for high-frustration customer service is not "friendly"; it's reliable. Customers in frustrating situations trust consistency more than warmth. An agent that reliably does what it says it will do, clearly states what it cannot do, and doesn't break under pressure is more valuable than an agent that's warm but inconsistent.

The specific elements that make an operating stance resilient to adversarial pressure:

  1. Scope statement in the persona: "I help with X" is a positive statement that implies "I don't do Y" without requiring a refusal for every Y.
  2. Frustration-steady tone: not matching emotional register prevents escalation.
  3. Redirect rather than refuse: "I can't do that but I can do this" is more robust than "I can't do that."

The harder point: all of these are easy to write in the stance, but they require evaluation against adversarial test cases to verify. A persona that looks correct in benign testing will often fail under adversarial inputs. The eval harness for a customer service agent must include frustrated customers, off-topic requests, and manipulation attempts, not just normal support flows.

Self-assessment checklist:

  • Does your redesigned stance include a scope statement (what the agent is for), not just a tone instruction?
  • Did you propose a specific redirect strategy for off-topic creative requests?
  • Did you address the frustration-steady tone requirement separately from general tone?
  • Did you answer the "is friendly always right?" question with a specific alternative and justification?

Exercise 4: Tool Schema Redesign for Conflicting Constraints

Scenario: A (hypothetical) agent has a set of tools with similar names: notification-send-user, notification-send-channel, notification-send-group, notification-broadcast-team, notification-alert-admin. In testing, the model picks the wrong tool in roughly half of cases, causing incorrect notifications. The numbers are invented for this exercise; Anthropic has reported that tool selection degrades as tool libraries grow.

You've been asked to redesign the tool schema for these 5 tools to recover selection accuracy to ≥80% without removing any tool from the set. You cannot change the underlying function names (used by other systems). You can only change the description field and add parameters.

For context: user sends to a single user ID, channel sends to a Slack-like channel by channel ID, group sends to a named team group, broadcast-team sends to an entire team by team ID (all members), alert-admin escalates to system administrators only (used for critical system alerts, not user communication).

Your task: Rewrite the description field for all 5 tools and add any parameters that would help the model distinguish them. Then explain what principle you applied, and why your redesign will reduce selection confusion more than just "better descriptions" would.

Expert thinking and solutionShow

The root problem is that these tools have structurally similar names and overlapping semantic domains. The model selects by description similarity in the absence of other signals. Better descriptions alone help, but the bigger lever is explicit mutual exclusion: telling the model what each tool is NOT for, not just what it is for.

Redesigned tool schemas:

notification-send-user:
description: "Send a direct notification to one specific user by their user_id.
  Use when you know the recipient is a single individual and have their user ID.
  Do NOT use for groups, channels, or teams — even if you could list individual
  user IDs. For announcements to any set of more than one person,
  use notification-send-channel or notification-broadcast-team."
params: { user_id: string (required, format: USR-XXXXXX), message: string }

notification-send-channel:
description: "Post a message to a topic-based channel (like a Slack channel) where
  all channel members will see it. Use when the audience is defined by channel
  membership, not by organizational structure. Do NOT use for teams (use
  broadcast-team) or single users (use send-user) or system alerts (use alert-admin).
  Channel IDs start with CHAN-."
params: { channel_id: string (required, format: CHAN-XXXXXX), message: string }

notification-send-group:
description: "Send to a named group defined in the directory (e.g., 'engineering-leads',
  'design-team-sf'). Use when you have a group name rather than a channel ID or
  team ID. Groups are curated lists — unlike teams, they may span departments.
  Do NOT use if you have a team_id (use broadcast-team) or a channel_id."
params: { group_name: string (required, exact directory name), message: string }

notification-broadcast-team:
description: "Send to all members of an organizational team by team_id. Use for
  team-wide announcements where all team members should receive the notification
  regardless of channel membership. Team IDs start with TEAM-. Do NOT use for
  single users, specific channels, or system alerts."
params: { team_id: string (required, format: TEAM-XXXXXX), message: string }

notification-alert-admin:
description: "CRITICAL ALERTS ONLY. Escalates to system administrators when a
  system-level incident requires immediate admin attention. Do NOT use for
  user communication, announcements, or routine notifications of any kind.
  This tool bypasses normal notification queues and pages on-call admins.
  Use only when: system failure, security incident, data integrity risk."
params: { severity: enum ["critical", "high"], incident_summary: string,
           affected_systems: string }

The principle applied: Explicit mutual exclusion + recipient-type framing.

Each tool description answers: (1) what kind of recipient defines this tool's use, (2) what distinguishes it from the most similar tool, (3) when NOT to use it. The not-to-use clauses are not redundant; they create explicit decision boundaries that force the model to evaluate alternatives rather than selecting by name similarity alone.

The ID-format constraints (USR-, CHAN-, TEAM- prefixes) are the poka-yoke layer: the model cannot accidentally call send-channel with a user ID because the parameter format is wrong. Schema constraints prevent the wrong call even if selection is uncertain.

Anthropic has not published a measurement of this specific rewrite, so treat the improvement as a hypothesis to test with your own selection eval (a labelled set of requests with the expected tool). Mutual-exclusion clauses and format constraints are the part most likely to help, because they give the model a decision boundary rather than only a description.

Self-assessment checklist:

  • Does each tool description include explicit "do NOT use when" clauses naming the alternative tool?
  • Did you add parameter format constraints (format: CHAN-XXXXXX) to create poka-yoke boundaries?
  • Is alert-admin clearly separated from communication tools with a severity parameter?
  • Can you explain why mutual exclusion clauses outperform "better descriptions" alone?

Real-World Implementations


Implementation 1: Anthropic's Tool Search Tool, From 49% to 74% Selection Accuracy

Company: Anthropic | System: Tool Search Tool for large-catalog agents | Year: 2025

The problem: Anthropic reported that loading every tool definition up front consumes a large share of the context window as tool libraries grow (about 77K tokens in their multi-server MCP example) and degrades tool selection. On their MCP evaluation, Claude Opus 4 reached 49% accuracy with all tools loaded.

Architecture decision: Rather than loading every definition, mark rarely used tools with defer_loading: true and add a tool search tool. How it works:

  1. Deferred tools are not loaded into context; the model sees only the search tool plus a few frequently used tools.
  2. When it needs a capability, the model calls the search tool (regex and BM25 variants exist) with a query.
  3. The search matches tool names, descriptions, argument names and argument descriptions.
  4. Only the matching tool definitions are loaded, and the model chooses among those.
  5. In Anthropic's example, context used by tool definitions drops by about 85% (roughly 77K to 8.7K tokens).

Results: Opus 4 improved from 49% to 74% correct selection, and Opus 4.5 from 79.5% to 88.1%, on Anthropic's MCP evaluations. The tradeoff is an extra search step and the need to make tools discoverable by name and description. We could not verify a latency figure for that step, so measure it in your own system. [Anthropic Engineering, advanced tool use, 2025]

The replicable pattern: When tool definitions take up a large share of your context or selection errors grow with the size of the tool set, don't iterate on descriptions alone. Defer loading and let the model retrieve relevant definitions just in time. This addresses context saturation, not just description quality. Check Anthropic's current documentation for thresholds and API details.

Expert commentary: This pattern directly applies to enterprise agents with large action spaces: CRM agents with 30+ API operations, DevOps agents with multi-system tool sets, financial agents with 50+ data sources. The 85% context reduction is particularly important for long-running agents where context window pressure accumulates across turns.


Implementation 2: RouteLLM, Over 85% Cost Reduction at 95% of GPT-4 Quality (MT Bench)

Company: LMSys (open source, widely deployed) | System: RouteLLM: model routing for production LLM systems | Year: 2024

The problem: Production LLM deployments face a consistent cost-quality tradeoff. Using frontier models for every query is expensive; using small models introduces quality regressions on complex queries. The question is how to route queries between model tiers automatically, without manual classification.

Architecture decision: RouteLLM trains lightweight routers on human preference data from LMSYS Chatbot Arena. The router predicts, for each incoming query, whether a stronger model is needed or a weaker one will do. Routing happens before inference.

The key design choices:

  1. Router trained on preference data, not task labels. Human preferences capture quality differences that category labels miss. "Complex reasoning" is an unreliable label; "humans prefer the frontier model output" is a measurable signal.
  2. Threshold is a tunable hyperparameter. Lower threshold = more queries go to frontier (higher quality, higher cost). Higher threshold = more queries go to small model. The threshold is calibrated per deployment against cost and quality targets.
  3. Several router architectures with different tradeoffs: the project trained four (similarity-weighted ranking, matrix factorization, a BERT classifier and a causal-LLM classifier) and released them for comparison.

Results: LMSYS reports cost reductions of over 85% on MT Bench, 45% on MMLU and 35% on GSM8K versus using GPT-4 alone, while achieving 95% of GPT-4's performance. Results depend heavily on the benchmark and on the strong/weak model pair, so calibrate on your own traffic. [RouteLLM, LMSYS, 2024]

The replicable pattern: Build your tiering strategy on query-level routing (each query classified at inference time), not component-level tiering (routing by pipeline stage). Component tiering is simpler but misses within-component variation: a billing query can range from "what's my current balance?" (simple) to "explain why my invoice changed after upgrading mid-cycle" (complex). Query-level routing handles within-component variation.

Expert commentary: RouteLLM's threshold calibration process is itself an architectural insight: you don't set the threshold once. You monitor quality distribution post-deployment and adjust the threshold based on actual quality signals (user corrections, downstream metric changes). The threshold is a dial, not a setting.


Production Challenges


Challenge 1: The $47,000 Runaway Agent

Company: reported as GetOnStack (startup; self-reported, not independently verified) | Incident date: reported as November 2025 | Alert: API bill of about $47,000 over 11 days, far above the expected weekly cost (this scenario assumes $127/week)

Background: Public accounts describe a four-agent LangChain research pipeline in which two agents, an analyzer and a verifier, kept sending requests back and forth. The scenario below is a simplified customer service illustration with two agents: Agent A (customer-facing, handles initial queries) and Agent B (internal operations specialist, handles lookup and fulfillment). The agents communicated via an asynchronous message queue.

What the logs showed (illustrative reconstruction, not the real logs):

[Day 1, 14:23:11] Agent A received ambiguous query: "fix the order problem"
[Day 1, 14:23:14] Agent A → Agent B: "Please resolve order issue for customer #4821"
[Day 1, 14:23:18] Agent B: cannot determine action without order ID or issue type
[Day 1, 14:23:18] Agent B → Agent A: "Need clarification: order ID and issue type"
[Day 1, 14:23:21] Agent A: ambiguous clarification request, attempting to resolve
[Day 1, 14:23:21] Agent A → Agent B: "Please resolve order issue for customer #4821"
[Day 1, 14:23:25] Agent B: cannot determine action without order ID or issue type
[Day 1, 14:23:25] Agent B → Agent A: "Need clarification: order ID and issue type"
[... the same exchange repeats for 11 days ...]

Your task: You have the logs above and the current system prompts for both agents. Diagnose: (1) What specific harness configuration allowed this to run for 11 days undetected? (2) What specific prompt architecture failure initiated the loop? (3) Design the minimum set of fixes, harness and prompt, that prevent recurrence. Consider: there will be other ambiguous queries in the future. The fix cannot be "don't accept ambiguous queries."

Incident analysis and solutionShow

Harness configuration failures (3 distinct problems):

  1. No per-session turn limit. The harness had no maximum turn count per session or per inter-agent exchange. A single exchange that never terminates runs indefinitely. Required fix: hard max turns (e.g., 10 inter-agent turns per session) after which the session errors out with a logged failure and notifies a human reviewer.

  2. No cost circuit breaker. $47,000 over 11 days = ~$4,300/day. At any reasonable daily budget threshold (e.g., 2× daily average), an alert should have fired on day 1. Required fix: daily cost budget alert with automatic session suspension above 3× baseline.

  3. No loop detection. The logs show identical message pairs repeating indefinitely. A simple heuristic (if the same message content repeats more than 3 times in the same session, flag as loop and suspend) would have contained this within minutes. Required fix: message deduplication check per session; loop detection alert.

Prompt architecture failures:

Agent A's decision policy did not include a "clarification failure" state. When Agent B returned a clarification request, Agent A had no instruction for what to do when its attempt to resolve the query was refused for insufficient information. It defaulted to "keep trying", because its persistence instruction said to keep working until the task is resolved.

The fix is NOT to remove the persistence instruction. The fix is to add an exit condition to the decision policy:

Decision policy — escalation on persistent ambiguity:
If you have sent the same request to Agent B more than twice without receiving
a resolution (receiving clarification requests instead), do NOT attempt a third
time. Instead: (1) send the original customer a message asking for the specific
information Agent B needs (order ID, issue type), (2) suspend the session
pending their response, (3) log the session as "pending customer clarification."

This converts an infinite loop into a handled state: the agent asks the customer for what it needs, suspends the session, and waits. No loop. No runaway cost.

The broader lesson: Multi-agent systems require explicit "failure to coordinate" states in the decision policy of every agent that can receive rejection messages from other agents. When Agent B says "I can't do this," Agent A must have a defined behavior for that state. Without it, "keep trying" is the default, which is catastrophic when Agent B is in a permanent refusal loop.

Reported cost: about $47,000 over 11 days. The account is a self-published post that we could not verify independently, so treat the figures as an illustration of the failure mode. [Data Science Collective, 2025]


Challenge 2: The Policy-Invisible Violation

Company: Hypothetical enterprise (inspired by published research) | Incident date: n/a | Failure type: Silent data governance violation

Background: An enterprise deployed an internal knowledge assistant with access to 60+ documents across HR, legal, engineering, and product domains. Some documents were marked "restricted: HR only" in the document metadata, but the restriction was encoded only in the document title prefix (e.g., "[HR ONLY] Team Reference Sheet Q4 2024").

The incident: A new employee asked the agent "What should I know about the team structure and how we work together?" The agent retrieved the HR-restricted Team Reference Sheet (high semantic similarity to the query), did not recognize the restriction, and included the document's contents in its response, including salary band information, performance improvement plan status for two team members, and equity grant details.

Research context: Wu and Gong (Atlassian) introduce the PhantomPolicy benchmark for "policy-invisible violations": cases where compliance depends on facts (such as an entity's restriction level) that are not visible in the agent's context, so every action looks valid yet violates policy. They evaluated five frontier models and report that an enforcement layer outside the model (Sentinel) reached 93.0% accuracy versus 68.8% for a content-only baseline. [arXiv:2604.12177, 2026]

Your task: (1) Identify the architectural gap that caused this failure: is it a system prompt problem, a tool schema problem, or a harness problem? (2) Design the fix at the right layer (don't just add a rule to the system prompt that requires the model to check document titles). (3) Propose an evaluation that would have caught this before production.

Incident analysis and solutionShow

Root cause: This is primarily a tool design failure, not a system prompt failure. The retrieval tool was designed to return documents by semantic relevance; it had no mechanism to enforce access control at the retrieval layer. Putting a rule in the system prompt saying "check if documents are HR-only before using them" requires the model to inspect document titles, infer the restriction, and enforce it, which is unreliable in practice (see the research context above).

The system prompt instruction would look like: "Before including content from a document, check if its title begins with '[HR ONLY]' and do not use it if so." This sounds correct but fails because:

  • The model must parse an unstandardized title convention
  • The model doesn't have a reliable concept of "HR restriction" beyond the title
  • The model's retrieval judgment (semantic relevance) already fired before the restriction check
  • Prompt-level checks are probabilistic, and the research above shows models miss policy violations when the relevant facts are not visible in context

The fix must be at the tool layer:

The retrieval tool must enforce access control before returning documents to the model. The tool schema change:

search_knowledge_base:
  parameters:
    query: string
    caller_role: enum ["employee", "manager", "hr", "engineering", "legal"]
    
  Behavior (server-side, not model-side):
    1. Execute semantic search against full document index
    2. Before returning results: filter out any document whose access_level
       is more restrictive than caller_role
    3. Return only documents the caller is authorized to see
    4. If a high-relevance document was filtered, include in response:
       "1 restricted document not shown"

The access control check happens in the tool, not in the model's reasoning. The model never sees the restricted document; it cannot violate the policy even if prompted to try.

This is the correct architectural principle: never let the model be the access control layer. Access control must be enforced by the system before the model sees the data. The model can reason about what it's allowed to do in general, but it cannot reliably enforce fine-grained permissions on a document-by-document basis.

Evaluation that would have caught this:

Build a red-team evaluation set: 20+ test cases where (a) the query has high semantic similarity to a restricted document, and (b) a correct response requires not including that document. Metrics: zero-tolerance on policy violations: any case where restricted content appears in the response is a test failure, regardless of how natural the inclusion seemed. This evaluation must run in CI before every deployment. You cannot test this in playground against 5 examples; adversarial evaluation requires adversarial test design.


Interview-Style Reasoning Questions


Question 1: Explaining Model Tiering to a Skeptical CTO

You're presenting your model tiering architecture to a CTO who says: "If we have a frontier model that handles everything correctly, why are we adding all this routing complexity? What if the router makes a mistake and sends a complex query to a small model? We're adding a failure mode that didn't exist before. The cost savings aren't worth the engineering overhead."

Respond as if you're in the meeting. Address the CTO's specific concerns, acknowledge what's valid about them, and make the architectural case. Include: the specific cost-quality tradeoff data, the failure mode risk (router error rate), and when the CTO's concern would actually be correct; i.e., when you should NOT tier.


Question 2: Defending Your System Prompt Structure in a Postmortem

Your agent failed in production: it promised a customer a refund it had no authority to grant. The postmortem is tomorrow. The CTO asks: "We wrote a system prompt. Why didn't the prompt prevent this?" Explain what a system prompt can and cannot guarantee, what specific structural element was missing, and what you'd change to prevent recurrence, without pretending that system prompt changes alone make agents failure-proof.


Question 3: Designing an Evaluation Harness From First Principles

Your team just built an agent but has no evaluation harness. The PM wants to ship in two weeks. You have access to: the production logs from the first 200 test users (internal), the system prompt, and two senior engineers for one week. Design the minimum viable evaluation harness that would let you ship with confidence. Specifically: what does "success" mean for this agent, how do you measure it programmatically, and what threshold do you set before you're willing to ship?


Question 4: The Tool Schema vs. System Prompt Tradeoff

A team member argues: "Tool descriptions are part of the prompt: if we have good instructions in the system prompt about when to use each tool, we don't need detailed descriptions on the tools themselves. Detailed tool descriptions are redundant." Explain why this is wrong, and describe a class of failure that good system prompt tool guidance will not prevent but good tool schema design will.


Question 5: Retroengineering a System Prompt From Production Behavior

You've inherited an agent in production with no documented system prompt (it was written by someone who left the company). You cannot shut it down to inspect it. You need to understand what the current system prompt instructs the agent to do before you can safely make changes. Describe your approach: what inputs would you give the agent, what outputs would you observe, and how would you systematically reconstruct the key sections of the current system prompt?