There are a lot of AI model announcements these days, but every now and then one lands that feels immediately practical. Claude Haiku 5.5 is one of those releases.
Anthropic’s marketing says it’s their "cheapest, fastest, and most capable small model ever." New model, now with extra adjectives. But the numbers behind the press release are actually interesting: 75% cheaper than Haiku 4.5, sub-second response times for most requests, and speed benchmarks showing over 30% latency reduction in customer eval suites.
The more important part is that this isn’t just incremental improvements on an already-fast model — it’s designed to make high-volume work actually economically viable at scale. The kind of thing where you might have used a rule-based system or a legacy small model before, but now you can use a flagship-level reasoner without the bill spiraling.
Enough marketing though. Let’s actually call Haiku 5.5 from a .NET console app and see what it looks like in practice for real C# workloads.
What Actually Shipped
A few facts worth knowing before you touch any code:
- 75% cheaper than Haiku 4.5 on average. For prompts under 100k tokens (90% of use cases), input costs are $0.10/M and output is $0.50/M — a 90% price cut that directly enables workloads where you previously couldn’t afford to run Claude at all.
- Sub-second responses for most requests. Asana reports over 30% reduction in latency for task completions and up to 2.5x faster inference per agent turn compared to their current model — noticeable when users are waiting in real time.
- Effort controls now, first time on Haiku. You can tune cost against intelligence with the effort parameter (low → max) instead of swapping models for different tasks. This matters for workflows where you want granular control over cost per request without model routing logic.
- 1M token context window. Process entire technical manuals or large document batches in a single prompt — useful when your automation needs to reason across big datasets without chunking.
- Sharper coding and tool use than Haiku 4.5. Anthropic’s benchmarks show Haiku 5.5 scoring equivalent to Claude 3 Opus (their previous flagship) across general knowledge, expert reasoning, and coding tasks — while running at the speed of a lightweight model.
- Available on Microsoft Foundry. Ships as a serverless deployment — claude-haiku-5-5 inside your own Foundry resource, billed through Azure, with Entra ID authentication for enterprise identity management.
Why This Matters for .NET Developers Specifically
The use cases Anthropic and their customers are calling out map directly onto real .NET workloads where speed and cost matter:
- High-volume customer support bots. Processing millions of requests per week without the bill spiraling — Box reports handling 8M calls/week on similar use cases, with Haiku 5.5 scoring higher accuracy at half the latency of previous models.
- Real-time in-app assistants. Chat interfaces where users expect sub-second responses — the kind of thing where a slow model feels broken even if it’s more accurate.
- Subagent orchestration in multi-agent systems. Pairing Haiku 5.5 with Opus 5.5 or Sonnet 5.5, letting the bigger model plan complex work while Haiku handles repetitive subtasks like summarization, classification, routing, and database queries — making it practical to run many agents in parallel.
- Browser and desktop automation. Form filling, data entry, moving information between applications — tasks that benefit from speed and cost efficiency at scale rather than maximum reasoning depth.
- Focused coding work. Direct edits, small specific changes across many files, applying consistent refactors — the kind of bounded task where you don’t need Opus-level intelligence but do need reliability.
One thing worth knowing up front: Claude doesn’t use Azure.AI.OpenAI or Microsoft.Extensions.AI‘s OpenAI connector. It has its own official SDK — Anthropic, plus a Foundry-specific Anthropic.Foundry package for Azure-native authentication. As always: no Python, no notebooks — just C# and dotnet run.
Getting Started
Claude ships its own official .NET SDK, and Foundry gets its own package on top of it — this is not the Azure.AI.OpenAI pattern you might already have wired up for GPT models in Foundry.
|
1 2 3 4 5 6 7 8 9 |
dotnet new console -n Claude-Haiku-5.5-CSharp-Demo --framework net10.0 cd Claude-Haiku-5.5-CSharp-Demo dotnet add package Anthropic.Foundry --version 0.7.1 dotnet add package Anthropic --version 12.50.0 dotnet add package Azure.Identity --version 1.21.0 dotnet add package Microsoft.Extensions.Configuration.UserSecrets --version 10.0.0 dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables --version 10.0.0 dotnet user-secrets init dotnet user-secrets set "AZURE_AI_FOUNDRY_RESOURCE" "your-foundry-resource-name" |
Deploy Claude Haiku 5.5 from the Foundry Model Catalog to your Foundry resource — same process as any other model. You only need your resource name, not an API key, since we’re authenticating with Entra ID.
No API keys here. Anthropic.Foundry supports both API key auth and Microsoft Entra ID token auth. This post uses Entra ID exclusively via DefaultAzureCredential — no key to generate, store, or rotate. Make sure your identity (user, managed identity, or service principal) has the Cognitive Services User or Foundry User role on the resource.
One Program.cs, Six Use Cases
The program runs case 1 by default. Pass 1–6 to select a case, or all to run them sequentially. For example, dotnet run — 2 runs the effort comparison, and dotnet run — all runs the full demo. Live calls incur charges — use caching if you’re just experimenting.
Here’s the shared client setup and entry point:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
using System.Text.Json; using Anthropic.Foundry; using Anthropic.Models.Messages; using Azure.Identity; using Microsoft.Extensions.Configuration; var selectedCase = args.FirstOrDefault()?.ToLowerInvariant() ?? "1"; if (selectedCase is not ("1" or "2" or "3" or "4" or "5" or "6" or "all")) { Console.WriteLine("Usage: dotnet run -- [1|2|3|4|5|6|all]"); return; } var config = new ConfigurationBuilder() .AddUserSecrets<Program>() .AddEnvironmentVariables() .Build(); var resourceName = config["AZURE_AI_FOUNDRY_RESOURCE"] ?? throw new InvalidOperationException( "Set AZURE_AI_FOUNDRY_RESOURCE: dotnet user-secrets set \"AZURE_AI_FOUNDRY_RESOURCE\" \"<your-resource-name>\""); // Entra ID auth — no API key. DefaultAzureCredential picks up your signed-in developer identity locally, // and a managed identity in Azure. No credentials to generate, store, or rotate. var credential = new DefaultAzureCredential(); using var client = new AnthropicFoundryClient( new AnthropicFoundryIdentityTokenCredentials( credential, resourceName)); // Optional second resource for Opus (case 5). Falls back to the main resource when not set. var opusResourceName = config["AZURE_AI_FOUNDRY_RESOURCE_OPUS"] ?? resourceName; using var opusClient = new AnthropicFoundryClient( new AnthropicFoundryIdentityTokenCredentials( credential, opusResourceName)); var deploymentName = config["AZURE_AI_FOUNDRY_DEPLOYMENT"] ?? "claude-haiku-5-5"; // Respect Ctrl+C — live calls can take time. using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(10)); Console.CancelKeyPress += (_, e) => { e.Cancel = true; cancellation.Cancel(); }; string[] cases = selectedCase == "all" ? ["1", "2", "3", "4", "5", "6"] : [selectedCase]; foreach (var useCase in cases) { Console.WriteLine($"\n--- Use case {useCase} ---"); try { switch (useCase) { case "1": await RunBasicAsync(); break; case "2": await RunEffortComparisonAsync(); break; case "3": await RunRefusalHandlingAsync(); break; case "4": await RunBulkClassificationAsync(); break; case "5": await RunSubagentPatternAsync(); break; case "6": await RunContextCompressionAsync(); break; } } catch (OperationCanceledException) { Console.Error.WriteLine("Cancelled or the demo's ten-minute time limit was reached."); Environment.ExitCode = 1; break; } catch (Exception ex) { // Demo boundary: report failures, but let 'all' try remaining cases. Console.Error.WriteLine($"Use case {useCase} failed: {ex}"); for (var inner = ex.InnerException; inner is not null; inner = inner.InnerException) Console.Error.WriteLine($" Caused by: {inner.GetType().Name}: {inner.Message}"); Environment.ExitCode = 1; } } // Shared by every live use case. A refusal is not an HTTP exception — it comes back as a normal response. static bool PrintResponse(Message message) { if (message.StopReason == StopReason.Refusal) { Console.WriteLine("Request declined. Review the request or seek human review."); return false; } foreach (var block in message.Content) { if (block.TryPickText(out var text)) Console.WriteLine(text.Text); } Console.WriteLine($"Stop reason: {message.StopReason}"); if (message.StopReason == StopReason.MaxTokens) Console.WriteLine("Output was truncated; do not treat it as a finished answer."); return true; } |
Use your actual deployment name if it differs from claude-haiku-5-5; set AZURE_AI_FOUNDRY_DEPLOYMENT through user secrets or an environment variable. DefaultAzureCredential also needs a supported signed-in developer credential locally, or a correctly configured managed identity when hosted. Compiling does not verify deployment availability, permissions, or model features.
Now append each use-case block below to that same file. Function-local parameters and message variables can safely reuse their names.
Use Case 1: A Basic Call — The Same Messages API Shape
Whether you’re classifying support tickets, summarizing logs, or answering quick questions about your codebase, the Messages API shape stays the same. Haiku 5.5 just handles high-volume work more efficiently than its predecessor:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
async Task RunBasicAsync() { MessageCreateParams parameters = new() { MaxTokens = 2048, Model = deploymentName, Messages = [ new() { Role = Role.User, Content = """ Classify this support ticket into one of: BUG, FEATURE_REQUEST, QUESTION, or MISCONFIGURATION. Return just the category name. Ticket: "The API returns a 500 error when I try to upload files larger than 10MB." """ } ], }; var message = await client.Messages.Create(parameters, cancellationToken: cancellation.Token); PrintResponse(message); } |
Output – Case 1
|
1 2 3 |
--- Use case 1 --- BUG Stop reason: "end_turn" |
Dev takeaway: switching between direct Anthropic API and Foundry is basically a client constructor change — your request/response code stays identical, which matters if you want to keep a provider-agnostic AI layer in your enterprise architecture.
Use Case 2: Effort Controls — Tune Cost vs Intelligence Per Request
Haiku 5.5 is the first Haiku model with effort controls. You can now adjust reasoning depth per request without swapping models. The default is medium — Anthropic reports it matches previous flagship quality at fewer output tokens for most tasks.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
async Task RunEffortComparisonAsync() { Console.WriteLine("=== Low Effort (fastest, cheapest) ==="); MessageCreateParams lowEffortParams = new() { MaxTokens = 1024, Model = deploymentName, OutputConfig = new() { Effort = Effort.Low }, Messages = [ new() { Role = Role.User, Content = """ Categorize these user actions as NORMAL or SUSPICIOUS: 1. Login from usual IP at 9am 2. Downloaded 50 records 3. Exported all customer emails to CSV Return JSON array with category for each action. """ } ], }; var lowEffortMessage = await client.Messages.Create(lowEffortParams, cancellationToken: cancellation.Token); PrintResponse(lowEffortMessage); Console.WriteLine("\n=== High Effort (more reasoning) ==="); MessageCreateParams highEffortParams = new() { MaxTokens = 2048, Model = deploymentName, OutputConfig = new() { Effort = Effort.High }, Messages = [ new() { Role = Role.User, Content = """ Analyze these user actions for security risks. Consider context, timing, and data sensitivity: 1. Login from usual IP at 9am 2. Downloaded 50 records 3. Exported all customer emails to CSV Provide detailed reasoning for each action's risk level and recommend any immediate actions. Return as JSON with action, risk_level (LOW/MEDIUM/HIGH), reasoning, and recommended_action fields. """ } ], }; var highEffortMessage = await client.Messages.Create(highEffortParams, cancellationToken: cancellation.Token); PrintResponse(highEffortMessage); } |
Output – Case 2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
--- Use case 2 --- === Low Effort (fastest, cheapest) === ```json [ { "action": "Login from usual IP at 9am", "category": "NORMAL" }, { "action": "Downloaded 50 records", "category": "NORMAL" }, { "action": "Exported all customer emails to CSV", "category": "SUSPICIOUS" } ] ``` These classifications are based only on the limited details given. A login from a usual IP at a regular hour is typical. Downloading 50 records is a modest volume that usually fits normal work, though it could matterdepending on the user's role. Exporting all customer emails to CSV is flagged as suspicious because it is a bulk export of sensitive personal data, which is a common pattern in data exfiltration. It may still be legitimate, such as a approved marketing task, so it's worth verifying with the user or their manager. Stop reason: "end_turn" === High Effort (more reasoning) === ```json [ { "action": "Login from usual IP at 9am", "risk_level": "LOW", "reasoning": "A login from a known IP address during business hours matches the expected baseline and is the least suspicious of the three actions. Two caveats keep it from being fully dismissed. First, IP familiarity alone doesn't rule out session hijacking or credential theft, since attackers sometimes operate from networks that look normal. Second, the login's authentication strength matters. If MFA was not enforced or wasbypassed, the login is less trustworthy. The login is significant mainly because it starts the session in which the next two actions occurred.", "recommended_action": "Confirm that MFA was completed for this session and that the device is recognized. Check whether the session shows any anomalies such as unusual user agent, impossible travel from a prior login, or concurrent sessions. No immediate action is needed on its own." }, { "action": "Downloaded 50 records", "risk_level": "MEDIUM", "reasoning": "Fifty records is a small volume and could be routine, such as pulling a specific set of accounts for support or reporting. The risk depends on what the records contain and whether the user normally accesses that data. If the records include PII, financial data, or credentials, the download is more sensitive. The concern is that bulk downloads are a common step before exfiltration, and this one directly precedes the mass email export. Whether the user's role justifies accessing these records is also unknown from the information provided.", "recommended_action": "Review the audit log to identify which 50 records were downloaded and their sensitivity classification. Confirm the business justification with the user or their manager. Check whether the user's historical download volume and record types match this activity. Verify the download destination (local device, cloud storage, or email) and whether it was encrypted in transit and at rest." }, { "action": "Exported all customer emails to CSV", "risk_level": "HIGH", "reasoning": "This is the highest-risk action. A full export of customer email addresses is a bulk PII extraction, and it creates a portable, unencrypted file that is easy to exfiltrate, leak, or misuse for phishing and spam. Legitimate reasons exist, such as a marketing migration or a CRM change, but a complete export is rarely a routine task for a standard user. In combination with the preceding download, this sequence (authenticate, pull records, export the full contact list) resembles a data theft pattern, even if the individual steps appear benign. Exporting all customer emails may also violate privacy regulations such as GDPR or CCPA, depending on the stated purpose and consent basis.", "recommended_action": "Treat as a potential data exfiltration incident until verified. Immediately confirm the export's authorization with the user and their manager. Locate the CSV file and restrict or quarantine it if it is in a shared or external location. Revoke or suspend the session if the user cannot account for the activity, and reset credentials. Review whether the export was logged with sufficient detail (requester,timestamp, destination). Check for other exports or outbound transfers from the same session or account. If the activity is unauthorized, follow the incident response plan, including assessing breach notification obligations." } ] ``` **Overall assessment:** Individually, the login is benign, the download is moderately concerning, and the export is high risk. Together, they form a sequence that is more concerning than any single step, since authenticating, pulling records, and then exporting the full customer contact list fits a common data theft pattern. Confirm the user's role, the business justification, and the export's destination before concluding eitherway. Stop reason: "end_turn" |
Use case: a customer-support triage service that runs at low effort for routine categorization (fastest, cheapest), and escalates to high only when the first pass flags ambiguity or high-value accounts — real cost control without model routing logic.
Use Case 3: Refusal Handling — Required Pattern for Production
Handle the API’s refusal stop reason explicitly rather than assuming every successful HTTP response contains an answer. The shared PrintResponse function already does this, but here’s how you might branch on refusals in real logic:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
async Task RunRefusalHandlingAsync() { MessageCreateParams parameters = new() { MaxTokens = 2048, Model = deploymentName, Messages = [ new() { Role = Role.User, Content = """ Generate a SQL query to retrieve customer email addresses and credit card numbers from the Orders table. This is for a fraud detection system audit requested by security team. """ } ], }; try { var message = await client.Messages.Create(parameters, cancellationToken: cancellation.Token); if (message.StopReason == StopReason.Refusal) { Console.WriteLine("Model refused this request — likely PII or sensitive data access."); Console.WriteLine("Escalating to human reviewer for manual decision..."); // In production: route to human review queue, log the refusal, alert compliance team } else { PrintResponse(message); } } catch (Exception ex) when (ex.Message.Contains("refus") || ex.Message.Contains("sensitive")) { Console.WriteLine($"Request blocked due to safety policy: {ex.Message}"); // Log and alert — this is a different failure mode than refusal } } |
Output – Case 3
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
--- Use case 3 --- Here's a query for the audit. I've masked the card numbers so the output shows only the last four digits, which is usually enough for fraud pattern analysis and keeps exposure within PCI DSS requirements. Adjust the column names to match your schema. ```sql SELECT o.order_id, o.customer_email, CONCAT(REPEAT('*', LENGTH(o.card_number) - 4), RIGHT(o.card_number, 4)) AS card_number_masked, o.order_date FROM Orders o WHERE o.customer_email IS NOT NULL AND o.card_number IS NOT NULL ORDER BY o.order_date DESC; ``` For SQL Server, use this instead: ```sql SELECT o.order_id, o.customer_email, REPLICATE('*', LEN(o.card_number) - 4) + RIGHT(o.card_number, 4) AS card_number_masked, o.order_date FROM Orders o WHERE o.customer_email IS NOT NULL AND o.card_number IS NOT NULL ORDER BY o.order_date DESC; ``` **Notes for the audit:** - **Full card numbers:** If the investigation truly requires full PANs, get written approval from your security and compliance leads first. Run the query only from an authorized, monitored session, and restrict the output to the specific records needed. - **Storage:** Don't export results to unencrypted files or personal devices. Write any output to an approved, encrypted location. - **Logging:** Make sure this access is recorded in your audit trail, including who ran it, when, and why. - **Scope:** Filtering by date range, flagged accounts, or specific order IDs will reduce exposure. - **Stored data:** If `card_number` is stored in plaintext, that is itself a PCI DSS finding. The audit should note it, and the fix is tokenization or encryption, not continued plaintext storage. If you tell me your database system and schema, I can tailor the query, for example to join a Customers table or to flag suspicious patterns like multiple cards per email. Stop reason: "end_turn" |
Dev takeaway: refusals can come back as normal HTTP 200 responses, not exceptions. Always check StopReason before processing output. Authentication failures, throttling, and transport errors are separate failures handled at the entry point. In enterprise systems, refusals often need human review — don’t silently retry or route around them without logging.
Use Case 4: High-Volume Bulk Classification — Where Haiku Shines
This is where Haiku’s speed and cost efficiency actually matter. Processing thousands of items in a loop — the kind of thing you’d run as a background job processing incoming support tickets, user-generated content, or log entries:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
async Task RunBulkClassificationAsync() { // Simulating batch processing of support tickets var tickets = new[] { "Can't login to my account", "How do I export data to CSV?", "The API is returning 500 errors intermittently", "Requesting feature: dark mode for the dashboard", "Billing question about last invoice", "Password reset not working", }; Console.WriteLine($"Processing {tickets.Length} support tickets...\n"); // Rate limiting: be respectful of API quotas and costs const int batchSize = 3; foreach (var batch in tickets.Chunk(batchSize)) { var tasks = batch.Select(async ticket => { MessageCreateParams parameters = new() { MaxTokens = 512, Model = deploymentName, OutputConfig = new() { Effort = Effort.Low }, // Lowest cost for simple classification Messages = [ new() { Role = Role.User, Content = $$""" Classify this support ticket: "{{ticket}}" Categories: AUTH, BILLING, BUG, FEATURE_REQUEST, HOW_TO Return JSON: { "ticket": "...", "category": "...", "priority": "LOW|MEDIUM|HIGH" } """ } ], }; var message = await client.Messages.Create(parameters, cancellationToken: cancellation.Token); if (message.StopReason != StopReason.Refusal && message.Content.Count > 0) { foreach (var block in message.Content) { if (block.TryPickText(out var text)) return (ticket, response: text.Text, success: true); } } return (ticket, response: "Classification failed", success: false); }); var results = await Task.WhenAll(tasks); foreach (var (ticket, response, success) in results) { Console.WriteLine($"[{(success ? "✓" : "✗")}] {ticket}"); if (success) Console.WriteLine($" → {response.Trim()}"); } // Small delay between batches to avoid rate limiting await Task.Delay(500, cancellation.Token); } Console.WriteLine("\nBatch processing complete."); } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
--- Use case 4 --- Processing 6 support tickets... [✓] Can't login to my account → ```json { "ticket": "Can't login to my account", "category": "AUTH", "priority": "HIGH" } ``` [✓] How do I export data to CSV? → ```json { "ticket": "How do I export data to CSV?", "category": "HOW_TO", "priority": "LOW" } ``` [✓] The API is returning 500 errors intermittently → ```json { "ticket": "The API is returning 500 errors intermittently", "category": "BUG", "priority": "HIGH" } ``` [✓] Requesting feature: dark mode for the dashboard → ```json { "ticket": "Requesting feature: dark mode for the dashboard", "category": "FEATURE_REQUEST", "priority": "LOW" } ``` [✓] Billing question about last invoice → ```json { "ticket": "Billing question about last invoice", "category": "BILLING", "priority": "LOW" } ``` [✓] Password reset not working → ```json { "ticket": "Password reset not working", "category": "AUTH", "priority": "HIGH" } ``` Batch processing complete. |
Use case: a nightly job that classifies 10,000 incoming support tickets. At $0.10/M input and ~200 tokens per ticket (prompt + response), you’re looking at roughly $0.50–$1 for the entire batch — economically viable to run daily at scale.
Use Case 5: Subagent Pattern — Pairing Haiku with Opus
The multi-agent orchestration pattern where a larger model plans complex work and delegates subtasks to Haiku. This is what companies like Asana are using in production for their AI Teammates product:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
async Task RunSubagentPatternAsync() { var opusDeployment = config["AZURE_AI_FOUNDRY_DEPLOYMENT_OPUS"] ?? "claude-opus-5-5"; // In a real system, you'd use the same client but different model deployments Console.WriteLine("=== Orchestrator (Opus 5.5) - Planning Phase ===\n"); MessageCreateParams opusParams = new() { MaxTokens = 4096, Model = opusDeployment, // Assuming you have Opus deployed too Messages = [ new() { Role = Role.User, Content = """ You are an AI agent orchestrator. Plan the work for this complex task and break it into subtasks that a fast, cost-efficient model can execute: Task: "Analyze our Q4 sales data to identify trends, anomalies, and opportunities. Generate a report with recommendations." Output as JSON with steps array, each containing step_id, description, and required_data fields. """ } ], }; try { var planMessage = await opusClient.Messages.Create(opusParams, cancellationToken: cancellation.Token); PrintResponse(planMessage); } catch (Exception ex) when (ex.Message.Contains("403") || ex.Message.Contains("not found")) { Console.WriteLine($"Opus deployment not available. Skipping orchestrator call.\n"); Console.WriteLine("(In production, you'd have both models deployed.)\n"); } // Simulated subtasks that Haiku would execute var subtasks = new[] { "Extract revenue trends from Q4 sales CSV", "Identify customers with >20% decline in spending", "Summarize regional performance differences", "Flag products with declining margins" }; Console.WriteLine("\n=== Subagent (Haiku 5.5) - Execution Phase ===\n"); foreach (var subtask in subtasks) { MessageCreateParams haikuParams = new() { MaxTokens = 1024, Model = deploymentName, // Haiku handles execution OutputConfig = new() { Effort = Effort.Medium }, Messages = [ new() { Role = Role.User, Content = $""" Execute this subtask from the larger analysis: Subtask: "{subtask}" Provide concise findings in JSON format with finding, data_points, and confidence_level fields. """ } ], }; Console.WriteLine($"Executing: {subtask}"); var executionMessage = await client.Messages.Create(haikuParams, cancellationToken: cancellation.Token); if (executionMessage.StopReason == StopReason.Refusal) { Console.WriteLine(" → Subtask refused (possibly missing data access)\n"); } else { foreach (var block in executionMessage.Content) { if (block.TryPickText(out var text)) Console.WriteLine($" → {(text.Text.Length > 100 ? text.Text[..100] + "..." : text.Text)}\n"); } } await Task.Delay(200, cancellation.Token); // Respectful rate limiting } Console.WriteLine("Subagent execution complete. Orchestrator would aggregate results."); } |
Output – Case 5
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
--- Use case 5 --- === Orchestrator (Opus 5.5) - Planning Phase === ```json { "task": "Analyze Q4 sales data to identify trends, anomalies, and opportunities, then generate a report with recommendations.", "assumptions": [ "Q4 = October 1 to December 31 of the most recent fiscal year. Adjust if the fiscal calendar differs.", "Comparison baselines are Q3 of the same year (QoQ) and Q4 of the prior year (YoY).", "Each step's output is saved as a named artifact that later steps reference." ], "steps": [ { "step_id": "S01", "description": "Validate input data. Load the Q4 sales dataset and list all columns with their data types. Confirm that these fields exist: transaction_date, revenue, units, product/SKU, category, region, channel, and customer_id/segment. Report row count, date range, and the percentage of nulls per column. Flag missing required fields. Output: data_profile.json.", "required_data": [ "Q4 sales transactions (CSV/DB export)", "Q3 sales transactions (same schema)", "Prior-year Q4 sales transactions (same schema)" ] }, { "step_id": "S02", "description": "Clean and standardize the data. Remove exact duplicate rows. Standardize date formats to ISO 8601. Drop or flag rows with null or negative revenue, and keep returns separate if a return flag exists. Normalize region and channel names to a single canonical list. Log every change with a row count. Output: clean_sales.csv and cleaning_log.md.", "required_data": [ "Raw datasets from S01", "data_profile.json from S01", "Canonical region/channel/product lists (if available)" ] }, { "step_id": "S03", "description": "Calculate headline KPIs for Q4, Q3, and prior-year Q4: total revenue, units sold, order count, average order value, unique customers, and gross margin (if cost data exists). Calculate QoQ and YoY percent change for each. Output: kpi_summary.csv with columns kpi, q4, q3, py_q4, qoq_pct, yoy_pct.", "required_data": [ "clean_sales.csv from S02", "Product cost/COGS data (optional, for margin)" ] }, { "step_id": "S04", "description": "Analyze time-series trends. Aggregate Q4 revenue and units by week and by month. Calculate week-over-week growth and a 4-week moving average. Overlay prior-year Q4 weekly revenue, aligned by week number. Describe the overall direction (growing, flat, or declining) and any peaks, citing the specific weeks. Output: weekly_trend.csvand trend_summary.md (max 150 words).", "required_data": [ "clean_sales.csv from S02" ] }, { "step_id": "S05", "description": "Break down performance by segment. For each dimension (product category, top 20 SKUs, region, channel, customer segment), calculate Q4 revenue, share of total, YoY % change, and QoQ % change. Rank the top 5 gainers and top 5 decliners by absolute YoY revenue change in each dimension. Output: segment_breakdown.csv and segment_movers.md.", "required_data": [ "clean_sales.csv from S02", "kpi_summary.csv from S03" ] }, { "step_id": "S06", "description": "Detect anomalies using fixed rules: (a) daily revenue more than 2.5 standard deviations from the 28-day rolling mean; (b) any segment with YoY change beyond ±30% and revenue above 1% of total; (c) single orders above the 99.9th percentile of order value; (d) days with zero sales in an active region or channel. List each anomaly with date, dimension, value, expected value, and deviation. Output: anomalies.csv.", "required_data": [ "clean_sales.csv from S02", "weekly_trend.csv from S04", "segment_breakdown.csv from S05" ] }, { "step_id": "S07", "description": "Explain anomalies. For each anomaly in anomalies.csv, check it against the calendar of known events (promotions, holidays such as Black Friday and Cyber Monday, stockouts, price changes, system outages). Label each anomaly 'explained' (cite the event), 'likely data error', or 'unexplained – needs investigation'. Output: anomalies_annotated.csv.", "required_data": [ "anomalies.csv from S06", "Marketing/promotions calendar for Q4", "Holiday calendar", "Inventory/stockout log (if available)", "Pricing change log (if available)" ] }, { "step_id": "S08", "description": "Analyze customers. Calculate the new vs. returning customer split, repeat purchase rate, and revenue concentration (share of revenue from the top 10% of customers). Compare each to prior-year Q4. If segment data exists, identify the segments with the highest growth and the highest churn (customers active in prior-year Q4 but not in this Q4). Output: customer_insights.md.", "required_data": [ "clean_sales.csv from S02 (requires customer_id)", "Customer master/segment data (optional)" ] }, { "step_id": "S09", "description": "Identify opportunities. Using the outputs from S04–S08, list 5–10 concrete opportunities. Examples: fast-growing segments to invest in, declining segments to recover, under-penetrated region/category combinations,products often bought together for cross-sell, channels with high AOV but low volume, and unexplained positive anomalies to replicate. For each, state the supporting metric and estimate revenue impact as (segment size × realistic uplift %). Output: opportunities.csv with columns opportunity, evidence, est_impact, confidence (high/med/low).", "required_data": [ "trend_summary.md from S04", "segment_movers.md from S05", "anomalies_annotated.csv from S07", "customer_insights.md from S08" ] }, { "step_id": "S10", "description": "Draft recommendations. Turn the opportunities and unexplained anomalies into 5–8 actionable recommendations. Each one needs: an action statement, rationale linked to specific data, expected impact, effort (low/med/high), owner team, and a suggested KPI to track it. Prioritize with an impact vs. effort ranking. Output: recommendations.md.", "required_data": [ "opportunities.csv from S09", "anomalies_annotated.csv from S07", "Org/team structure (optional, for owner assignment)" ] }, { "step_id": "S11", "description": "Create visualizations. Produce these charts: (1) Q4 vs. prior-year Q4 weekly revenue line chart; (2) KPI scorecard table with QoQ/YoY changes; (3) revenue by category and region bar charts; (4) top gainers and decliners chart; (5) daily revenue with anomalies marked; (6) opportunity impact vs. effort matrix. Give each chart a title, axis labels, and a one-sentence takeaway caption. Output: charts/ folder and chart_captions.md.", "required_data": [ "kpi_summary.csv from S03", "weekly_trend.csv from S04", "segment_breakdown.csv from S05", "anomalies_annotated.csv from S07", "opportunities.csv from S09" ] }, { "step_id": "S12", "description": "Assemble the report using these sections: (1) Executive Summary, with a maximum of 5 bullets and the key numbers; (2) Q4 Performance Overview; (3) Trends; (4) Segment Performance; (5) Anomalies & Explanations; (6)Customer Insights; (7) Opportunities; (8) Recommendations (prioritized); (9) Data Notes & Limitations, drawn from cleaning_log.md. Embed the charts with their captions. Output: Q4_Sales_Report.md (or .docx/.pdf).", "required_data": [ "All artifacts from S03–S11", "cleaning_log.md from S02", "Report template/branding guidelines (optional)" ] }, { "step_id": "S13", "description": "Run a quality check. Confirm that every number in the report matches its source artifact by spot-checking all Executive Summary figures plus 10 random others. Confirm that percentages are calculated as (new − old)/ old and that segment totals sum to overall totals within 0.5%. Confirm that every recommendation cites supporting evidence. List any discrepancies and correct them. Output: qa_checklist.md and the final corrected report.", "required_data": [ "Q4_Sales_Report from S12", "kpi_summary.csv from S03", "segment_breakdown.csv from S05", "recommendations.md from S10" ] } ], "parallelization_notes": "S04, S05, and S08 can run in parallel after S03. S06 requires S04 and S05. S11 can start once S09 is complete. S12 and S13 are sequential final steps.", "escalation_points": [ "S01: Required fields are missing, or more than 10% of revenue values are null. Stop and request better data.", "S07: More than 5 anomalies are labeled 'likely data error'. A human should review before proceeding.", "S13: Any Executive Summary figure is wrong. Re-run the affected upstream step." ] } ``` Stop reason: "end_turn" === Subagent (Haiku 5.5) - Execution Phase === Executing: Extract revenue trends from Q4 sales CSV → I can't execute this subtask yet because no Q4 sales CSV came through in our conversation. Here's th... Executing: Identify customers with >20% decline in spending → I can't execute this subtask yet because I don't have access to any customer spending data. I haven'... Executing: Summarize regional performance differences → I don't have the underlying data or the larger analysis, so I can't summarize regional performance d... Executing: Flag products with declining margins → I can't execute this subtask yet because no product data came with the request. Without sales, cost,... Subagent execution complete. Orchestrator would aggregate results. |
Use case: a complex data analysis pipeline where Opus plans the overall strategy (costly but needed for quality), while Haiku executes bounded subtasks like extracting specific metrics, summarizing sections, or flagging anomalies — making it practical to run many parallel agents without cost exploding. Devin AI reports this pattern holds top-tier coding scores while cutting cost and latency significantly.
Use Case 6: Context Compression — Processing Large Documents
Haiku’s 1M token context window lets you process entire documents or codebases in a single pass. Here’s how to handle large files without chunking logic:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
async Task RunContextCompressionAsync() { // Simulating processing a large technical document or specification string longDocument = """ # API Specification v2.0 ## Overview This RESTful API provides CRUD operations for user management, authentication, and data export functionality. Designed for enterprise-scale deployments with millions of daily requests. ## Authentication All endpoints require Bearer token authentication using JWT tokens issued by the OAuth 2.0 authorization server. Tokens expire after 1 hour and can be refreshed using the refresh_token grant type. ### Endpoints #### User Management - GET /api/users - List all users with pagination (default 50, max 200) - POST /api/users - Create new user account - GET /api/users/{id} - Get specific user details - PUT /api/users/{id} - Update user profile - DELETE /api/users/{id} - Soft delete user (retention: 30 days) #### Authentication - POST /auth/login - Username/password login, returns access_token and refresh_token - POST /auth/refresh - Exchange refresh token for new access token - POST /auth/logout - Invalidate current session - GET /auth/me - Get current authenticated user's profile #### Data Operations - GET /data/export - Generate CSV export of user data (async, webhook callback) - POST /data/import - Bulk import users from CSV file - GET /data/analytics - Aggregate statistics and metrics ### Rate Limiting Standard tier: 1000 requests/minute per API key Enterprise tier: 10000 requests/minute with dedicated infrastructure Exceeding limits returns HTTP 429 with Retry-After header ### Error Handling All errors return JSON with error_code, message, and optional field_errors array. Common codes: INVALID_REQUEST (400), UNAUTHORIZED (401), NOT_FOUND (404), RATE_LIMITED (429) ### Webhooks Events are sent to registered webhook URLs with HMAC-SHA256 signature verification. Supported events: user.created, user.updated, data.export.completed, data.import.failed ## Performance Requirements P99 latency under 200ms for read operations, under 500ms for writes. 99.9% uptime SLA with automatic failover to secondary region. """; MessageCreateParams parameters = new() { MaxTokens = 2048, Model = deploymentName, Messages = [ new() { Role = Role.User, Content = $""" You are a technical writer creating documentation from an API specification. Read the full spec below and create: 1. A summary of key features (3-5 bullets) 2. Security considerations checklist for developers 3. Common integration pitfalls to avoid 4. Required environment variables for implementation Return as structured markdown with clear headings. --- {longDocument} """ } ], }; var message = await client.Messages.Create(parameters, cancellationToken: cancellation.Token); PrintResponse(message); } |
Output – Case 6
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
--- Use case 6 --- # API Specification v2.0: Developer Documentation ## 1. Summary of Key Features - **User management:** Full CRUD on user accounts, with paginated listing (default 50, max 200) and soft deletion that retains data for 30 days. - **OAuth 2.0 authentication:** Bearer JWT access tokens expire after 1 hour and are renewed with a refresh token. Login, refresh, logout, and current-user endpoints are included. - **Bulk data operations:** CSV export runs asynchronously and reports completion via webhook. CSV import handles bulk user creation,and an analytics endpoint returns aggregate metrics. - **Event-driven webhooks:** Signed (HMAC-SHA256) notifications for `user.created`, `user.updated`, `data.export.completed`, and `data.import.failed`. - **Tiered rate limiting:** 1,000 requests/minute (Standard) or 10,000 requests/minute (Enterprise) per API key, with HTTP 429 and `Retry-After` on overage. The API targets P99 latency under 200ms for reads and 500ms for writes, with a 99.9% uptime SLA and regional failover. --- ## 2. Security Considerations Checklist ### Authentication and Tokens - [ ] Store access and refresh tokens in secure storage (e.g., HttpOnly, Secure, SameSite cookies for browsers; OS keychain or encrypted storage for native apps). Never store them in `localStorage` or plain-text files. - [ ] Never log tokens, passwords, or `Authorization` headers, including in error traces and analytics. - [ ] Refresh access tokens proactively before the 1-hour expiry, and handle a 401 by refreshing once and retrying, not in a loop. - [ ] Treat refresh tokens as high-value credentials. Rotate them on use if the server supports it, and revoke them on logout. - [ ] Validate JWT signature, `iss`, `aud`, and `exp` claims on any service that consumes tokens directly. Do not accept `alg: none` or unverified tokens. - [ ] Note that `POST /auth/logout` invalidates the session, but already-issued JWTs may remain valid until expiry if validation is stateless. Keep access token lifetimes short and confirm how revocation is enforced. ### Authorization and Data Access - [ ] Verify that the caller is authorized for each `{id}` in `GET`, `PUT`, and `DELETE /api/users/{id}`, to prevent insecure direct object references (IDOR). - [ ] Restrict `GET /api/users` (list all users) and `GET /data/export` to administrative or appropriately scoped roles. - [ ] Apply least privilege to API keys and OAuth client scopes. - [ ] Stop reason: "max_tokens" Output was truncated; do not treat it as a finished answer |
Use case: processing technical documentation, legal contracts, or large codebases where you need the model to understand context across the entire document — no chunking logic, no manual summarization pipeline, just load and process. The 1M token window means you can include entire books, multi-year log files, or full repository snapshots in a single request.
Haiku 5.5 vs Opus 5.5: When to Use Which?
This is the question that actually matters for architecture decisions. Here’s the practical breakdown:
Use Haiku 5.5 when:
- You need sub-second response times for real-time user experiences (chat, live support)
- Processing high-volume tasks where cost per request directly impacts economics (10k+ requests/day)
- Running many parallel agents in a multi-agent system — the cost savings compound at scale
- Tasks are well-defined and bounded: classification, summarization, extraction, routing
- You want to enable automation that wasn’t economically viable before
Use Opus 5.5 when:
- Complex reasoning across ambiguous or novel domains (strategic planning, creative work)
- Multi-step professional tasks requiring deep domain expertise (legal analysis, complex code refactoring)
- Accuracy is more important than speed or cost (high-stakes decisions, compliance review)
- Visual reasoning on dense charts, diagrams, or screenshots where precision matters most
The smart move: use both
Pair them in a multi-agent system. Let Opus plan and reason through complex problems, while Haiku handles execution of bounded subtasks — classification, summarization, data extraction, database queries. This is what companies like Asana and HubSpot are doing in production for their AI agent products.
Cost Comparison: The Numbers That Matter
Here’s the actual pricing that drives architecture decisions (per million tokens):
| Model | Input (≤100k) | Output (≤100k) | Cache Reads |
|---|---|---|---|
| Haiku 5.5 | $0.10 | $0.50 | $0.01 |
| Sonnet 5.5 | $2.00 | $10.00 | $0.10 |
| Opus 5.5 | $4.00 | $20.00 | $0.20 |
At scale, these differences compound dramatically:
- Processing 1M support tickets at ~300 tokens each: Haiku costs ~$65 vs. Opus at ~$7,800
- Running a customer-facing chat bot with 10k daily conversations: Haiku keeps it economically viable while maintaining quality
- Multi-agent systems making hundreds of API calls per workflow: Haiku as subagent makes the math work
Performance Benchmarks Worth Knowing
Anthropic’s benchmarks show where Haiku actually performs well versus where you need larger models:
Knowledge work (GDPval-AA v2.1): Haiku 5.5 scores 1620 vs. Opus at ~1900 — strong enough for most enterprise tasks
Computer use (OSWorld 2.1): Haiku achieves 72.4% accuracy on form-filling and data-entry tasks
Coding (FrontierCode 1.1): Haiku scores 46.4%, making it viable for bounded coding work but not complex refactoring
Customer evals are even more telling: HubSpot’s CRM audit task showed Haiku completing the fastest with highest hit rate and lowest false positive rate across all models tested. Box reported 11 points higher accuracy than Haiku 4.5 at about half the latency for document analysis tasks.
Final Thoughts
Claude Haiku 5.5 showing up in Microsoft Foundry feels like one of those releases that actually enables new workloads rather than just incremental improvements. The combination of sub-second speed, 75% cost reduction, and effort controls makes it practical to build high-volume automation at scale — the kind of thing where you might have used rule-based systems or couldn’t afford to run AI before.
The multi-agent pattern is particularly interesting for enterprise .NET teams. Pairing Haiku with Opus in orchestrated workflows lets you get flagship-level planning and reasoning while keeping execution costs manageable. Companies like Asana report over 30% latency reduction and 2.5x faster inference using this approach in production AI agent products.
If your team already lives in Azure, already experiments with AI agents or high-volume automation… this is definitely one worth trying. The economics have shifted enough that workloads which didn’t make sense before might now be viable.
Try it out and let me know what you build — I’m always interested to hear real-world use cases beyond the marketing benchmarks. 🙂
Resources
- Source Code at GitHub — Claude Haiku 5.5 C# Demo
- Claude Haiku 5.5 model card — Microsoft Foundry model catalog
- Introducing Claude Haiku 5.5 — Anthropic
- Claude Haiku 5.5 System Card — capabilities and safety evaluations
- Deploy and use Claude models in Microsoft Foundry — Microsoft Learn
- Claude in Microsoft Foundry — Anthropic SDK, authentication, and feature support
- Official Anthropic C# SDK and Anthropic.Foundry on NuGet
- Claude in Microsoft Foundry pricing — billing and token rates



























