Previous All Posts Next

Claude SDK: Ship Production AI Agents Safely

Posted: December 31, 1969 to AI.

Software engineering team collaborating around a whiteboard with architecture diagrams in a modern office with natural light

Most AI demos die on the way to production. The model looks brilliant in a notebook, then everything falls apart when you add a real user, a real document set, a real tool, and a real compliance officer asking where the data went. Petronella Technology Group has been shipping production AI agents on Anthropic's Claude for over two years, and the thing that separates a working agent from a science experiment is almost never the model. It is the plumbing.

That plumbing is what the Claude SDK gives you. Anthropic ships two distinct things under the same umbrella: the low-level Messages API client libraries that expose the raw model, and the newer Claude Agent SDK that ships the same agent loop and tool harness that powers Claude Code. Picking the right layer, wiring it correctly, and treating it like a regulated-industry system from day one is the work.

This guide is written for teams at healthcare, defense, finance, legal, and engineering firms who need to build real AI agents and not just chat toys. It covers the SDK choices, the architecture patterns that survive an audit, the code you actually need to write, and the production safeguards that keep you out of trouble. Petronella Technology Group has ten or more production AI agents running on this stack right now, including Penny the AI receptionist who answers (919) 348-4912 live, Peter the chat assistant on petronellatech.com, ComplyBot the compliance copilot, the Auto Blog Agent that writes long-form content, and several private digital twin voice assistants deployed for clients in regulated verticals. Everything in this guide is stack we have run in anger.

Two SDKs, one vendor

Before you write a single line of code, understand which Claude surface you are actually building on. Anthropic exposes three layers.

The Messages API is the raw HTTP endpoint. You POST to /v1/messages with a model ID, a message list, and an optional tools array, and you get back a response. Every official Anthropic client (Python, TypeScript, Java, Go, Ruby, C#, PHP) is a thin wrapper on top of this one endpoint. If you have ever used an OpenAI-style chat completion API, this will feel familiar. The docs for the endpoint are at platform.claude.com/docs/en/api/messages.

The Claude Agent SDK (formerly called the Claude Code SDK, renamed in early 2026) is a higher-level Python and TypeScript library that ships the exact agent loop powering Claude Code. It includes built-in tools (Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, Monitor, Agent for subagents), a permissions model, hooks, session persistence, and native MCP support. You install it with pip install claude-agent-sdk or npm install @anthropic-ai/claude-agent-sdk. Reference docs live at code.claude.com/docs/en/agent-sdk/overview.

The Claude Managed Agents service is a fully hosted harness still in beta as of early 2026. You define an agent (model, system prompt, tools, MCP servers), an environment (a cloud container with pre-installed packages and network rules), and a session, and Anthropic runs the whole thing on their infrastructure. You send events over SSE and Claude does the rest. It is billed on tokens plus session runtime at eight cents per session-hour according to the pricing page.

The decision tree Petronella Technology Group uses with clients is this. If your agent needs custom orchestration, hard-to-mock side effects, or deep integration with a proprietary codebase, use the Messages API directly. If your agent looks like a coding or research agent (reads files, runs commands, edits code, searches the web), use the Claude Agent SDK because you get the loop for free. If you want zero infrastructure and your task fits the managed container model, evaluate Managed Agents. Most of our shipped agents use the raw Messages API with our own orchestration because our tool surface is domain-specific (compliance document retrieval, CMMC control mapping, Twilio voice events, CRM mutations) and we want full control over the loop.

The minimal agent

Close-up of a laptop screen showing Python code in a dark-theme IDE with hands on the keyboard and a blurred coffee cup

Let us start with the simplest working agent on the raw Messages API. This is the Python version using the official anthropic package.

import anthropic

client = anthropic.Anthropic()  # picks up ANTHROPIC_API_KEY env var

message = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Summarize the top three HIPAA Security Rule administrative safeguards."}
    ],
)

for block in message.content:
    if block.type == "text":
        print(block.text)

Three required parameters: model, max_tokens, and messages. Current production-ready model IDs as of April 2026 are claude-opus-4-7 (the frontier model), claude-sonnet-4-6 (best speed and intelligence tradeoff), and claude-haiku-4-5-20251001 (fastest, near-frontier). The model list is documented in the client SDKs reference.

