Work · National food brokerage
AI-augmented IT operations platform
Skills exercised
I built an internal operations platform that unifies the IT team's core systems into a governed data and automation layer. It brings ITSM, identity and endpoint management, remote support, network, security, patch management, documentation, and task tracking into a Supabase store; exposes them as Cowork skills; and runs a conversational ticket agent that drafts a first pass on every new ticket. Every state-changing action is confirm-gated.
Force-multiplier for a three-person IT team supporting 850 users across 51 offices, inside a company of more than 1,000 employees whose infrastructure the team also owns end to end. It turns "half a dozen different consoles you have to log into" into one data store with one AI-augmented interface, and pushes the team's most common operations (search history, triage, diagnose, draft, enrich, look up live state) from minutes to seconds, plus an unattended agent that does the first pass of triage before a human opens the ticket.
Why this exists
The IT team supports a national user base spread across 51 offices. Knowledge management, automation, and additional support capacity already carry a lot of weight. This platform is the next layer beyond those: an AI-mediated interface to the team's data, plus an autonomous agent that shortens common operational work.
The "AI-augmented" framing matters. The platform does not replace human technicians. The agent reads, suggests, and runs read-only diagnostics; it does not close tickets or take destructive action on its own. It removes the boilerplate friction (looking up past tickets, hunting asset records, checking live endpoint or patch state, running the same diagnostic workup, drafting common replies, choosing a category) so human technicians can focus on actual problem-solving.
The prototype that set the architecture
The first cut was deliberately cheap: a scheduled Python job pulled Freshservice analytics exports into a SQLite database on a synced folder each weekday morning, and a Cowork skill opened it read-only and queried it. A few seconds to refresh the full ticket, user, asset, and knowledge-base dataset into a single small file. It was genuinely useful, and it lasted about a week.
Three limits landed at once, and each maps onto a design decision in the platform that replaced it:
- A once-daily file on one person's synced folder cannot answer "what is happening right now," and cannot serve an unattended process at all → a server-hosted Postgres backbone with row-level security, plus a cloud-scheduled feed on GitHub Actions.
- Five flat tables with every column stored as TEXT are fine for counting tickets and wrong for anything relational → a modeled multi-source schema that supports the cross-entity reasoning the triage agent depends on.
- Sync clients do not tolerate SQLite's locking and journaling; the build-locally-then-move workaround was propping up a design that would need concurrent readers and writers anyway → a store designed for concurrent access, and a separate live-API path for genuinely real-time questions, which is why the IT Ops MCP server exists as its own connector rather than another sync job.
The prototype was retired rather than patched. A week of build effort produced the three requirements that defined the real architecture, which beat designing a Postgres backbone against assumptions. The one decision that carried forward unchanged is curated function exposure: a set of named functions, not "let Claude run arbitrary SQL."
Architecture
Scheduled GitHub Actions run a Python feed (stdlib-first; idempotent and resumable; rate-limit aware; secrets out of source) that lands every source system into a unified Supabase Postgres backbone (a multi-table schema with audit columns and row-level security enabled). On top sit two front ends sharing one gather/data layer: the curated Cowork skill set the team calls directly, and an event-driven autonomous agent (a Freshservice webhook into a queue, processed on Azure Functions) that drafts a suggestion as a private staff-only note on each new ticket.
Architecture conventions
- Curated, named functions only Every integration exposes a curated set of named functions (read state, search history, look up an asset, draft a reply, run a themed diagnostic) rather than raw SQL or arbitrary tool access. Prevents prompt-injection or careless requests from producing destructive operations.
- stdlib-first Python Dependencies pulled in only where the standard library does not suffice.
- Idempotent and resumable jobs Every ingestion job can be re-run safely; partial failures resume from where they left off. No "watch-the-script-run-don't-touch-it" operational pattern.
- Rate-limit aware The Freshservice feed splits a frequent incremental ticket sync from a once-daily reference refresh (lookup tables, assets, people, KB) to stay inside the rate budget; every source-system client backs off cleanly when throttled.
- Secrets out of source API keys, certificates, and connection strings live outside the repository, delivered through platform secret stores (GitHub Actions secrets for the feed, Azure Function app settings for the agent). Nothing sensitive is committed.
- Row-Level Security at the database layer, not the application layer.
- Exactly-once scheduled actions A scheduled action (like a step in an offboarding plan) is claimed with an atomic conditional update before it runs, so it executes exactly once even though more than one trigger can see the same due action at the same time.
Safety model
Because the agent can read from and act against production systems, the trust boundaries are explicit:
- Auto-triage is read-and-suggest only. Ticket content is treated as data, never as instructions, so a crafted ticket cannot steer the agent. Diagnostic and action targets come from asset / ScreenConnect data, never from ticket text.
- State-changing actions are gated. Operator commands in a private note run read-only diagnostics on request; a scan, an isolate, a patch deploy, a reboot, or a mailbox/OneDrive access grant only ever queues a preview and requires a second explicit confirmation before anything changes.
- Destructive command execution is never in the autonomous path and is denylist-protected.
- All agent notes are private (never tag or notify the requester) and run through secret-redaction before they are written.
Judgment and guardrails
The agent's core design principle: judgment belongs to the model, guardrails belong to the writes. The LLM's whole-ticket comprehension owns intent, the subject person (who the ticket is about, which may differ from who filed it), and repair-vs-replace judgment, while every safety property lives at the action layer, where it's enforceable. The agent can analyze and recommend anything; it cannot change anything without a human confirm. A deterministic path covers model outages.
A companion charter principle governs its epistemics: skeptical by disposition: verify, don't echo. Every human statement on a ticket, including the assigned technician's stated diagnosis, is a claim to verify, not a fact. Evidence is ranked by reliability: machine telemetry (endpoint read-back, event logs, network and identity state) is ground truth, what people say is a hypothesis, and when telemetry contradicts the account, the agent trusts the telemetry. Skepticism of information, never of the technician's authority: the decision always stays with the human.
Status
In production. The unified Supabase backbone is live with row-level security and full ticket history seeded. The agent runs live on Azure Functions and is conversational: technicians tag it in a private note and talk in plain language; it investigates on its own, asks clarifying questions back, and carries the conversation across notes. That conversational path is routed to a cheap model by default and reserved for the strongest model only on a small set of structural signals, a pending confirmation, an offboarding ticket, an ambiguous machine, read off resolved state rather than guessed from ticket wording; the router fails closed to the strongest model on any error rather than silently downgrading the conversation, and the confirm gate that authorizes destructive actions runs on its own pinned model outside that routing entirely. On each new ticket the agent separately reasons over the whole thing: subject, body, the full email thread, the requester's profile and recent tickets, attached screenshots (vision), live machine and network context (an office-vs-home verdict derived from Cisco Meraki presence), and how similar tickets were resolved before. It then posts a suggestion note that leads with the recommendation and ends with its confidence band and the model that reasoned it. It runs the cheapest capable model first and escalates up a Haiku → Sonnet → Opus cascade only on low confidence, with a prompt-cached reasoning prefix and a deterministic fallback. Token usage, including cache hits versus fresh input, is recorded per model call across both paths, so cost and prompt-cache effectiveness are measured directly rather than inferred from behavior. Confirm-gated actions span named machine fixes, reboots and service restarts, Exchange Online changes (composing the exact PowerShell from a plain-language ask), password/MFA resets, group membership, litigation holds, and scheduled offboarding plans that run at the ticket's effective date, each scheduled step claimed atomically so it executes exactly once. A verify loop re-checks whether a fix actually held. A note-driven training loop lets technicians coach the agent from inside the ticket, and the agent can file GitHub issues on itself and propose documentation changes as human-reviewed pull requests. Pushes to main auto-deploy the function.
What this demonstrates
- Building AI-augmentation infrastructure for operational teams rather than just consuming hosted AI products. The platform is the integration layer between the team's source systems and Claude (or any future LLM), not "we use a chatbot to help with tickets."
- Shipping an autonomous agent into production with a conservative safety model Read-and-suggest by default, read-only diagnostics autonomously, state-changing actions confirm-gated and never sourced from ticket text.
- Curated-function exposure rather than raw access, plus an explicit confirm gate for state-changing actions, is the right pattern for AI agents that touch production systems and reflects mature thinking about LLM safety.
- Judgment-led agent design Letting the model's comprehension drive routing while keeping every guardrail at the write layer (analysis unconstrained, actions confirm-gated) instead of subordinating the LLM to brittle intent scripts.
- Multimodal and cost-aware model use Vision for screenshot triage, a prompt-cached reasoning prefix, and a cheap-first / escalate-on-low-confidence model cascade so spend tracks the hard cases, with the model and confidence band named on every note for transparency.
- Multi-source unification with audit and RLS at the database layer is the right shape for an IT data store that crosses ITSM, identity / MDM, remote support, network, endpoint security, and patch management.
- A feedback loop that improves the agent in place Technicians coach it from inside the ticket, and that feedback is captured and applied rather than lost in chat.