All articles
ArticleMarch 19, 2026

Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)

By AI Guy in LA

Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)

This build delivers a support/billing agent you can actually ship. It follows Brain+Hands separation, explicit tool contracts, a deterministic state machine, and secure backend tools. Stack: WordPress (frontend), Python FastAPI (tools + orchestrator), Redis (state/cache), Postgres (KB + logs), vector DB (memory), OpenAI/Groq/Anthropic (LLM).

1) Architecture overview
– Brain (LLM policy): Plans, chooses tools, reasons. No direct DB/API access.
– Hands (tools): Idempotent HTTP endpoints with strict schemas. Observable, rate-limited, auditable.
– Orchestrator: State machine controlling turns, tool calls, retries, and timeouts.
– Memory:
– Short-term: per-session scratchpad (Redis).
– Knowledge: vector search over product docs/FAQ.
– Facts: authoritative store lookups (billing, orders).
– Guardrails: auth, PII redaction, tool allowlist, cost/time budgets, circuit breakers.
– Integration: WordPress plugin sends chat events to orchestrator; streaming tokens back to UI.

2) Contracts first (make tools boring and safe)
Define JSON schemas for every tool. Keep them narrow, idempotent, and testable.

Example tool manifest (slice):
{
“name”: “get_order_status”,
“description”: “Return current order state for a given order_id.”,
“method”: “POST”,
“url”: “https://api.example.com/tools/get_order_status”,
“input_schema”: {
“type”: “object”,
“properties”: {
“order_id”: {“type”: “string”, “pattern”: “^[A-Z0-9-]{6,}$”}
},
“required”: [“order_id”],
“additionalProperties”: false
},
“output_schema”: {
“type”: “object”,
“properties”: {
“status”: {“type”: “string”},
“updated_at”: {“type”: “string”, “format”: “date-time”}
},
“required”: [“status”]
},
“timeouts_ms”: 2500,
“retries”: 1
}

3) Hands: secure Python FastAPI tools
– Enforce schema at the edge.
– Require JWT with narrow scopes.
– Add rate limits and audit logs.

from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel, Field
import time

app = FastAPI(title=”SupportAgentTools”)

class OrderReq(BaseModel):
order_id: str = Field(min_length=6, pattern=r”^[A-Z0-9-]{6,}$”)

class OrderResp(BaseModel):
status: str
updated_at: str | None = None

def auth(scope: str):
def _auth(token=Depends(…)): # your JWT dependency
if scope not in token.scopes:
raise HTTPException(403, “forbidden”)
return token.sub
return _auth

@app.post(“/tools/get_order_status”, response_model=OrderResp)
def get_order_status(req: OrderReq, _=Depends(auth(“order:read”))):
start = time.time()
# query read replica; maintain SLO use respective tools.
– General product usage -> retrieve_docs.
– Anything else -> answer concisely or ask a clarifying question.

6) Orchestrator: a small, reliable state machine
States:
– RECEIVE -> PLAN -> EXECUTE_TOOL? -> OBSERVE -> RESPOND -> END

Pseudo:
def handle_turn(msg, session_id):
budget = Budget(tokens=3000, tools=3, wall_ms=8000)
state = “PLAN”
memory = load_session(session_id)
while budget.ok() and state != “END”:
if state == “PLAN”:
action = llm_policy(memory, tool_manifest)
if action.type == “tool”:
state = “EXECUTE_TOOL”
else:
state = “RESPOND”
elif state == “EXECUTE_TOOL”:
result = call_tool(action.name, action.args, timeout=manifest[action.name].timeouts_ms)
record_observation(result)
state = “OBSERVE”
elif state == “OBSERVE”:
memory.update_with_observation(result)
if need_more_tools(result): state = “PLAN”
else: state = “RESPOND”
elif state == “RESPOND”:
reply = llm_response(memory)
emit_stream(reply)
state = “END”

Controls:
– Max 2 tool calls/turn for latency.
– Tool circuit breaker after 2x p95 failures.
– Token + time budgets enforced per turn.