The TypeScript equivalent is almost identical.

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const message = await client.messages.create({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Summarize the top three HIPAA Security Rule administrative safeguards." }
  ]
});

for (const block of message.content) {
  if (block.type === "text") console.log(block.text);
}

This is the whole contract. Everything else we add below is either tools, caching, streaming, thinking, retries, or observability on top of these same three parameters.

Tool use is where real agents live

A language model that cannot call tools is a parlor trick. A language model that can is an agent. The Messages API tool use contract is simple: you pass a tools array, Claude decides when to call one, you execute the call, and you feed the result back. The tool use docs have the full reference, but here is the pattern we use in every client engagement.

import anthropic
import json

client = anthropic.Anthropic()

tools = [
    {
        "name": "lookup_cmmc_control",
        "description": (
            "Look up a CMMC 2.0 practice by control ID. Returns the practice text, "
            "level, domain, and assessment objectives. Use when a user asks about a "
            "specific CMMC control by ID (AC.L2-3.1.1, SI.L1-3.14.1, etc)."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "control_id": {
                    "type": "string",
                    "description": "CMMC control ID, e.g. 'AC.L2-3.1.1'."
                }
            },
            "required": ["control_id"]
        }
    }
]

def lookup_cmmc_control(control_id: str) -> dict:
    # In production this hits our internal compliance DB.
    # For brevity we return a stub here.
    return {
        "id": control_id,
        "level": "L2",
        "domain": "Access Control",
        "text": "Limit information system access to authorized users...",
    }

def run_agent(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]
    while True:
        response = client.messages.create(
            model="claude-opus-4-7",
            max_tokens=2048,
            tools=tools,
            messages=messages,
        )

        if response.stop_reason == "end_turn":
            return "".join(b.text for b in response.content if b.type == "text")

        if response.stop_reason == "tool_use":
            messages.append({"role": "assistant", "content": response.content})
            tool_results = []
            for block in response.content:
                if block.type == "tool_use" and block.name == "lookup_cmmc_control":
                    result = lookup_cmmc_control(**block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result),
                    })
            messages.append({"role": "user", "content": tool_results})
            continue

        raise RuntimeError(f"Unexpected stop_reason: {response.stop_reason}")

print(run_agent("What does AC.L2-3.1.1 require for CMMC Level 2?"))

Four things to internalize about this loop.

The loop is yours to run. The model tells you it wants to call a tool by returning stop_reason: "tool_use" and one or more tool_use blocks. Your code executes the tool, wraps the output in a tool_result block with the same tool_use_id, and sends the conversation back. Repeat until stop_reason == "end_turn". This is the exact pattern Anthropic documents as the agentic loop.

Tool descriptions are prompts. Claude decides whether to call a tool based on its name and description. A vague description leads to hallucinated calls or missed opportunities. Write descriptions like you are writing onboarding docs for a junior engineer: state the purpose, state when to use it, state when not to use it, and give an example input.

Claude can call multiple tools in parallel. A single assistant turn may return two, three, or ten tool_use blocks. Always iterate over all of them before sending results back. Parallel tool use is the single biggest latency optimization available to you, because a serial loop that calls three tools is three round trips to the model, while parallel tool use is one.

Server tools exist. Anthropic hosts a few tools on their own infrastructure: web_search, web_fetch, and code_execution. You enable them by passing {"type": "web_search_20260209", "name": "web_search"} in the tools array, and Anthropic executes the call and returns the result to Claude for you. Web search is priced at $10 per 1,000 searches on top of tokens. Web fetch and code execution (when used with web search or fetch) are free. The full server-tool reference is in the tool use docs.

For comparison, here is what the same loop looks like with the Claude Agent SDK. The SDK handles the loop, so you write less code.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="Review utils.py for bugs and fix them.",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        print(message)

asyncio.run(main())

That is a fully autonomous bug-fixing agent in nine lines. The SDK ships Read, Edit, and Glob as built-in tools. You do not write the tool executor. You do not write the loop. You specify the tool allowlist and a permission mode and Claude runs until done. This is the right shape for coding agents, research agents, and anything that looks like a generic autonomous task. It is the wrong shape when your tool surface is your business logic, because you lose the ability to cleanly unit-test each tool in isolation.

Prompt caching is the single biggest cost lever

This is the optimization that turns a six-figure AI bill into a five-figure AI bill. Prompt caching lets you tell the API which parts of your prompt are stable and reusable, and for those parts you pay a discounted rate on every subsequent call.

The economics are aggressive. Per Anthropic's pricing page, Opus 4.7 input tokens cost $5 per million normally. A 5-minute cache write costs $6.25 per million (1.25x), and a cache read costs $0.50 per million (0.1x). The break-even point for the default 5-minute TTL is a single cache hit. For a 1-hour TTL (2x write cost) it is two cache hits. Any volume beyond that is pure savings.

Petronella Technology Group ran the math on Penny, our AI receptionist that answers (919) 348-4912 live. Penny has a system prompt of roughly 8,000 tokens including her voice-agent behavior rules, CMMC and HIPAA guardrails, and a pruned knowledge base of service offerings. Before caching, every call cost $0.04 in input tokens alone for the system prompt. After caching, every call after the first costs $0.004. Over 500 calls a day that is a $60/day delta on the system prompt alone. Prompt caching paid for our entire development time in the first month.

Here is how you enable it. Place cache_control blocks at natural breakpoints in your prompt. A good hierarchy: tool definitions, then system prompt, then static document context, then dynamic user content. Up to four breakpoints per request are allowed.

import anthropic

client = anthropic.Anthropic()

SYSTEM_PROMPT = """You are Penny, the AI receptionist for Petronella Technology Group,
a Raleigh NC managed IT and cybersecurity firm. You handle inbound calls on
(919) 348-4912. You never reveal vendor names, never quote prices, and always
book a free 15-minute assessment..."""  # assume ~8000 tokens

CMMC_KNOWLEDGE_BASE = open("kb/cmmc.md").read()  # assume ~12000 tokens

def ask_penny(user_turn: str) -> str:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": SYSTEM_PROMPT,
                "cache_control": {"type": "ephemeral"}
            },
            {
                "type": "text",
                "text": CMMC_KNOWLEDGE_BASE,
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[{"role": "user", "content": user_turn}]
    )
    usage = response.usage
    print(f"cache_read={usage.cache_read_input_tokens} "
          f"cache_write={usage.cache_creation_input_tokens} "
          f"uncached={usage.input_tokens}")
    return "".join(b.text for b in response.content if b.type == "text")

A few things to verify when you first enable caching. Watch the usage fields. cache_creation_input_tokens > 0 on the first call, then cache_read_input_tokens > 0 on subsequent calls within the TTL. If both are zero on call two, your cache is not hitting. Common causes: the prompt is below the per-model minimum (4,096 tokens for Opus 4.7 and Haiku 4.5, 2,048 for Sonnet 4.6, per the prompt caching docs), the cacheable content is not actually byte-identical between calls, or you modified something upstream of your breakpoint (tool definitions, system content) which invalidates everything downstream.

Cached tokens also give you a rate-limit superpower. For most current models, cache reads do not count against your input-tokens-per-minute rate limit. Anthropic's rate limits docs give the example of a 2M ITPM limit with an 80% cache hit rate effectively becoming 10M tokens per minute of throughput. If you are near your ITPM ceiling, caching is the cheapest way to scale before upgrading your tier.

Extended thinking for hard problems

Claude 4.x introduced extended thinking, a mode where the model produces internal reasoning tokens before its visible output. For complex reasoning tasks (code debugging, multi-step math, strategic analysis) it is a measurable quality lift. As of Opus 4.7, adaptive thinking is the default and the old manual thinking.type.enabled parameter is no longer supported on that specific model. For Sonnet 4.6 and Haiku 4.5, manual mode still works.

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=16000,
    thinking={"type": "enabled", "budget_tokens": 10000},
    messages=[
        {"role": "user", "content": "Analyze this CMMC SSP gap analysis and produce a remediation plan..."}
    ]
)

for block in response.content:
    if block.type == "thinking":
        # Internal reasoning; log it for debugging but do not show end users
        log.debug(block.thinking)
    elif block.type == "text":
        print(block.text)