7) Retrieval that doesn’t hallucinate
– Chunk docs to 300–500 tokens with overlap 50–100.
– Store title, URL, product tags, and last_updated.
– Rerank top 20 -> 5 with a fast cross-encoder or LLM-judge at small context.
– In answers, include “According to ()” and quote minimal lines.<br /> – Evict stale docs with last_updated TTL checks.</p> <p>8) Error handling and fallbacks<br /> – Tool error classes: 4xx user-fixable (show guidance), 5xx transient (retry with jitter), timeout (offer manual escalation).<br /> – If tools unavailable, switch to knowledge-only mode and surface a status note to the user.<br /> – Log: request_id, user_hash, tool_calls, latencies, token_usage, model, success_flag.</p> <p>9) WordPress integration (plugin sketch)<br /> – Shortcode [aiguy_chat] renders chat UI.<br /> – Frontend calls /wp-json/aiguy/v1/chat (nonce protected).<br /> – Server proxy signs JWT to orchestrator and streams chunks back.</p> <p>PHP (very abbreviated):<br /> add_action(‘rest_api_init’, function () {<br /> register_rest_route(‘aiguy/v1’, ‘/chat’, [‘methods’=>’POST’,’callback’=>’aiguy_chat’,’permission_callback’=>’__return_true’]);<br /> });<br /> function aiguy_chat(WP_REST_Request $r) {<br /> $jwt = make_scoped_jwt([‘aud’=>’orchestrator’,’scopes’=>[‘chat:send’]]);<br /> $resp = wp_remote_post(‘https://agent.example.com/chat’, [<br /> ‘headers’=>[‘Authorization’=>”Bearer $jwt”],<br /> ‘body’=>[‘session_id’=>get_session_id(), ‘message’=>$r->get_param(‘message’)],<br /> ‘timeout’=>15<br /> ]);<br /> return rest_ensure_response(wp_remote_retrieve_body($resp));<br /> }</p> <p>10) Models and performance<br /> – Use fast model for planning (e.g., gpt-4o-mini, llama-3.1-70b-instruct via Groq) and a stronger model for final generation when needed.<br /> – Target p95 < 2.5s single-turn with 0–1 tool; < 4.5s with 2 tools.<br /> – Cache retrieval and deterministic tool schemas to reduce tokens.</p> <p>11) Security checklist<br /> – Tool allowlist + strict JSON schemas.<br /> – JWT with narrow scopes + rotation.<br /> – PII redaction before logs; encrypt sensitive fields at rest.<br /> – Separate read/write tools; require user confirmation for writes.<br /> – Rate limit per IP/user/session; WAF on tool API.</p> <p>12) Observability<br /> – OpenTelemetry spans: plan, tool call, observe, generate.<br /> – Emit metrics: latency p50/p95, tool error rate, deflection rate, CSAT.<br /> – Log all prompts/responses with redaction; enable replay in a sandbox.</p> <p>13) Deployment<br /> – Dockerize orchestrator + tools. Separate autoscaling for tools with spiky I/O.<br /> – Blue/green deploy; health checks include LLM warmup and tool canary.<br /> – Backpressure: queue requests when model or DB under load; shed load gracefully with a user-facing “email me results” fallback.</p> <p>14) Minimal smoke test<br /> – Happy path: “Where is my order ABC123?”<br /> – Tool timeout path: inject 2s delay; verify graceful message.<br /> – Hallucination guard: ask for unavailable feature; ensure denial with doc citation.</p> <p>Deliverables you can ship this week<br /> – Tool API (FastAPI) with 3 tools: get_order_status, get_invoice_pdf, retrieve_docs.<br /> – Orchestrator with state machine, budgets, and streaming.<br /> – WordPress plugin with nonce-protected REST route and basic chat UI.<br /> – Vector index with top 20 support articles and citations in answers.<br /> – Dashboards: latency, tool errors, deflection, cost.</p> </div><div class="mt-20 pt-10 border-t border-ink-200 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6"><a class="text-sm text-ink-500 hover:text-ink-900 transition-colors" href="/blog">← More articles</a><a class="inline-flex items-center gap-2 rounded-full bg-ember-600 px-5 py-2.5 text-sm font-medium text-white hover:bg-ember-700 transition-colors" href="/#free-audit">Book a free audit →</a></div></article></div></main><footer class="bg-paper border-t border-ink-200"><div class="mx-auto max-w-6xl px-6 lg:px-8 py-16 lg:py-20 grid gap-12 lg:grid-cols-12"><div class="lg:col-span-5"><div class="inline-flex items-baseline gap-2 text-ink-900"><span class="font-serif text-2xl leading-none tracking-tight">AI Guy</span><span class="font-sans text-[0.7rem] uppercase tracking-[0.22em] text-ink-500">in LA</span></div><p class="mt-5 text-ink-600 max-w-md leading-relaxed">Practical AI engineering and consulting for real organizations in Los Angeles. Websites, chatbots, workflow automation, and custom software — built with privacy-first guardrails.</p><div class="mt-8 inline-flex items-center gap-3 rounded-full border border-ink-200 px-3 py-1.5 text-xs text-ink-600"><span class="h-1.5 w-1.5 rounded-full bg-ember-500"></span>Based in Los Angeles, California</div></div><div class="lg:col-span-4 grid grid-cols-2 gap-8"><nav aria-label="Work"><p class="eyebrow mb-4">Work</p><ul class="space-y-2.5 text-[0.95rem] text-ink-700"><li><a class="hover:text-ink-900 transition-colors" href="/#services">Services</a></li><li><a class="hover:text-ink-900 transition-colors" href="/case-studies">Case Studies</a></li><li><a class="hover:text-ink-900 transition-colors" href="/blog">Blog</a></li></ul></nav><nav aria-label="Company"><p class="eyebrow mb-4">Company</p><ul class="space-y-2.5 text-[0.95rem] text-ink-700"><li><a class="hover:text-ink-900 transition-colors" href="/about">About</a></li><li><a class="hover:text-ink-900 transition-colors" href="/#privacy">Privacy guardrails</a></li><li><a class="hover:text-ink-900 transition-colors" href="/audit">Free audit</a></li></ul></nav></div><div class="lg:col-span-3"><p class="eyebrow mb-4">Get a free audit</p><p class="text-[0.95rem] text-ink-600 leading-relaxed">30 minutes. No pitch. Three practical recommendations for your site and workflows.</p><a class="mt-5 inline-flex items-center gap-2 text-sm font-medium text-ink-900 group" href="/audit"><span class="underline underline-offset-4 decoration-1 group-hover:decoration-2">Book your audit</span><span aria-hidden="true" class="transition-transform group-hover:translate-x-1">→</span></a></div></div><div class="border-t border-ink-200"><div class="mx-auto max-w-6xl px-6 lg:px-8 py-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-xs text-ink-500"><p>© <!-- -->2026<!-- --> AI Guy in LA. Built locally in Los Angeles, CA.</p><p class="font-mono tracking-wide uppercase text-[10px] text-ink-400">Privacy-first · Compliance-aware · Human in the loop</p></div></div></footer><button type="button" aria-label="Back to top" tabindex="-1" class="fixed bottom-5 right-5 sm:bottom-8 sm:right-8 z-40 h-11 w-11 sm:h-12 sm:w-12 rounded-full bg-ink-900 text-paper border border-ink-900 shadow-card-lg hover:shadow-card hover:bg-ink-800 hover:-translate-y-0.5 transition-all duration-200 ease-out opacity-0 translate-y-3 pointer-events-none inline-flex items-center justify-center"><svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"></path><path d="M5 12l7-7 7 7"></path></svg></button><script src="/_next/static/chunks/webpack-154595e9cefd471e.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/media/4c9affa5bc8f420e-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/_next/static/media/af4bf8399d1aacdf-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n3:HL[\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n4:HL[\"/_next/static/css/8bfd1a7cc7416179.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"5:I[2846,[],\"\"]\n8:I[4707,[],\"\"]\na:I[6423,[],\"\"]\nb:I[1322,[\"972\",\"static/chunks/972-fbe8e47101704ce9.js\",\"185\",\"static/chunks/app/layout-780e448d91804a48.js\"],\"default\"]\nc:I[2972,[\"972\",\"static/chunks/972-fbe8e47101704ce9.js\",\"878\",\"static/chunks/878-f7029bec14cb6a4b.js\",\"308\",\"static/chunks/app/blog/%5Bslug%5D/page-779567937409a39e.js\"],\"\"]\nd:I[9177,[\"972\",\"static/chunks/972-fbe8e47101704ce9.js\",\"185\",\"static/chunks/app/layout-780e448d91804a48.js\"],\"default\"]\nf:I[1060,[],\"\"]\n9:[\"slug\",\"shipping-a-production-ready-brainhands-support-agent-wordpress-python-api\",\"d\"]\n10:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L5\",null,{\"buildId\":\"x8yFcqbKc2kRsd9T4qV3I\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"blog\",\"shipping-a-production-ready-brainhands-support-agent-wordpress-python-api\"],\"initialTree\":[\"\",{\"children\":[\"blog\",{\"children\":[[\"slug\",\"shipping-a-production-ready-brainhands-support-agent-wordpress-python-api\",\"d\"],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"blog\",{\"children\":[[\"slug\",\"shipping-a-production-ready-brainhands-support-agent-wordpress-python-api\",\"d\"],{\"children\":[\"__PAGE__\",{},[[\"$L6\",\"$L7\",null],null],null]},[null,[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"blog\",\"children\",\"$9\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[null,[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"blog\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/8bfd1a7cc7416179.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"LocalBusiness\\\",\\\"name\\\":\\\"AI Guy in LA\\\",\\\"url\\\":\\\"https://aiguyinla.com\\\",\\\"description\\\":\\\"AI websites, chatbots, content automation, and custom software development for Los Angeles businesses.\\\",\\\"address\\\":{\\\"@type\\\":\\\"PostalAddress\\\",\\\"addressLocality\\\":\\\"Los Angeles\\\",\\\"addressRegion\\\":\\\"CA\\\",\\\"addressCountry\\\":\\\"US\\\"},\\\"areaServed\\\":{\\\"@type\\\":\\\"City\\\",\\\"name\\\":\\\"Los Angeles\\\"},\\\"knowsAbout\\\":[\\\"AI chatbots\\\",\\\"Workflow automation\\\",\\\"Headless WordPress\\\",\\\"Next.js\\\",\\\"Django\\\",\\\"AI content systems\\\"]}\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_3a6cb3 __variable_d2dc74 __variable_3c557b font-sans bg-paper text-ink-900 antialiased min-h-screen flex flex-col\",\"children\":[[\"$\",\"$Lb\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[\"$\",\"div\",null,{\"className\":\"bg-paper\",\"children\":[\"$\",\"div\",null,{\"className\":\"mx-auto max-w-2xl px-6 lg:px-8 py-32 text-center\",\"children\":[[\"$\",\"p\",null,{\"className\":\"font-mono text-[11px] tracking-[0.18em] uppercase text-ember-600\",\"children\":\"404 · Page not found\"}],[\"$\",\"h1\",null,{\"className\":\"mt-5 font-serif text-[2.5rem] lg:text-[3.5rem] leading-[1.05] tracking-tight text-ink-900 font-medium\",\"children\":\"We could not find that page.\"}],[\"$\",\"p\",null,{\"className\":\"mt-5 text-ink-600 leading-relaxed\",\"children\":\"The link may be broken or the page has moved. Try one of these instead.\"}],[\"$\",\"div\",null,{\"className\":\"mt-9 flex items-center justify-center gap-3\",\"children\":[[\"$\",\"$Lc\",null,{\"href\":\"/\",\"className\":\"inline-flex items-center gap-2 rounded-full bg-ember-600 px-5 py-2.5 text-sm font-medium text-white hover:bg-ember-700 transition-colors\",\"children\":\"Go home →\"}],[\"$\",\"$Lc\",null,{\"href\":\"/blog\",\"className\":\"inline-flex items-center gap-2 rounded-full border border-ink-300 px-5 py-2.5 text-sm font-medium text-ink-900 hover:border-ink-500 hover:bg-paper-100 transition-colors\",\"children\":\"Read the blog\"}]]}]]}]}],\"notFoundStyles\":[]}]}],[\"$\",\"footer\",null,{\"className\":\"bg-paper border-t border-ink-200\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mx-auto max-w-6xl px-6 lg:px-8 py-16 lg:py-20 grid gap-12 lg:grid-cols-12\",\"children\":[[\"$\",\"div\",null,{\"className\":\"lg:col-span-5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"inline-flex items-baseline gap-2 text-ink-900\",\"children\":[[\"$\",\"span\",null,{\"className\":\"font-serif text-2xl leading-none tracking-tight\",\"children\":\"AI Guy\"}],[\"$\",\"span\",null,{\"className\":\"font-sans text-[0.7rem] uppercase tracking-[0.22em] text-ink-500\",\"children\":\"in LA\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-5 text-ink-600 max-w-md leading-relaxed\",\"children\":\"Practical AI engineering and consulting for real organizations in Los Angeles. Websites, chatbots, workflow automation, and custom software — built with privacy-first guardrails.\"}],[\"$\",\"div\",null,{\"className\":\"mt-8 inline-flex items-center gap-3 rounded-full border border-ink-200 px-3 py-1.5 text-xs text-ink-600\",\"children\":[[\"$\",\"span\",null,{\"className\":\"h-1.5 w-1.5 rounded-full bg-ember-500\"}],\"Based in Los Angeles, California\"]}]]}],[\"$\",\"div\",null,{\"className\":\"lg:col-span-4 grid grid-cols-2 gap-8\",\"children\":[[\"$\",\"nav\",\"Work\",{\"aria-label\":\"Work\",\"children\":[[\"$\",\"p\",null,{\"className\":\"eyebrow mb-4\",\"children\":\"Work\"}],[\"$\",\"ul\",null,{\"className\":\"space-y-2.5 text-[0.95rem] text-ink-700\",\"children\":[[\"$\",\"li\",\"/#services\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/#services\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"Services\"}]}],[\"$\",\"li\",\"/case-studies\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/case-studies\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"Case Studies\"}]}],[\"$\",\"li\",\"/blog\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/blog\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"Blog\"}]}]]}]]}],[\"$\",\"nav\",\"Company\",{\"aria-label\":\"Company\",\"children\":[[\"$\",\"p\",null,{\"className\":\"eyebrow mb-4\",\"children\":\"Company\"}],[\"$\",\"ul\",null,{\"className\":\"space-y-2.5 text-[0.95rem] text-ink-700\",\"children\":[[\"$\",\"li\",\"/about\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/about\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"About\"}]}],[\"$\",\"li\",\"/#privacy\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/#privacy\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"Privacy guardrails\"}]}],[\"$\",\"li\",\"/audit\",{\"children\":[\"$\",\"$Lc\",null,{\"href\":\"/audit\",\"className\":\"hover:text-ink-900 transition-colors\",\"children\":\"Free audit\"}]}]]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"lg:col-span-3\",\"children\":[[\"$\",\"p\",null,{\"className\":\"eyebrow mb-4\",\"children\":\"Get a free audit\"}],[\"$\",\"p\",null,{\"className\":\"text-[0.95rem] text-ink-600 leading-relaxed\",\"children\":\"30 minutes. No pitch. Three practical recommendations for your site and workflows.\"}],[\"$\",\"$Lc\",null,{\"href\":\"/audit\",\"className\":\"mt-5 inline-flex items-center gap-2 text-sm font-medium text-ink-900 group\",\"children\":[[\"$\",\"span\",null,{\"className\":\"underline underline-offset-4 decoration-1 group-hover:decoration-2\",\"children\":\"Book your audit\"}],[\"$\",\"span\",null,{\"aria-hidden\":true,\"className\":\"transition-transform group-hover:translate-x-1\",\"children\":\"→\"}]]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"border-t border-ink-200\",\"children\":[\"$\",\"div\",null,{\"className\":\"mx-auto max-w-6xl px-6 lg:px-8 py-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-xs text-ink-500\",\"children\":[[\"$\",\"p\",null,{\"children\":[\"© \",2026,\" AI Guy in LA. Built locally in Los Angeles, CA.\"]}],[\"$\",\"p\",null,{\"className\":\"font-mono tracking-wide uppercase text-[10px] text-ink-400\",\"children\":\"Privacy-first · Compliance-aware · Human in the loop\"}]]}]}]]}],[\"$\",\"$Ld\",null,{}]]}]]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$Le\"],\"globalErrorComponent\":\"$f\",\"missingSlots\":\"$W10\"}]\n"])</script><script>self.__next_f.push([1,"11:I[5878,[\"972\",\"static/chunks/972-fbe8e47101704ce9.js\",\"878\",\"static/chunks/878-f7029bec14cb6a4b.js\",\"308\",\"static/chunks/app/blog/%5Bslug%5D/page-779567937409a39e.js\"],\"Image\"]\n12:T23e7,"])</script><script>self.__next_f.push([1,"\u003cp\u003eThis build delivers a support/billing agent you can actually ship. It follows Brain+Hands separation, explicit tool contracts, a deterministic state machine, and secure backend tools. Stack: WordPress (frontend), Python FastAPI (tools + orchestrator), Redis (state/cache), Postgres (KB + logs), vector DB (memory), OpenAI/Groq/Anthropic (LLM).\u003c/p\u003e\n\u003cp\u003e1) Architecture overview\u003cbr /\u003e\n\u0026#8211; Brain (LLM policy): Plans, chooses tools, reasons. No direct DB/API access.\u003cbr /\u003e\n\u0026#8211; Hands (tools): Idempotent HTTP endpoints with strict schemas. Observable, rate-limited, auditable.\u003cbr /\u003e\n\u0026#8211; Orchestrator: State machine controlling turns, tool calls, retries, and timeouts.\u003cbr /\u003e\n\u0026#8211; Memory:\u003cbr /\u003e\n \u0026#8211; Short-term: per-session scratchpad (Redis).\u003cbr /\u003e\n \u0026#8211; Knowledge: vector search over product docs/FAQ.\u003cbr /\u003e\n \u0026#8211; Facts: authoritative store lookups (billing, orders).\u003cbr /\u003e\n\u0026#8211; Guardrails: auth, PII redaction, tool allowlist, cost/time budgets, circuit breakers.\u003cbr /\u003e\n\u0026#8211; Integration: WordPress plugin sends chat events to orchestrator; streaming tokens back to UI.\u003c/p\u003e\n\u003cp\u003e2) Contracts first (make tools boring and safe)\u003cbr /\u003e\nDefine JSON schemas for every tool. Keep them narrow, idempotent, and testable.\u003c/p\u003e\n\u003cp\u003eExample tool manifest (slice):\u003cbr /\u003e\n{\u003cbr /\u003e\n \u0026#8220;name\u0026#8221;: \u0026#8220;get_order_status\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;description\u0026#8221;: \u0026#8220;Return current order state for a given order_id.\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;method\u0026#8221;: \u0026#8220;POST\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;url\u0026#8221;: \u0026#8220;https://api.example.com/tools/get_order_status\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;input_schema\u0026#8221;: {\u003cbr /\u003e\n \u0026#8220;type\u0026#8221;: \u0026#8220;object\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;properties\u0026#8221;: {\u003cbr /\u003e\n \u0026#8220;order_id\u0026#8221;: {\u0026#8220;type\u0026#8221;: \u0026#8220;string\u0026#8221;, \u0026#8220;pattern\u0026#8221;: \u0026#8220;^[A-Z0-9-]{6,}$\u0026#8221;}\u003cbr /\u003e\n },\u003cbr /\u003e\n \u0026#8220;required\u0026#8221;: [\u0026#8220;order_id\u0026#8221;],\u003cbr /\u003e\n \u0026#8220;additionalProperties\u0026#8221;: false\u003cbr /\u003e\n },\u003cbr /\u003e\n \u0026#8220;output_schema\u0026#8221;: {\u003cbr /\u003e\n \u0026#8220;type\u0026#8221;: \u0026#8220;object\u0026#8221;,\u003cbr /\u003e\n \u0026#8220;properties\u0026#8221;: {\u003cbr /\u003e\n \u0026#8220;status\u0026#8221;: {\u0026#8220;type\u0026#8221;: \u0026#8220;string\u0026#8221;},\u003cbr /\u003e\n \u0026#8220;updated_at\u0026#8221;: {\u0026#8220;type\u0026#8221;: \u0026#8220;string\u0026#8221;, \u0026#8220;format\u0026#8221;: \u0026#8220;date-time\u0026#8221;}\u003cbr /\u003e\n },\u003cbr /\u003e\n \u0026#8220;required\u0026#8221;: [\u0026#8220;status\u0026#8221;]\u003cbr /\u003e\n },\u003cbr /\u003e\n \u0026#8220;timeouts_ms\u0026#8221;: 2500,\u003cbr /\u003e\n \u0026#8220;retries\u0026#8221;: 1\u003cbr /\u003e\n}\u003c/p\u003e\n\u003cp\u003e3) Hands: secure Python FastAPI tools\u003cbr /\u003e\n\u0026#8211; Enforce schema at the edge.\u003cbr /\u003e\n\u0026#8211; Require JWT with narrow scopes.\u003cbr /\u003e\n\u0026#8211; Add rate limits and audit logs.\u003c/p\u003e\n\u003cp\u003efrom fastapi import FastAPI, Depends, HTTPException\u003cbr /\u003e\nfrom pydantic import BaseModel, Field\u003cbr /\u003e\nimport time\u003c/p\u003e\n\u003cp\u003eapp = FastAPI(title=\u0026#8221;SupportAgentTools\u0026#8221;)\u003c/p\u003e\n\u003cp\u003eclass OrderReq(BaseModel):\u003cbr /\u003e\n order_id: str = Field(min_length=6, pattern=r\u0026#8221;^[A-Z0-9-]{6,}$\u0026#8221;)\u003c/p\u003e\n\u003cp\u003eclass OrderResp(BaseModel):\u003cbr /\u003e\n status: str\u003cbr /\u003e\n updated_at: str | None = None\u003c/p\u003e\n\u003cp\u003edef auth(scope: str):\u003cbr /\u003e\n def _auth(token=Depends(\u0026#8230;)): # your JWT dependency\u003cbr /\u003e\n if scope not in token.scopes:\u003cbr /\u003e\n raise HTTPException(403, \u0026#8220;forbidden\u0026#8221;)\u003cbr /\u003e\n return token.sub\u003cbr /\u003e\n return _auth\u003c/p\u003e\n\u003cp\u003e@app.post(\u0026#8220;/tools/get_order_status\u0026#8221;, response_model=OrderResp)\u003cbr /\u003e\ndef get_order_status(req: OrderReq, _=Depends(auth(\u0026#8220;order:read\u0026#8221;))):\u003cbr /\u003e\n start = time.time()\u003cbr /\u003e\n # query read replica; maintain SLO use respective tools.\u003cbr /\u003e\n\u0026#8211; General product usage -\u0026gt; retrieve_docs.\u003cbr /\u003e\n\u0026#8211; Anything else -\u0026gt; answer concisely or ask a clarifying question.\u003c/p\u003e\n\u003cp\u003e6) Orchestrator: a small, reliable state machine\u003cbr /\u003e\nStates:\u003cbr /\u003e\n\u0026#8211; RECEIVE -\u0026gt; PLAN -\u0026gt; EXECUTE_TOOL? -\u0026gt; OBSERVE -\u0026gt; RESPOND -\u0026gt; END\u003c/p\u003e\n\u003cp\u003ePseudo:\u003cbr /\u003e\ndef handle_turn(msg, session_id):\u003cbr /\u003e\n budget = Budget(tokens=3000, tools=3, wall_ms=8000)\u003cbr /\u003e\n state = \u0026#8220;PLAN\u0026#8221;\u003cbr /\u003e\n memory = load_session(session_id)\u003cbr /\u003e\n while budget.ok() and state != \u0026#8220;END\u0026#8221;:\u003cbr /\u003e\n if state == \u0026#8220;PLAN\u0026#8221;:\u003cbr /\u003e\n action = llm_policy(memory, tool_manifest)\u003cbr /\u003e\n if action.type == \u0026#8220;tool\u0026#8221;:\u003cbr /\u003e\n state = \u0026#8220;EXECUTE_TOOL\u0026#8221;\u003cbr /\u003e\n else:\u003cbr /\u003e\n state = \u0026#8220;RESPOND\u0026#8221;\u003cbr /\u003e\n elif state == \u0026#8220;EXECUTE_TOOL\u0026#8221;:\u003cbr /\u003e\n result = call_tool(action.name, action.args, timeout=manifest[action.name].timeouts_ms)\u003cbr /\u003e\n record_observation(result)\u003cbr /\u003e\n state = \u0026#8220;OBSERVE\u0026#8221;\u003cbr /\u003e\n elif state == \u0026#8220;OBSERVE\u0026#8221;:\u003cbr /\u003e\n memory.update_with_observation(result)\u003cbr /\u003e\n if need_more_tools(result): state = \u0026#8220;PLAN\u0026#8221;\u003cbr /\u003e\n else: state = \u0026#8220;RESPOND\u0026#8221;\u003cbr /\u003e\n elif state == \u0026#8220;RESPOND\u0026#8221;:\u003cbr /\u003e\n reply = llm_response(memory)\u003cbr /\u003e\n emit_stream(reply)\u003cbr /\u003e\n state = \u0026#8220;END\u0026#8221;\u003c/p\u003e\n\u003cp\u003eControls:\u003cbr /\u003e\n\u0026#8211; Max 2 tool calls/turn for latency.\u003cbr /\u003e\n\u0026#8211; Tool circuit breaker after 2x p95 failures.\u003cbr /\u003e\n\u0026#8211; Token + time budgets enforced per turn.\u003c/p\u003e\n\u003cp\u003e7) Retrieval that doesn’t hallucinate\u003cbr /\u003e\n\u0026#8211; Chunk docs to 300–500 tokens with overlap 50–100.\u003cbr /\u003e\n\u0026#8211; Store title, URL, product tags, and last_updated.\u003cbr /\u003e\n\u0026#8211; Rerank top 20 -\u0026gt; 5 with a fast cross-encoder or LLM-judge at small context.\u003cbr /\u003e\n\u0026#8211; In answers, include “According to \u003ctitle\u003e ()” and quote minimal lines.\u003cbr /\u003e\n\u0026#8211; Evict stale docs with last_updated TTL checks.\u003c/p\u003e\n\u003cp\u003e8) Error handling and fallbacks\u003cbr /\u003e\n\u0026#8211; Tool error classes: 4xx user-fixable (show guidance), 5xx transient (retry with jitter), timeout (offer manual escalation).\u003cbr /\u003e\n\u0026#8211; If tools unavailable, switch to knowledge-only mode and surface a status note to the user.\u003cbr /\u003e\n\u0026#8211; Log: request_id, user_hash, tool_calls, latencies, token_usage, model, success_flag.\u003c/p\u003e\n\u003cp\u003e9) WordPress integration (plugin sketch)\u003cbr /\u003e\n\u0026#8211; Shortcode [aiguy_chat] renders chat UI.\u003cbr /\u003e\n\u0026#8211; Frontend calls /wp-json/aiguy/v1/chat (nonce protected).\u003cbr /\u003e\n\u0026#8211; Server proxy signs JWT to orchestrator and streams chunks back.\u003c/p\u003e\n\u003cp\u003ePHP (very abbreviated):\u003cbr /\u003e\nadd_action(\u0026#8216;rest_api_init\u0026#8217;, function () {\u003cbr /\u003e\n register_rest_route(\u0026#8216;aiguy/v1\u0026#8217;, \u0026#8216;/chat\u0026#8217;, [\u0026#8216;methods\u0026#8217;=\u0026gt;\u0026#8217;POST\u0026#8217;,\u0026#8217;callback\u0026#8217;=\u0026gt;\u0026#8217;aiguy_chat\u0026#8217;,\u0026#8217;permission_callback\u0026#8217;=\u0026gt;\u0026#8217;__return_true\u0026#8217;]);\u003cbr /\u003e\n});\u003cbr /\u003e\nfunction aiguy_chat(WP_REST_Request $r) {\u003cbr /\u003e\n $jwt = make_scoped_jwt([\u0026#8216;aud\u0026#8217;=\u0026gt;\u0026#8217;orchestrator\u0026#8217;,\u0026#8217;scopes\u0026#8217;=\u0026gt;[\u0026#8216;chat:send\u0026#8217;]]);\u003cbr /\u003e\n $resp = wp_remote_post(\u0026#8216;https://agent.example.com/chat\u0026#8217;, [\u003cbr /\u003e\n \u0026#8216;headers\u0026#8217;=\u0026gt;[\u0026#8216;Authorization\u0026#8217;=\u0026gt;\u0026#8221;Bearer $jwt\u0026#8221;],\u003cbr /\u003e\n \u0026#8216;body\u0026#8217;=\u0026gt;[\u0026#8216;session_id\u0026#8217;=\u0026gt;get_session_id(), \u0026#8216;message\u0026#8217;=\u0026gt;$r-\u0026gt;get_param(\u0026#8216;message\u0026#8217;)],\u003cbr /\u003e\n \u0026#8216;timeout\u0026#8217;=\u0026gt;15\u003cbr /\u003e\n ]);\u003cbr /\u003e\n return rest_ensure_response(wp_remote_retrieve_body($resp));\u003cbr /\u003e\n}\u003c/p\u003e\n\u003cp\u003e10) Models and performance\u003cbr /\u003e\n\u0026#8211; Use fast model for planning (e.g., gpt-4o-mini, llama-3.1-70b-instruct via Groq) and a stronger model for final generation when needed.\u003cbr /\u003e\n\u0026#8211; Target p95 \u0026lt; 2.5s single-turn with 0–1 tool; \u0026lt; 4.5s with 2 tools.\u003cbr /\u003e\n\u0026#8211; Cache retrieval and deterministic tool schemas to reduce tokens.\u003c/p\u003e\n\u003cp\u003e11) Security checklist\u003cbr /\u003e\n\u0026#8211; Tool allowlist + strict JSON schemas.\u003cbr /\u003e\n\u0026#8211; JWT with narrow scopes + rotation.\u003cbr /\u003e\n\u0026#8211; PII redaction before logs; encrypt sensitive fields at rest.\u003cbr /\u003e\n\u0026#8211; Separate read/write tools; require user confirmation for writes.\u003cbr /\u003e\n\u0026#8211; Rate limit per IP/user/session; WAF on tool API.\u003c/p\u003e\n\u003cp\u003e12) Observability\u003cbr /\u003e\n\u0026#8211; OpenTelemetry spans: plan, tool call, observe, generate.\u003cbr /\u003e\n\u0026#8211; Emit metrics: latency p50/p95, tool error rate, deflection rate, CSAT.\u003cbr /\u003e\n\u0026#8211; Log all prompts/responses with redaction; enable replay in a sandbox.\u003c/p\u003e\n\u003cp\u003e13) Deployment\u003cbr /\u003e\n\u0026#8211; Dockerize orchestrator + tools. Separate autoscaling for tools with spiky I/O.\u003cbr /\u003e\n\u0026#8211; Blue/green deploy; health checks include LLM warmup and tool canary.\u003cbr /\u003e\n\u0026#8211; Backpressure: queue requests when model or DB under load; shed load gracefully with a user-facing “email me results” fallback.\u003c/p\u003e\n\u003cp\u003e14) Minimal smoke test\u003cbr /\u003e\n\u0026#8211; Happy path: “Where is my order ABC123?”\u003cbr /\u003e\n\u0026#8211; Tool timeout path: inject 2s delay; verify graceful message.\u003cbr /\u003e\n\u0026#8211; Hallucination guard: ask for unavailable feature; ensure denial with doc citation.\u003c/p\u003e\n\u003cp\u003eDeliverables you can ship this week\u003cbr /\u003e\n\u0026#8211; Tool API (FastAPI) with 3 tools: get_order_status, get_invoice_pdf, retrieve_docs.\u003cbr /\u003e\n\u0026#8211; Orchestrator with state machine, budgets, and streaming.\u003cbr /\u003e\n\u0026#8211; WordPress plugin with nonce-protected REST route and basic chat UI.\u003cbr /\u003e\n\u0026#8211; Vector index with top 20 support articles and citations in answers.\u003cbr /\u003e\n\u0026#8211; Dashboards: latency, tool errors, deflection, cost.\u003c/p\u003e\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"div\",null,{\"className\":\"bg-paper\",\"children\":[\"$\",\"article\",null,{\"className\":\"mx-auto max-w-3xl px-6 lg:px-8 pt-12 pb-20 lg:pt-16 lg:pb-28\",\"children\":[[\"$\",\"$Lc\",null,{\"href\":\"/blog\",\"className\":\"inline-flex items-center gap-2 text-sm text-ink-500 hover:text-ink-900 transition-colors\",\"children\":[[\"$\",\"span\",null,{\"aria-hidden\":true,\"children\":\"←\"}],\" All articles\"]}],[\"$\",\"header\",null,{\"className\":\"mt-10\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3 text-ink-400\",\"children\":[[\"$\",\"span\",null,{\"className\":\"eyebrow\",\"children\":\"Article\"}],[\"$\",\"span\",null,{\"className\":\"h-px flex-1 bg-ink-200\"}],[\"$\",\"span\",null,{\"className\":\"font-mono text-[11px] tabular-nums\",\"children\":\"March 19, 2026\"}]]}],[\"$\",\"h1\",null,{\"className\":\"mt-7 font-serif text-[2.25rem] sm:text-[2.75rem] lg:text-[3.5rem] leading-[1.05] tracking-tight text-ink-900 font-medium\",\"children\":\"Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)\"}],[\"$\",\"p\",null,{\"className\":\"mt-7 text-sm text-ink-500\",\"children\":[\"By \",[\"$\",\"span\",null,{\"className\":\"text-ink-900 font-medium\",\"children\":\"AI Guy in LA\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"mt-10 aspect-[16/9] w-full overflow-hidden rounded-2xl border border-ink-200 bg-paper-100 relative\",\"children\":[\"$\",\"$L11\",null,{\"src\":\"https://cms.aiguyinla.com/wp-content/uploads/2026/03/aw-auto-254-1773904594.png\",\"alt\":\"Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)\",\"fill\":true,\"priority\":true,\"sizes\":\"(min-width: 1024px) 768px, 100vw\",\"className\":\"object-cover\"}]}],[\"$\",\"div\",null,{\"className\":\"article mt-12\",\"dangerouslySetInnerHTML\":{\"__html\":\"$12\"}}],[\"$\",\"div\",null,{\"className\":\"mt-20 pt-10 border-t border-ink-200 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6\",\"children\":[[\"$\",\"$Lc\",null,{\"href\":\"/blog\",\"className\":\"text-sm text-ink-500 hover:text-ink-900 transition-colors\",\"children\":\"← More articles\"}],[\"$\",\"$Lc\",null,{\"href\":\"/#free-audit\",\"className\":\"inline-flex items-center gap-2 rounded-full bg-ember-600 px-5 py-2.5 text-sm font-medium text-white hover:bg-ember-700 transition-colors\",\"children\":\"Book a free audit →\"}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"e:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API) | AI Guy in LA\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"A practical blueprint for a multi-tool customer support agent with strict Brain+Hands separation, deterministic orchestration, secure API tools, and WordPress i\"}],[\"$\",\"meta\",\"4\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"5\",{\"name\":\"google-site-verification\",\"content\":\"M14ImkJLWzYuttb-yJtBlPnbG6tmwhJ_3YIPj3n3Wi0\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:title\",\"content\":\"Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:description\",\"content\":\"A practical blueprint for a multi-tool customer support agent with strict Brain+Hands separation, deterministic orchestration, secure API tools, and WordPress i\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image\",\"content\":\"https://cms.aiguyinla.com/wp-content/uploads/2026/03/aw-auto-254-1773904594.png\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"10\",{\"property\":\"article:published_time\",\"content\":\"2026-03-19T00:16:04\"}],[\"$\",\"meta\",\"11\",{\"property\":\"article:modified_time\",\"content\":\"2026-03-19T00:16:04\"}],[\"$\",\"meta\",\"12\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:title\",\"content\":\"Shipping a Production-Ready Brain+Hands Support Agent (WordPress + Python API)\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:description\",\"content\":\"A practical blueprint for a multi-tool customer support agent with strict Brain+Hands separation, deterministic orchestration, secure API tools, and WordPress i\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:image\",\"content\":\"https://cms.aiguyinla.com/wp-content/uploads/2026/03/aw-auto-254-1773904594.png\"}],[\"$\",\"meta\",\"16\",{\"name\":\"next-size-adjust\"}]]\n6:null\n"])</script></body></html>