The budget_tokens parameter caps how much thinking Claude can do. You are billed for the full thinking tokens (not the summary the API returns by default), so budget this like any other cost line. See the extended thinking docs for the full reference.

We use extended thinking for ComplyBot on complex CMMC gap analyses where the reasoning chain matters more than response latency. We do not use it for Penny, where the user is on a live voice call and waits in silence for every token.

Streaming keeps voice agents alive

Penny would be unusable if every caller waited five seconds between the end of their sentence and the start of her response. Streaming fixes that. The Messages API supports server-sent events via stream: true, and every client SDK exposes a streaming helper.

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "What services does Petronella Technology Group offer?"}]
) as stream:
    for text in stream.text_stream:
        # Pipe each chunk to the voice synthesis engine immediately
        voice_tts.enqueue(text)
    final = stream.get_final_message()

The TypeScript SDK exposes the same pattern with an async iterable.

const stream = await client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "What services does Petronella Technology Group offer?" }]
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    voiceTts.enqueue(event.delta.text);
  }
}

For voice agents, streaming plus caching plus Haiku or Sonnet on the smaller turns is the difference between a natural conversation and an awkward pause. We route Penny's conversational turns through Sonnet 4.6 for quality and her quick "hold on while I check" interjections through Haiku 4.5 for speed. Both are streamed.

Retrieval done right

Claude has a million-token context window on Opus 4.7, Opus 4.6, and Sonnet 4.6 at standard pricing. That is enough to fit most small-company document sets in a single call. It is not enough to fit your law firm's case archive, your engineering firm's decade of CAD documentation, or your managed IT book of business in one shot. You need retrieval.

There are three retrieval patterns that work. Pick the one that matches your data.

Pattern one: static pre-load. If your knowledge base is under a million tokens and changes daily at most, load the whole thing into the prompt, mark it with cache_control, and pay the 10% cache-read rate on every call. No vector DB, no embeddings, no stale index. This is Penny's architecture. Our service catalog and compliance quick-reference total about 40,000 tokens loaded once per Apollo or calendar trigger, cached for an hour, and queried directly by Claude in context.

Pattern two: hybrid retrieval with a tool. Build a search_kb tool that accepts a query, runs a vector or keyword search against your corpus, and returns the top N chunks. Claude decides when to call it based on the user question. This is ComplyBot's architecture. Our CMMC knowledge base is 400,000+ tokens and growing, so we index it with a Postgres pgvector store and expose a lookup_control tool plus a semantic_search tool. Claude routes between them.

Pattern three: Managed Agents with MCP. If you are on Managed Agents, connect an MCP server that exposes your corpus. Claude calls the MCP tool, the server does retrieval, Anthropic runs the session. The MCP connector docs cover the wiring.

Whichever pattern you pick, do not skip the step that matters most: cite your sources. Every Petronella Technology Group agent that retrieves returns a citation block in its final answer so a human can audit the trail. For regulated industries this is not optional.

Rate limits are a production concern

Every Claude tier has an input-tokens-per-minute ceiling, an output-tokens-per-minute ceiling, and a requests-per-minute ceiling. The full table is in the rate limits docs. Tier 1 on Opus 4.x gives you 50 RPM, 30,000 ITPM, and 8,000 OTPM. Tier 4 (the highest standard tier before Enterprise) gives you 4,000 RPM, 2M ITPM, and 400,000 OTPM.

When you hit a limit the API returns a 429 with a retry-after header and a set of anthropic-ratelimit-* headers that tell you your current usage. Your code must respect retry-after and back off, or you will stay in 429 purgatory indefinitely. Here is the retry pattern we ship in every production client.

import time
import anthropic
from anthropic import RateLimitError, APIStatusError

def call_with_retry(client, **kwargs):
    max_attempts = 5
    for attempt in range(max_attempts):
        try:
            return client.messages.create(**kwargs)
        except RateLimitError as e:
            retry_after = int(e.response.headers.get("retry-after", "1"))
            if attempt == max_attempts - 1:
                raise
            time.sleep(retry_after)
        except APIStatusError as e:
            if 500 <= e.status_code < 600 and attempt < max_attempts - 1:
                # Exponential backoff on 5xx
                time.sleep(2 ** attempt)
                continue
            raise
    raise RuntimeError("exhausted retries")

Two rules of production rate-limit handling. First, never retry a 4xx that is not a 429; your request is malformed and retrying will not fix it. Second, combine rate-limit retry with a per-conversation timeout; a rate-limited request stuck in a retry loop for twenty minutes is as broken as a 500 error. Put a ceiling on total wall-clock time and fail loudly when you hit it.

The Batch API is the correct escape hatch for workloads that do not need low latency. You get a 50% discount on both input and output tokens, and separate, much higher rate limits. We use batch for nightly document classification jobs and for our competitor-monitoring agent, both of which run unattended and can tolerate up to 24-hour turnaround.

Benchmarks, or how to know it actually works

The single biggest delta between a working agent and a dangerous one is whether the team has a benchmark harness. Not unit tests. A regression benchmark.

A benchmark is a frozen set of input-output pairs that you run through your agent on every change, with an automated scorer that reports pass/fail. For Penny we have 120 voice transcripts with expected behavioral outcomes (did she book the assessment, did she quote a price incorrectly, did she name a vendor). For ComplyBot we have 300 CMMC questions with expected control IDs in the answer. For the Auto Blog Agent we have a fabrication detector that greps outputs for the long-dash character, fake statistics, and the strings we have banned from customer-facing copy.

The mechanical shape of a benchmark harness is boring on purpose.

import json
import anthropic
from pathlib import Path

client = anthropic.Anthropic()

def run_benchmark(benchmark_path: Path, agent_fn):
    cases = [json.loads(line) for line in benchmark_path.read_text().splitlines()]
    results = []
    for case in cases:
        output = agent_fn(case["input"])
        passed = all(check(output, case) for check in case["checks"])
        results.append({"id": case["id"], "passed": passed, "output": output})
    pass_rate = sum(1 for r in results if r["passed"]) / len(results)
    return pass_rate, results

def contains_check(expected: str):
    return lambda output, case: expected in output

def no_long_dash(output, case):
    return "\u2014" not in output

# Run before every prompt change
rate, details = run_benchmark(Path("benchmarks/complybot.jsonl"), ask_complybot)
print(f"pass_rate={rate:.2%}")

Ship this before you ship anything else. A prompt change that improves one scenario often breaks three others, and you will not notice until a customer does.

Observability is not optional

Every production Petronella Technology Group agent logs the same five fields on every call: request ID (from the x-request-id response header), model, input tokens (cached and uncached), output tokens, and latency. We ship those to PostgreSQL with a 90-day retention window and graph them in Grafana.

The minimum viable observability layer is a two-table schema.

CREATE TABLE agent_calls (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  request_id text,
  agent_name text NOT NULL,
  model text NOT NULL,
  input_tokens int NOT NULL,
  cache_read_input_tokens int NOT NULL DEFAULT 0,
  cache_creation_input_tokens int NOT NULL DEFAULT 0,
  output_tokens int NOT NULL,
  thinking_tokens int NOT NULL DEFAULT 0,
  stop_reason text,
  latency_ms int,
  error text,
  user_id text,
  session_id text,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE agent_events (
  id bigserial PRIMARY KEY,
  call_id uuid REFERENCES agent_calls(id),
  event_type text NOT NULL,
  payload jsonb,
  created_at timestamptz NOT NULL DEFAULT now()
);

With this you can answer the questions that matter. What is our cache hit rate by agent? Which tool gets called most often? What is the 95th-percentile latency? Which user session produced a failed tool call? For regulated clients the answer to "can you show me everything this agent did with my data last Tuesday" is non-negotiable, and a query against agent_events filtered by session_id is how you answer.

For distributed traces across multiple agents (for example, Penny handing off to ComplyBot for a compliance question) we use OpenTelemetry and propagate the traceparent header. The SDK does not care; it is just another header on outbound requests. Your observability stack does the rest.

Security and compliance posture

This is the part a demo tutorial never covers and a regulated-industry engagement cannot skip.

API key handling. Your ANTHROPIC_API_KEY is a credential with full spend authority. Store it in a secrets manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault), never in a source file, and rotate on a schedule. Every production key at Petronella Technology Group is tied to a single workspace in the Claude Console so a blast radius is contained.

Data residency. For Claude Opus 4.7, Opus 4.6, and newer models, you can set inference_geo: "us" to pin inference to US-only infrastructure, at a 1.1x pricing multiplier per the data residency docs. For CMMC and ITAR-adjacent work this is often the difference between a go and a no-go.

Zero Data Retention. Anthropic offers ZDR arrangements for eligible customers where prompts and completions are not retained at rest. This matters for HIPAA-regulated health data and any attorney-client privileged content. Contact Anthropic's sales team to get it configured before you send real production traffic.

Third-party platforms. Anthropic-hosted Claude also runs on AWS Bedrock, Google Vertex AI, and Microsoft Foundry. For clients with existing BAAs or cloud contracts, routing Claude through their existing cloud is often the cleanest compliance path. The SDK supports all three via environment variables (CLAUDE_CODE_USE_BEDROCK=1, CLAUDE_CODE_USE_VERTEX=1, CLAUDE_CODE_USE_FOUNDRY=1).

Human-in-the-loop gates. Every agent Petronella Technology Group ships that can take a consequential action (send email, spend money, modify a CRM record, commit code) has a human approval gate in front of the action. We learned this the hard way. The approval pattern is a database table of pending actions, a webhook or email that notifies the human, and a signed approve/deny URL the human taps on their phone. The agent waits on the action status before proceeding. Simple, boring, audit-friendly.

What we actually ship

Let us make this concrete. Petronella Technology Group currently runs the following Claude-backed production agents. Each one started as a prototype and graduated to production only after passing its benchmark harness and getting an observability backplane.

Penny answers (919) 348-4912 live. Sonnet 4.6 for conversation, Haiku 4.5 for quick confirmations, streaming end-to-end, 8,000-token cached system prompt. She books free 15-minute assessments onto the calendar and hands callers off to humans when the request is outside her competency.

Peter is the chat assistant on petronellatech.com. Sonnet 4.6, tool use for the knowledge base and lead capture form, prompt caching on the brand voice guidelines.

ComplyBot is the compliance copilot on petronella.ai. Opus 4.7 for reasoning, vector retrieval against our CMMC and HIPAA knowledge bases, extended thinking enabled, citation-mandatory output format.

Auto Blog Agent writes long-form content (including drafts of this post). Opus 4.7 with tool use for research, a strict benchmark harness that blocks long-dash characters and fabricated statistics, and a human-in-the-loop approval before anything ships.

Private digital twin voice assistants are deployed for regulated-industry clients under NDA. Same architecture as Penny, tuned to the client's voice and intake flow.

The AI services page at /ai/ documents the full fleet. For the hardware and infrastructure that hosts these agents on-premise or in private cloud, see /solutions/private-ai-cluster/. For the specific voice-agent build service (including a concrete SKU and scope), see /solutions/digital-twin-voice/.

A production checklist

If you are kicking off a Claude SDK build today, here is the sequence we follow on every new engagement.

  1. Pick the SDK layer. Custom orchestration or domain-specific tools equals raw Messages API. Code and file agents equal Claude Agent SDK. Hosted container workload equals Managed Agents.
  2. Pick the model. Haiku 4.5 for cost-sensitive high-volume simple tasks. Sonnet 4.6 for the default production agent. Opus 4.7 for reasoning-heavy work where quality matters more than cost.
  3. Write the system prompt. Include role, guardrails, banned behaviors, output format. Test it in isolation before adding tools.
  4. Define tools with tight descriptions. Each tool name and description is a prompt. Be explicit about when to use and when not to use.
  5. Enable prompt caching. Mark the stable chunks of your prompt with cache_control. Verify cache hits in the usage fields on call two.
  6. Wrap the call in retry logic. Respect retry-after on 429s. Exponential backoff on 5xx. Hard timeout on total attempts.
  7. Stream if a human is waiting. Voice, chat, and interactive tools need stream: true. Batch jobs do not.
  8. Build the benchmark harness first. Freeze ten to twenty input-output pairs with automated checks. Run them on every prompt change.
  9. Log everything. Request ID, model, tokens (cached and uncached), latency, tool calls, errors. Persist to a queryable store.
  10. Gate consequential actions. Never let an agent spend money, send external email, or modify a regulated record without a human approval step.

This checklist is the thing that turns a clever demo into a piece of software you can bill for, audit, and maintain.

Where we go from here

The Claude SDK is the best production-grade AI harness in the market as of April 2026. The tool-use contract is clean, the agent loop is batteries-included when you want it, prompt caching is priced aggressively enough to make real throughput affordable, and the data residency and third-party platform options give regulated businesses a compliance path. The gap between a working Claude prototype and a production Claude agent is not model capability. It is the plumbing: benchmarks, observability, retry logic, human gates, data-handling posture, and the discipline to ship slowly into production.

Petronella Technology Group has walked this path ten times now across internal agents and client deployments, including clients in healthcare, defense-adjacent manufacturing, legal, and engineering. If you are a regulated-industry business evaluating whether to build your own AI agents on Claude or stay on the commodity SaaS tools your competitors use, we offer a free 15-minute assessment with a human (not Penny) to scope the work. Call (919) 348-4912 and ask for a private AI readiness conversation, or use the form at /contact-us/. We will tell you honestly whether your workload is ready for an AI agent, which of the three SDK layers fits it, and what the realistic budget and timeline look like.

The AI that is going to matter in your business over the next five years is not the chatbot on your homepage. It is the agent that reads your email, handles your inbound calls, reviews your contracts, analyzes your compliance gaps, and does the work your senior people used to do at 2 a.m. Built correctly, on the Claude SDK, it is a competitive moat. Built carelessly, it is a compliance incident waiting to happen. The difference is the plumbing.


About the author: Craig Petronella is the founder of Petronella Technology Group and holds CMMC-RP, CCNA, CWNE, and DFE #604180 credentials. Petronella Technology Group is a CMMC-AB Registered Provider Organization (RPO #1449) and has held a BBB A+ rating since 2003. Call (919) 348-4912 or visit /contact-us/.

Need help implementing these strategies? Our cybersecurity experts can assess your environment and build a tailored plan.
Get Free Assessment

About the Author

Craig Petronella, CEO and Founder of Petronella Technology Group
CEO, Founder & AI Architect, Petronella Technology Group

Craig Petronella founded Petronella Technology Group in 2002 and has spent more than 30 years working at the intersection of cybersecurity, AI, compliance, and digital forensics. He holds the CMMC Registered Practitioner credential (RP-1372) issued by the Cyber AB, is an NC Licensed Digital Forensics Examiner (License #604180-DFE), and completed MIT Professional Education programs in AI, Blockchain, and Cybersecurity. Craig also holds CompTIA Security+, CCNA, and Hyperledger certifications.

He is an Amazon #1 Best-Selling Author of 15+ books on cybersecurity and compliance, host of the Encrypted Ambition podcast (95+ episodes on Apple Podcasts, Spotify, and Amazon), and a cybersecurity keynote speaker with 200+ engagements at conferences, law firms, and corporate boardrooms. Craig serves as Contributing Editor for Cybersecurity at NC Triangle Attorney at Law Magazine and is a guest lecturer at NCCU School of Law. He has served as a digital forensics expert witness in federal and state court cases involving cybercrime, cryptocurrency fraud, SIM-swap attacks, and data breaches.

Under his leadership, Petronella Technology Group has served 2,500+ clients, maintained a zero-breach record among compliant clients, earned a BBB A+ rating every year since 2003, and been featured as a cybersecurity authority on CBS, ABC, NBC, FOX, and WRAL. The company leverages SOC 2 Type II certified platforms and specializes in AI implementation, managed cybersecurity, CMMC/HIPAA/SOC 2 compliance, and digital forensics for businesses across the United States.

CMMC-RP NC Licensed DFE MIT Certified CompTIA Security+ Expert Witness 15+ Books
Related Service
Need Cybersecurity or Compliance Help?

Schedule a free consultation with our cybersecurity experts to discuss your security needs.

Schedule Free Consultation
Previous All Posts Next
Free cybersecurity consultation available Schedule Now