Category: Thought Leadership

  • Coordinated Omission: Why Your p99 Latency Is Lying

    Your p99 latency is one of the most-quoted numbers in your observability stack, and if you measured it with a standard load test, it is very likely a lie — not off by a little, but understating the truth by a factor of hundreds. The tool didn’t malfunction. It measured exactly what it was designed to measure, which turns out to be the wrong thing the moment your system stalls.

    This is coordinated omission, and once you see it you can’t unsee it in a benchmark result again.

    The load generator stalls with the server

    Most load tests are closed-loop: a fixed pool of workers each send a request, wait for the response, then send the next. It’s simple and it’s how most benchmarking harnesses work by default. It also has a fatal blind spot.

    When the server freezes for 200 ms — a GC pause, a lock, a failover, a noisy neighbor — the workers waiting on it freeze too. During that stall they send nothing. So the requests that should have been issued during those 200 ms are never sent, never timed, and never counted. And those are exactly the requests that would have been slow. The test quietly omits its own worst samples and hands you a beautiful percentile for a system that, for a fifth of a second, was serving no one. Your users experienced that stall. Your benchmark didn’t.

    The same stall, measured two ways

    Here is one 200 ms freeze in an otherwise sub-millisecond service, at 1,000 requests/second. The only difference between the two columns is when the clock starts: the naive test times each request from when it was actually sent; the corrected version times it from when it was scheduled to be sent — the honest question, because a request that couldn’t even be issued was already failing the user.

    percentile   naive (ms)   corrected (ms)
          p50          0.2              0.2
          p90          0.2              0.2
          p99          0.2            120.0
        p99.9          0.2            192.0
    Bar chart comparing naive vs schedule-corrected latency percentiles: the naive load test reports p99 of 0.2 ms, while the corrected p99 is 120 ms and p99.9 is 192 ms — the tail latency coordinated omission hides.

    Same service, same stall, same run. The naive p99 reports 0.2 ms. The honest p99 is 120 ms — a 600× understatement. The average is fine in both; the deception lives entirely in the tail, which is precisely the part you’re quoting p99 to protect.

    The correction itself is one line of intent: measure latency from the schedule, not the send.

    scheduled    = i * interval           # when this request SHOULD have gone out
    actual_start = max(scheduled, clock)  # closed loop: can't send until a worker is free
    
    naive.append(finish - actual_start)   # what a naive test records
    corrected.append(finish - scheduled)  # honest: the user was waiting since 'scheduled'

    The fix isn’t a formula — it’s a different test

    You can correct after the fact, but the better fix is to stop omitting in the first place. Drive load open-loop: issue requests at a constant target rate regardless of whether prior ones have returned, so a stall produces a visible backlog instead of an invisible gap. Practically:

    • Use a constant-throughput generator. wrk2, not wrk; fio with a fixed rate, not an unbounded queue depth. They hold the send schedule even when the system underneath them stumbles.
    • Record into an HdrHistogram. Full latency resolution across the whole range, plus built-in coordinated-omission correction — you get honest high percentiles instead of a rounded-off tail.
    • Report the tail, and report the load you held. “p99.9 at a sustained 1,000 rps” is a claim. “p99” with no throughput attached is a vibe.

    Why this is a leadership problem, not a testing footnote

    Capacity plans, SLOs, and autoscaling thresholds get set from these numbers. A p99 that’s silently 600× optimistic doesn’t just embarrass you in an incident review — it sizes your cluster, writes your error budget, and sets the alarm that was supposed to wake someone before the customer noticed. You built a safety margin on a measurement that deletes its own worst cases.

    This is the whole discipline of the Benchmark Files in one number: a claim without an honest benchmark is just an opinion, and a benchmark that omits its worst samples is an opinion wearing a lab coat. Treat every latency figure as a conviction with a review date — stated strongly enough to plan against, and re-run honestly the moment the load, the runtime, or the topology changes.

    The takeaway

    Before you quote a p99, ask one question: did the test keep sending while the system was stalling? If it was closed-loop and the answer is no, the number is describing a system that never had a bad moment — which is not the system you run. The average will always survive a benchmark. The tail only survives an honest one.

    The coordinated-omission demo — naive vs schedule-corrected percentiles — is runnable on GitHub: github.com/waghmaredb/vexpose-labs. Benchmark latency for a living and fighting the same tail? Compare methods on LinkedIn or X.

  • Measuring Your AI Exit Cost: An Architecture Review

    On LinkedIn, I make the executive argument: your AI problem is not cost, it is dependency. Cost is what you pay this quarter; dependency is what you pay at every renewal, for a decade. This post is the engineering companion. If dependency is the real exposure, it should be measurable — and anything measurable can be architected down.

    So: how do you actually measure your AI exit cost? Not as a slide. As an audit.

    Exit cost is a stack, not a number

    Teams tend to equate switching providers with changing an API key. That was roughly true in 2023. In an agentic estate, provider coupling has accumulated in five distinct layers, and each has its own unwind cost.

    Layer 1 — API surface

    The cheapest layer to fix and the first to audit. If application code calls a provider SDK directly, every model reference is a migration line-item. The fix is a gateway: one internal endpoint, provider adapters behind it, model names resolved by policy rather than hard-coded. If you cannot change your default model in one config file, start here — nothing else in this audit is testable until you can.

    Layer 2 — Prompt and behavior coupling

    Prompts are tuned, consciously or not, to one model’s quirks: its instruction-following style, its formatting habits, its tool-calling dialect. This coupling is invisible until you swap models and watch pass rates drop. The countermeasure is an evaluation suite that defines correct behavior independently of any provider — golden datasets, structured-output checks, task-level assertions. Your eval suite is your portability contract. No evals, no measurable exit cost; only guesses.

    Layer 3 — Data gravity

    The heaviest layer. Embeddings are tied to the model that produced them: switch embedding models and every vector in the store must be regenerated — a compute bill and a re-indexing window that scales with your corpus. Fine-tunes are worse: weights trained on a proprietary base do not travel at all. Rule of thumb: prefer retrieval over fine-tuning wherever quality allows, and record the full re-embedding cost of your corpus as a standing line in the exit ledger. If you do not know that number today, that is finding #1 of your audit.

    Layer 4 — Orchestration and agent coupling

    Agent frameworks encode provider assumptions: tool-schema formats, context-window sizes, retry semantics, reasoning-token behavior. A workflow that plans, retrieves, calls tools, and self-corrects has ten to twenty coupling points where a chat app had one. Keep tool definitions in a neutral schema and translate at the gateway; treat any framework feature that only works on one provider as a loan you will repay at migration time.

    Layer 5 — Economic architecture

    Routing is where dependency becomes money. The arithmetic is one-sided even at public list prices: frontier models cost on the order of ten times more per token than the small and open-weight models that handle classification, extraction, and summarization perfectly well. Send every request to a frontier model and you pay the premium rate for work a cheaper tier would do identically — so a tiered blend lands at a fraction of a frontier-everything bill, with the exact multiple set by your own traffic mix. Measure yours; don’t borrow someone else’s number. But the deeper value of a routing tier is optionality: once classification, extraction, and summarization run on interchangeable smaller models — including open-weight ones you could self-host — the share of your workload that is truly captive shrinks to the frontier-only remainder. Price your renewal leverage as: the percentage of token volume that could move within 30 days without eval regression.

    The failover drill

    An exit cost you have never tested is a fiction. The drill is simple and quarterly: repoint the gateway’s default policy at your designated fallback (a second provider or an open-weight deployment), run the full eval suite, and record three numbers — engineering hours spent, eval pass-rate delta, and the workload percentage that moved cleanly.

    # gateway routing policy — failover drill
    default_tier:
      primary:   frontier-a
      fallback:  open-weight-70b   # drill target
    routes:
      classify|extract|summarize:  small-tier
      agentic|complex-reasoning:   default_tier
    drill:
      cadence: quarterly
      pass_criteria: eval_delta <= 2%
      record: [eng_hours, eval_delta, pct_moved]

    Those three numbers, tracked over time, are your exit cost — not an estimate, a measurement. In my experience the first drill is always humbling: the gap between the architecture diagram and the actual coupling is where the real dependency lives.

    The point is not to leave

    None of this is a plan to abandon your provider. It is the opposite of a plan: it is an option. An option you hold changes a renewal negotiation even if you never exercise it. An option you lack is a price you will accept.

    Rent intelligence if you choose to. Measure the exit before it is load-bearing. And never rent the operating model.

  • A Reference Architecture for Enterprise RAG (That Won’t Hallucinate Your Docs)

    Every RAG demo works. You point a model at your docs, ask a question, get a fluent answer with a citation, and the room nods. Then it ships, someone asks about a policy that changed last quarter — and the system confidently cites the old version. The demo never showed you that failure mode, because the failure mode isn’t “the model can’t answer.” It’s “the model answers anyway.”

    That gap is where enterprise RAG loses trust — and it’s almost never the model’s fault. It’s retrieval handing over plausible-but-wrong context that a capable model then synthesizes beautifully. The tool is not the transformation: a bigger model makes a confident wrong answer more convincing, not less. The fix lives below the model, in retrieval and grounding. Here’s the architecture I’d stand up for RAG you can actually put in front of employees or customers.

    The failure mode: fluent, cited, and wrong

    Naive RAG is three lines: embed the query, pull the top-k nearest chunks, stuff them in the prompt. It fails three ways at once. Vector search alone misses the exact terms — part numbers, error codes, policy names — that keyword search would nail. Without reranking, “near in embedding space” isn’t “actually answers the question.” And with no relevance floor, the pipeline always returns something, so the model always has context to rationalize an answer from — even when the right answer is “we don’t have that.”

    The result is the worst kind of wrong: confident, well-written, and cited. Everything below exists to make the system able to say “I don’t have that in the sources.”

    The reference architecture

      Query
         |
         v
      Hybrid Retrieval      BM25 (keyword) + vector
         |
         v
      Rerank                cross-encoder scores the union
         |
         v
      Relevance floor  --- nothing relevant --->  Refuse ("not in the sources")
         |
      context clears floor
         |
         v
      Grounded generation   answer ONLY from context, cite every claim
         |
         v
      Answer + citations
         |
         v
      Eval + observability  groundedness + citation checks  --->  tune retrieval

    1. Ingestion & chunking. How you split documents is a retrieval decision, not a preprocessing chore. Chunk too big and the reranker drowns in noise; too small and you sever the context a claim needs. Preserve structure (headings, tables) and keep the source metadata — you’ll need the doc_id for citations.

    2. Hybrid retrieval. Run keyword (BM25) and vector search and take the union. Keyword catches the exact strings vectors miss; vectors catch the meaning keywords miss. Neither alone is enough in the enterprise, where half the questions hinge on a specific identifier.

    3. Rerank. Score the union with a cross-encoder that reads the query and each chunk together. This is the highest-ROI line most teams aren’t running — it reorders “nearby” into “actually relevant.”

    4. The grounding gate (relevance floor). The seam. If nothing clears a relevance threshold, return nothing. This is where your operating DNA — what the system is allowed to answer, and from what — stops being a hopeful prompt and becomes code. An empty retrieval result is a feature: it’s what lets the generator refuse instead of improvise.

    5. Grounded generation. Instruct the model to answer only from the provided context and cite a doc_id for every claim. With no context, it refuses — by construction, not by good behavior.

    6. Eval & observability. Score every answer for groundedness and citation validity, continuously. Without this you can’t tell an improvement from a regression — you’re just guessing with a chatbot.

    The grounding gate, in code

    Hybrid recall, rerank, and a relevance floor — the floor is the part that makes refusal possible:

    def retrieve(self, query, k=5):
        # Hybrid recall: keyword catches exact terms, vectors catch meaning.
        candidates = self._dedupe(
            self.bm25.search(query, k=20) + self.vector_index.search(query, k=20)
        )
        # Rerank the union with a cross-encoder — the single biggest quality win.
        for c in candidates:
            c.score = self.reranker.score(query, c.text)
        candidates.sort(key=lambda c: c.score, reverse=True)
        # Relevance floor: if nothing is truly relevant, return nothing.
        # An empty result is a feature — it lets the generator refuse.
        return [c for c in candidates[:k] if c.score >= self.min_score]

    And the grounding instruction that turns “no context” into “no answer”:

    GROUNDING_INSTRUCTION = (
        "Answer ONLY from the provided context. Cite the doc_id for every claim. "
        "If the context does not contain the answer, say 'I don't have that in the "
        "sources' — do not use prior knowledge."
    )

    Measuring whether it’s actually grounded

    Two numbers predict trust: is every claim supported by a retrieved chunk, and does every cited doc_id actually appear in the context? Score them on a fixed set of questions on every prompt, model, or index change.

    def score(case):
        groundedness = case.claims_supported / case.claims_total
        hallucinated = [d for d in case.cited_doc_ids if d not in case.context_doc_ids]
        return {"groundedness": groundedness, "citation_valid": not hallucinated}

    The guardrail everyone forgets: retrieval-time access control

    Every architecture above assumes one reader is allowed to see every document. In a real enterprise they are not. HR files, board decks, a customer’s data walled off from another customer’s — the same index holds chunks that different users have different rights to. Miss this and you haven’t built a helpful assistant; you’ve built the most efficient data-exfiltration path in the company. The fastest way to leak the salary spreadsheet is to embed it and let anyone ask.

    The critical design decision: access control lives in retrieval, not in the prompt. Filter the candidate set to what this caller is entitled to see before anything is reranked or shown to the model — sourced from the same entitlements as the system of record (row-level security, document ACLs), not re-invented in the pipeline.

    def retrieve(self, query, user, k=5):
        candidates = self._dedupe(
            self.bm25.search(query, k=40) + self.vector_index.search(query, k=40)
        )
        # Trim to what THIS user may see — enforced at the data layer, BEFORE rerank,
        # so unauthorized chunks never influence ranking or reach the model at all.
        candidates = [c for c in candidates if can_read(user, c.doc_id)]
    
        for c in candidates:
            c.score = self.reranker.score(query, c.text)
        candidates.sort(key=lambda c: c.score, reverse=True)
        return [c for c in candidates[:k] if c.score >= self.min_score]

    A system prompt that says “only answer if the user is authorized” is not a security control — it’s a polite request to a probabilistic system, and it will eventually say yes. Real access control is a hard filter on the retrieval set, applied before rerank so an unauthorized chunk can’t even tilt the ranking. This is the RAG expression of least privilege — the same operating DNA that governs a good agent: the model may ground only on what this specific user could already open for themselves. That’s a decision you enforce in code, not one you hope the model respects.

    The parts everyone underestimates

    Chunking is a retrieval decision. More RAG quality is won or lost here than in model choice. Test chunk sizes against your eval set like any other parameter.

    Reranking is the cheap win you’re skipping. A cross-encoder over the top ~40 candidates typically moves groundedness more than a model upgrade — for a fraction of the cost.

    The relevance floor is the whole ballgame. “Always answers” is the root of hallucinated RAG. A threshold that returns nothing when nothing is relevant is what converts a plausible liar into a trustworthy assistant.

    Retrieval decays silently. A doc gets superseded, and the old chunk still ranks — confidently. Freshness and provenance on your index matter more than the model you pick.

    The takeaway

    RAG trustworthiness is a retrieval-and-grounding problem, not a model problem. What the system is allowed to answer — and from what — is operating DNA, and it belongs in code at the grounding gate, not in a prompt you hope the model respects. Get that seam right and you can swap models freely as they improve. Skip it and you’ve built a very persuasive way to cite the wrong document.

    The retriever and the groundedness eval from this post are runnable on GitHub: github.com/waghmaredb/vexpose-labs. Building enterprise RAG and fighting the same failure modes? I’d like to compare notes — reach me on LinkedIn or X.

  • The ServiceNow REST pagination gotcha that silently drops records

    ServiceNow’s REST API uses offset-based pagination. When you paginate through a live table—one where records are being created or deleted—the offset becomes stale between calls. Records silently get dropped.

    Why it happens

    The offset is positional within the current result set at the exact moment you query. If a new incident is created and sorts before your current page, all subsequent offsets shift down—you skip records.

    Example: You’re processing incidents with offset=0, limit=100, then offset=100, limit=100. If a new incident arrives and sorts into position 50, the second page now contains records 151–250 instead of 100–200. Record 100 is never seen.

    The fix: use sys_id as a cursor

    Instead of offset, anchor pagination to sys_id. Since sys_id is immutable, new records don’t invalidate your position—they sort after your cursor.

    # DON'T: offset drifts
    offset = 0
    while True:
        records = api.query(offset=offset, limit=100)
        process(records)
        offset += 100  # Invalid if records were added
    
    # DO: stable cursor
    last_sys_id = ""
    while True:
        query = f"sys_id>{last_sys_id}^ORDERBYsys_id" if last_sys_id else "ORDERBYsys_id"
        records = api.query(query, limit=100)
        process(records)
        if records:
            last_sys_id = records[-1]["sys_id"]  # Advance cursor
    

    See the full before-and-after in github.com/waghmaredb/vexpose-labs.

  • vExpose is back — and it’s changing

    A quick, honest note: vExpose went quiet for a while. I’m back — and the blog is changing in a way I’m genuinely excited about.

    Here’s the shift. Most technical writing shows you the strategy but hides the wiring. Going forward, vExpose does both: I state the call a technology leader has to make, then prove it in code, architecture, and benchmarks. One conviction runs through all of it — AI fails in the operating model, not the model. The tool is not the transformation.

    Two things to start with:

    • A new flagship piece, A Reference Architecture for Agentic ITSM, on safely letting a GenAI agent act in ServiceNow — with the guardrail code that makes it work.
    • A new Start Here page that organizes the best of the blog by topic, so you can jump straight to what’s useful.

    What’s coming: reference architectures, build logs with real code, and benchmarks — on a steady cadence this time. No noise, just the work. If there’s a topic you want me to dig into, just reply — I read everything.

    — Deepak

  • A Reference Architecture for Agentic ITSM: Wiring GenAI into ServiceNow Without Losing Control

    A year ago I wrote about integrating generative AI into ITSM workflows. Back then, “integration” mostly meant a chatbot that summarized tickets and drafted responses. Useful, but bounded — the model suggested, a human acted.

    That line has moved. The interesting question in 2026 isn’t whether GenAI can read a ticket; it’s whether you can safely let it close one. The moment a model can take actions in your system of record — reassign an incident, run a diagnostic, restart a service, request a change — you’ve crossed from “assistant” into “agent.” And agents in production are an infrastructure and governance problem long before they’re a model problem. This is the clearest case I know of a principle I keep coming back to: AI fails in the operating model, not the model.

    This post is the reference architecture I’d stand up to do agentic ITSM properly: enough autonomy to actually save time, enough control that I’d let it near a production ServiceNow instance. I’ll show the components, the wiring, the code that matters, and — more importantly — the parts everyone underestimates.

    The failure mode we’re designing against

    Most agentic pilots fail the same way. Someone connects a capable model directly to an API with broad credentials, it works beautifully in the demo, and then in week three it confidently takes a wrong action on a real incident because a retrieved document was stale and nothing stopped it. The problem was never the model’s intelligence. It was that the architecture had no seam between “the model decided” and “the system did.”

    Which is the whole point in one line: the tool is not the transformation. A more capable model doesn’t close this gap — it just takes the wrong action more fluently. Everything below exists to create that seam.

    The reference architecture

    Five layers, each with a single responsibility. The discipline is keeping them separate — that separation is what makes the system auditable, testable, and safe to evolve.

      Trigger  (new/updated incident, chat, webhook)
         |
         v
      Retrieval Layer      RAG over KB, CMDB, past incidents
         |
         v
      Orchestration Layer  agent loop + tool-calling
         |
         v
      Policy & Guardrail Gate  --- restricted / low confidence --->  Human-in-the-loop
         |                                                            (approval queue)
      allowed + high confidence                                            |
         |                                                              approved
         v  <-------------------------------------------------------------+
      Action Layer  (scoped ServiceNow API)
         |
         v
      Observability  (traces, evals, audit log)  --->  back to Trigger

    1. Retrieval Layer. The agent is only as good as what it knows about this incident. Ground every decision in your own data: the knowledge base, the CMDB (what is this CI, what depends on it), and — the highest-signal source most teams ignore — the resolution history of similar past incidents. Retrieval quality, not model size, is usually the difference between a useful agent and a plausible-sounding one.

    2. Orchestration Layer. The agent loop: given the incident and retrieved context, decide the next step, optionally call a tool, observe the result, repeat until resolved or escalated. This is where model-provider choice lives — and it should be swappable, not hardcoded.

    3. Policy & Guardrail Gate. The seam. Every proposed action passes through a deterministic policy check before it can touch ServiceNow. This layer is plain code, not a model — because “what is this agent allowed to do” is a decision you must be able to read, test, and audit, not one you delegate to a probability distribution. This is where your operating DNA — how decisions get made and who is trusted to act — stops being culture and becomes code.

    4. Action Layer. A narrow, scoped adapter to ServiceNow. It exposes only the specific operations the agent is permitted to perform, each backed by a least-privilege service account. The agent never gets your admin token.

    5. Observability. Full traces of every decision, tool call, and action; an immutable audit log; and continuous evaluation. If you can’t answer “why did the agent do that, and was it right,” you can’t run this in production.

    The seam, in code

    Here’s the heart of it: the orchestration layer proposes an action, and the guardrail gate decides whether it executes, gets downgraded to a human approval, or is refused. This is deliberately boring, deterministic code — that’s the point.

    from dataclasses import dataclass
    from enum import Enum
    
    class Decision(Enum):
        ALLOW = "allow"
        REQUIRE_APPROVAL = "require_approval"
        DENY = "deny"
    
    @dataclass
    class ProposedAction:
        name: str            # e.g. "restart_service", "reassign", "add_work_note"
        target_ci: str       # configuration item the action touches
        params: dict
        confidence: float    # model-reported, treated as a hint, never as truth
    
    # Policy is data you can read, diff in git, and audit — not model output.
    POLICY = {
        "add_work_note":   {"max_blast_radius": "none",   "min_confidence": 0.0},
        "reassign":        {"max_blast_radius": "ticket", "min_confidence": 0.75},
        "restart_service": {"max_blast_radius": "service","min_confidence": 0.90,
                            "require_approval_for": ["prod"]},
        # default-deny: anything not listed cannot run
    }
    
    def evaluate(action: ProposedAction, ci_env: str) -> Decision:
        rule = POLICY.get(action.name)
        if rule is None:
            return Decision.DENY                      # default deny — the safe default
        if action.confidence < rule["min_confidence"]:
            return Decision.REQUIRE_APPROVAL
        if ci_env in rule.get("require_approval_for", []):
            return Decision.REQUIRE_APPROVAL
        return Decision.ALLOW

    Three design choices are doing all the work here:

    • Default deny. An action the policy doesn’t explicitly know about cannot run. New capabilities are added deliberately, in a reviewed change to the policy, not discovered by the model at runtime.
    • Confidence is a hint, never a gate on its own. A model saying it’s 0.99 sure is not evidence. Confidence lowers autonomy when low; it never grants autonomy on its own. High-blast-radius actions in prod route to a human regardless.
    • Blast radius is explicit. Every action is classified by what it can break. That classification, not the model’s eloquence, determines how much oversight it gets.

    The action layer itself stays thin and scoped:

    import os, requests
    
    class ServiceNowAdapter:
        """Exposes ONLY whitelisted operations, backed by a least-privilege account."""
        def __init__(self):
            self.base = os.environ["SN_INSTANCE_URL"]
            self.auth = (os.environ["SN_AGENT_USER"], os.environ["SN_AGENT_TOKEN"])
    
        def add_work_note(self, sys_id: str, note: str):
            return self._patch(sys_id, {"work_notes": note})
    
        def reassign(self, sys_id: str, group: str):
            return self._patch(sys_id, {"assignment_group": group})
    
        def _patch(self, sys_id: str, body: dict):
            r = requests.patch(f"{self.base}/api/now/table/incident/{sys_id}",
                               json=body, auth=self.auth, timeout=10)
            r.raise_for_status()
            return r.json()

    Notice what’s not here: there’s no generic “run arbitrary query” or “execute” method. The agent’s power is defined by the surface area of this class, and that surface area is a design decision you make on purpose.

    Deploy it like infrastructure, not like a notebook

    The fastest way to lose trust in an AI system is to run it as a hand-configured process someone SSHed in to start. Treat the whole stack as infrastructure-as-code from day one — reproducible, version-controlled, and reviewable. The service account, its exact permissions, the policy file, and the deployment all live in git:

    # ansible: provision the least-privilege ServiceNow integration account
    - name: Ensure agent service account has ONLY scoped roles
      vars:
        agent_roles:
          - itil            # read/update incidents
          - snc_read_only   # broad read, no write
      ansible.builtin.uri:
        url: "{{ sn_instance }}/api/now/table/sys_user_has_role"
        method: POST
        user: "{{ sn_admin_user }}"
        password: "{{ sn_admin_pass }}"
        body_format: json
        body:
          user: "{{ agent_sys_id }}"
          role: "{{ item }}"
      loop: "{{ agent_roles }}"
      # No admin, no security_admin. If the agent needs more, that's a reviewed PR.

    The point isn’t Ansible specifically — it’s that “what can this agent touch” should be a diff someone approved, not tribal knowledge.

    The parts everyone underestimates

    Evaluation is the hard part, not orchestration. Wiring an agent loop is a weekend. Knowing whether it’s getting better or worse over time is the real engineering. Build a regression set of real (anonymized) incidents with known-good resolutions and run it on every prompt or model change. Without this, every “improvement” is a guess.

    Retrieval decay will hurt you silently. A KB article gets superseded, a CMDB relationship changes, and the agent starts grounding decisions in stale truth — confidently. Freshness and provenance on retrieved context matter more than the model you pick.

    Cost is an architecture decision. Running a frontier model on every ticket update is how pilots die in the budget review. Route by difficulty: cheap/local models for triage and summarization, the expensive model only for genuinely ambiguous cases. Inference routing belongs in the design, not the invoice.

    Human-in-the-loop is a feature, not a fallback. The approval queue isn’t the system admitting defeat — it’s the mechanism that lets you start with tight autonomy and earn more as the eval data proves the agent is trustworthy for a given action class. Every autonomy level you grant is a conviction with a review date: stated strongly enough to act on, dated explicitly enough that your eval data is allowed to revise it.

    How I’d actually roll this out

    1. Shadow mode first. The agent proposes actions and writes them to work notes, but executes nothing. You get real eval data at zero risk.
    2. Graduate low-blast-radius actions. Let it add work notes and reassign autonomously once shadow-mode accuracy clears your bar.
    3. Keep high-blast-radius actions human-gated — indefinitely, if that’s what the risk math says. But don’t hide behind the gate forever, either: shadow mode with no graduation path is its own failure mode — professional neutrality, saying a great deal while committing to nothing. The discipline is interpretive courage: reading the eval data everyone can see and making the call to graduate an action when it has earned it.
    4. Instrument everything from step one. You cannot retrofit observability onto an incident you can’t explain.

    The takeaway

    Agentic ITSM is not a model you buy; it’s a system you architect — because, again, AI fails in the operating model, not the model. The model is the least differentiated part — it’s swappable, and it’s improving whether you do anything or not. Your durable advantage is the seam: the deterministic, auditable, least-privilege layer between what the agent decides and what your systems actually do. Get that right and you can adopt every model improvement safely for years. Skip it and you’ve built a very expensive way to take the wrong action quickly.

    Flexibility with guardrails is the real competitive advantage in enterprise AI. Is your architecture ready to let an agent act — or only to let it talk?

    The policy-gate, ServiceNow-adapter, and provisioning code from this post are runnable on GitHub: github.com/waghmaredb/vexpose-labs. If you’re building agentic workflows on your ITSM stack, I’d like to compare notes — reach me on LinkedIn or X.

  • The Evolution of IT Infrastructure: Why Disaggregated Platforms Are the Future for GenAI

    The Evolution of IT Infrastructure: Why Disaggregated Platforms Are the Future for GenAI

    Over the past two decades, enterprise IT architecture has undergone a dramatic transformation. What began as siloed, three-tier environments has evolved through converged and hyper-converged infrastructure (HCI) to today’s cutting-edge disaggregated platforms. This journey is more than a story of hardware innovation-it’s the foundation for the next era of data-driven business, especially as generative AI (GenAI) workloads reshape the technology landscape.

    From Three-Tier to Hyper-Converged: The Drive for Simplicity

    Three-tier architecture-with separate compute, storage, and networking-offered flexibility, but at the cost of complexity and inefficiency. IT teams often faced overprovisioning, stranded resources, and operational headaches.

    The arrival of converged infrastructure bundled these elements into pre-validated stacks, simplifying procurement and deployment. Yet, the fundamental silos remained, limiting agility and resource utilization.

    Hyper-converged infrastructure (HCI) took consolidation further by merging compute and storage into modular nodes managed by software. According to Fortune Business Insights, the global HCI market is projected to reach $65 billion by 2029, reflecting enterprises’ desire for simplified management and scalability. However, HCI’s tightly coupled design makes it difficult to scale compute and storage independently-a critical limitation for today’s AI and data-intensive workloads.

    The Rise of Disaggregated Platforms: Built for GenAI

    Enter disaggregated infrastructure: an architecture that decouples compute, storage, and networking into independent resource pools. This approach is rapidly gaining traction, with the composable/disaggregated infrastructure market expected to triple to over $19 billion by 2029 (MarketsandMarkets).

    Why Disaggregation Matters-Especially for GenAI

    1. Independent Scaling GenAI workloads are unpredictable and data-hungry. Disaggregated platforms allow organizations to scale storage for massive datasets or add GPU-rich compute nodes as needed-without unnecessary overprovisioning.
    2. Superior Resource Utilization Dell reports that disaggregated architectures can deliver “orders of magnitude higher” core utilization and reduce server and software licensing costs by up to 50%.
    3. Performance for AI Technologies like NVMe-over-Fabrics and CXL memory pooling enable low-latency, high-throughput access to data-keeping GPUs saturated and AI pipelines moving at full speed.
    4. Hybrid and Edge Flexibility Disaggregated storage and compute pools can be orchestrated across on-premises, cloud, and edge environments, supporting the distributed nature of modern AI applications.

    Real-World Impact

    Industry leaders are moving fast:

    • Dell’s PowerEdge servers offer modular, independently scalable resources.
    • Many vendors/startups are rapidly evolving their portfolio to embrace disaggregation, making it the new default for enterprises scaling GenAI and other advanced workloads.
    • Edge AI is now viable, with lightweight models running inference on disaggregated clusters far from the data center.

    As Travis Vigil Vigil, SVP at Dell, puts it: Disaggregation isn’t just about hardware-it’s about rethinking the entire data center ecosystem to unlock performance and control.

    Looking Ahead

    The shift to disaggregated infrastructure is not just a technological trend-it’s a strategic imperative for organizations embracing GenAI and next-generation workloads. By enabling independent scaling, maximizing resource efficiency, and supporting hybrid deployment models, disaggregated platforms are redefining what’s possible in the data center.

    In the age of AI, flexibility is the ultimate competitive advantage. Is your infrastructure ready?

    Let’s connect and discuss how your organization can leverage disaggregated architectures to accelerate GenAI and future-proof your digital transformation.

  • GenAI as a Replacement for Enterprise Search: Revolution or Evolution?

    GenAI as a Replacement for Enterprise Search: Revolution or Evolution?

    Enterprise search has long been the backbone of organizational knowledge discovery, enabling employees to sift through vast troves of internal data-emails, documents, reports, and more. Yet, as information volume and complexity have exploded, traditional search systems have struggled to keep pace. Enter Generative AI (GenAI): a transformative technology that promises not just incremental improvements, but a fundamental reimagining of how enterprises access and leverage information.

    The Traditional Enterprise Search Challenge

    Legacy enterprise search platforms typically rely on keyword-matching and index-based algorithms. While effective for straightforward queries, these systems falter when faced with:

    • Ambiguous or complex natural language queries
    • Unstructured or poorly tagged data
    • Industry-specific jargon and synonyms
    • The need for contextual, role-based, or personalized results
    • Multi-language demands and adaptive access controls

    The result? Knowledge workers spend an estimated 2.5 to 3.6 hours per day searching for information, leading to frustration and lost productivity.

    How GenAI Transforms Enterprise Search

    GenAI, powered by large language models (LLMs) and advanced natural language processing (NLP), addresses these pain points in several critical ways:

    1. Deep Contextual Understanding and Intent Detection

    Unlike traditional search, GenAI interprets the context and underlying intent behind user queries. Whether an employee asks, “How do I reset my password?” or “I forgot my password, can you help?”-GenAI recognizes the shared intent and delivers precise, relevant answers. This semantic understanding extends to complex, multi-step, or follow-up queries, enabling conversational and intuitive information retrieval.

    2. Personalization and Adaptive Learning

    GenAI-powered search systems learn from user profiles, search histories, and real-time interactions, tailoring results to individual roles and preferences. Over time, this leads to hyper-personalized experiences, with the system anticipating user needs and improving with every interaction.

    3. Mastery of Unstructured and Multilingual Data

    GenAI excels at parsing unstructured data-emails, PDFs, images-transforming them into structured, searchable formats. It also supports multilingual and cross-domain queries, making it invaluable for global enterprises with diverse data sources.

    4. Automation and Decision Support

    Beyond search, GenAI can automate clerical tasks-summarizing documents, drafting emails, compiling reports-freeing employees to focus on higher-value work. It can synthesize insights across multiple sources, supporting faster and more informed decision-making.

    5. Enhanced Security and Compliance

    Modern GenAI enterprise search solutions integrate robust access controls and compliance features, ensuring sensitive information is only accessible to authorized users. AI can also detect anomalies and potential security threats by analyzing usage patterns.

    Real-World Impact

    Leading organizations are already leveraging GenAI to revolutionize internal knowledge centers, streamline customer service, and optimize business processes. The result is a measurable boost in productivity, faster decision cycles, and significant cost savings.

    Challenges and Considerations

    Despite its promise, GenAI is not a panacea. Key challenges include:

    • Data Quality and Integration: Success depends on clean, well-organized data and seamless integration with existing systems.
    • Hallucination Risk: LLMs may generate plausible but incorrect answers if not properly constrained by retrieval-augmented generation (RAG) or domain-specific fine-tuning.
    • Security and Governance: Ensuring robust access controls and preventing prompt-based security bypasses remain ongoing concerns.
    • Infrastructure Readiness: Many enterprises lack the foundational data infrastructure and skilled workforce needed for successful GenAI deployment.

    Is GenAI a Replacement or an Evolution?

    While GenAI dramatically elevates enterprise search, it is best viewed not as a wholesale replacement, but as an evolutionary leap. GenAI augments and, in many cases, supersedes traditional search by delivering context-aware, conversational, and highly relevant results. However, its success hinges on thoughtful implementation, robust data governance, and continuous improvement.

    “GenAI is no magic bullet, but applied in the right places it has potential to improve enterprise search… As the technology evolves, its ability to further enhance search capabilities will grow, offering even more refined solutions to complex search challenges.”

    The Future: From Search to Knowledge Discovery

    As GenAI matures, the line between search and intelligent knowledge discovery will blur. Enterprises that invest in GenAI-powered search today are not just making information easier to find-they are laying the groundwork for a future where knowledge is proactively surfaced, synthesized, and delivered in context, driving innovation and competitive advantage.

    In summary: GenAI is not just a better search engine-it is the catalyst for a new era of enterprise intelligence. For organizations ready to embrace this shift, the rewards are substantial: more empowered employees, faster insights, and a culture of data-driven excellence.

  • Exploring the Strengths and Trade-offs of Fine-tuning and RAG in Language Models

    In the ever-evolving landscape of artificial intelligence, the incorporation of domain-specific knowledge into language models (LLMs) is not just a lofty goal—it’s a mission-critical aspect of model performance. This is where fine-tuning and Retriever-Reader (RAG) come into the picture, two powerful approaches with distinct methodologies for imbuing models with domain-specific prowess. As the Director of AI Research at a tech startup, investing in the right approach to empower language models with knowledge is a debate that rages on in our weekly strategy meetings. In this piece, I dissect the benefits and trade-offs of both methods, aiming to help data scientists, AI enthusiasts, and tech professionals make informed decisions regarding the enhancement of LLMs.

    Introduction

    The modern data scientist wields the power to curate a model’s understanding to an unprecedented degree. Incorporating domain knowledge has become less of an afterthought and more of the central piece to the puzzle of AI applications. As established models like GPT-3 demonstrate extraordinary capabilities, the question of specialized knowledge arises. Both fine-tuning and RAG have stepped forward as capable candidates for augmenting language models, offering different paths to the same destination.

    Fine-tuning: Leveraging Existing Models

    Fine-tuning involves starting with a pre-trained model and updating its weights using labeled examples from within the target domain. The rationale is simple: rather than reinventing the wheel, one can build upon the wealth of knowledge already stored within established models.

    https://sebastianraschka.com/images/blog/2023/llm-finetuning-llama-adapter/classic-flowchart.png

    The Fine-tuning Approach in Depth

    Fine-tuning has gained popularity due to its relatively lower resource consumption compared to training from scratch. Pre-trained models come with an inherent understanding of language and are adept at various natural language processing (NLP) tasks. By fine-tuning these models, often with a smaller, domain-specific dataset, we can specialize the general model to fit particular needs.

    Benefits of Fine-tuning

    • Faster Deployment: Leveraging an existing model allows for a quicker setup, reducing the time from development to deployment significantly.
    • Capitalizing on Pre-trained Weights: The pre-training phase is costly in terms of computation and time. Fine-tuning capitalizes on this investment, using pre-trained weights as a head start for domain tasks.
    • Leveraging Pre-Trained Models: Pre-trained models are increasingly sophisticated and capture various nuances of human language.

    Examples of Successful Applications

    The medical field, for instance, has seen strides with fine-tuned models specializing in entity recognition, question answering, and summarization tasks. In patient data analysis, these models can parse through vast amounts of unstructured text, extracting relevant information with precision.

    RAG: Incorporating Explicit Knowledge

    RAG, on the other hand, is a more recently introduced framework designed to incorporate external knowledge sources into the inference process. It aims to enhance the rationality and awareness of AI systems by allowing them to query reference materials as part of their preliminary thinking.

    The RAG Framework in Depth

    The RAG framework consists of two components: a retriever and a reader. The retriever uses a query to extract relevant passages from a knowledge source, and the reader processes these passages to find an answer or provide context.

    Image courtesy – https://lilianweng.github.io/posts/2020-10-29-odqa/

    Advantages of RAG

    • Handling Out-of-Domain Queries: RAG is capable of tackling a broader set of tasks, not just those within the dataset scope, by referring to the internet or other massive knowledge bases.
    • Interpretability: The retriever component offers insights into the knowledge basis of the model’s decisions, essential for accountability and trust in AI systems.

    Real-World Use Cases

    In legal research, for example, RAG models can sift through laws, cases, and precedents to provide up-to-date advice, cross-referencing information as legal landscapes shift. As such, the legal domain provides a fertile ground for RAG models to shine, reshaping how we approach legal queries and research.

    Strengths and Weaknesses of Fine-tuning

    Fine-tuning isn’t without its downsides. While it excels in many facets, particularly speed and leveraging existing models, it does come with concerns over model performance in unique domains.

    Discussion of the Strengths

    Fine-tuned models often achieve better performance on in-domain tasks, as they’ve been trained to recognize and respond to specific patterns and language nuances within the domain.

    Analysis of the Weaknesses

    Fine-tuned models can be sensitive to the distribution and quality of the training data. Overfitting, a common problem, may occur, leading to less generalizable models. Moreover, fine-tuning can inadvertently strip away some of the broader knowledge captured in the pre-training phase.

    Strengths and Weaknesses of RAG

    RAG’s ability to query large knowledge bases is a distinct advantage but not without its own set of challenges.

    Examination of the Strengths

    RAG models offer improved interpretability, particularly through the retriever’s explicit referencing of the source of its decisions. They also enjoy the benefit of not being overly specialized to a specific domain, serving as a more flexible solution.

    Analysis of the Weaknesses

    However, RAG’s computational requirements are significant. Each query necessitates running through a retrieval system, which can be a bottleneck in terms of the model’s scalability. There’s also the potential for errors in retrieving and parsing large external datasets.

    Trade-offs and Considerations

    When facing a decision between fine-tuning and RAG, it’s critical to assess the nuances of each approach and how they align with the project’s objectives and constraints.

    Comparison of the Two Approaches

    • Performance: Fine-tuned models often outperform RAG models on in-domain tasks. However, RAG’s ability to call upon external knowledge can provide a richer context and improve overall understanding.
    • Flexibility: RAG models are inherently more flexible, handling out-of-domain queries with ease. Fine-tuned models may struggle with tasks beyond their initial scope.
    • Resource Requirements: Fine-tuning generally requires fewer resources, both in terms of infrastructure and data. RAG, with its need for knowledge bases and retrieval systems, tends to be more resource-intensive.

    Factors to Consider

    Certain factors, such as the availability of domain-specific data, the tolerance for uncertainty in results, and the willingness to invest in computational power, should heavily influence the choice between these two approaches.

    Conclusion

    In navigating the complex terrain of domain knowledge incorporation in language models, our journey is one of constant assessment and adaptation. Both fine-tuning and RAG represent leading strategies, each replete with strengths and trade-offs. While there may be no one-size-fits-all answer, the key to unlocking the potential of AI systems lies in understanding and consciously selecting the tool that best suits the task at hand.

    As we stride forward, it’s clear that a balanced approach, perhaps even a hybrid of fine-tuning and RAG, could be the most promising direction. It’s incumbent upon us as practitioners to continue probing, experimenting, and pushing the boundaries of what is possible with language models. By doing so, we will not only elevate the efficiency of our AI systems but also deepen our understanding of what it truly means to teach machines with human wisdom.

    Investing in the right approach isn’t just about model performance; it’s about the ethical and practical implications of the choices we make in the burgeoning field of AI. The confluence of domain knowledge and language models is a domain ripe with potential, and as we integrate these methods into our systems, it will be exciting to see how they unfold, bringing us discoveries, better performance, and perhaps most importantly, a greater appreciation for the delicate art of AI model construction.

  • The EU AI Act: A New Framework for the Development and Use of Artificial Intelligence

    The EU AI Act: A New Framework for the Development and Use of Artificial Intelligence

    The European Union has introduced a new framework for the development and use of Artificial Intelligence (AI). The AI Act  which The European Parliament passed the AI Act on June 14, 2023., aims to ensure that AI is developed and used in a way that respects fundamental rights and freedoms, such as the right to privacy, the right to non-discrimination, and the right to safety.

    The AI Act identifies three categories of AI systems:

    • High-risk AI systems: These systems are considered to pose a high risk to fundamental rights and freedoms. High-risk AI systems will be subject to strict requirements, such as mandatory ex-ante conformity assessments, transparency obligations, and user control mechanisms.
    • Moderate-risk AI systems: These systems are considered to pose a moderate risk to fundamental rights and freedoms. Moderate-risk AI systems will be subject to a lighter set of requirements than high-risk AI systems, such as risk management measures and transparency obligations.
    • Low-risk AI systems: These systems are considered to pose a low risk to fundamental rights and freedoms. Low-risk AI systems will not be subject to any specific requirements under the Act.

    The AI Act also establishes a new European Artificial Intelligence Board (EAAB) to oversee the implementation of the Act. The EAAB will be composed of representatives from the European Commission, national authorities, and stakeholders.

    The AI Act is a significant piece of legislation that will have a major impact on the development and use of AI in the European Union. The Act is still under negotiation, but it is expected to be finalized in 2023.

    Here are some of the key benefits of the EU AI Act:

    • Ensure that AI is developed and used in a way that respects fundamental rights and freedoms. The EU high-risk AI regulation will ban AI systems that are considered to pose an unacceptable risk to fundamental rights and freedoms. This includes AI systems that are used for social scoring, mass surveillance, or biometric identification without consent. The regulation will also require AI systems that are considered to pose a high risk to fundamental rights and freedoms to comply with a number of safeguards. These safeguards will help to ensure that AI systems are developed and used in a way that respects the fundamental rights and freedoms of individuals.
    • Create a level playing field for businesses that develop and use AI in the European Union. This is because the regulation will apply to all AI systems that are considered to pose a high risk, regardless of where the developer or user is located. This will help to prevent businesses from moving their operations to countries with less stringent AI regulations in order to avoid compliance costs. The regulation will also require businesses to comply with a number of technical standards, which will help to ensure that AI systems are interoperable and that data can be shared more easily between different systems. This will make it easier for businesses to develop and use AI solutions, and it will also help to boost innovation in the field of AI.
    • Help to boost innovation in the field of AI. The AI Act regulations are designed to boost innovation in the field of AI by providing a clear framework for the development and use of AI systems. The regulation will also create a level playing field for businesses, which will make it easier for them to invest in AI research and development.

    Here are some of the potential challenges of the EU AI Act:

    • It could be difficult to implement and enforce.
    • It could stifle innovation in the field of AI.
    • It could lead to the fragmentation of the AI market in the European Union.

    Overall, the EU AI Act is a positive step towards ensuring that AI is developed and used in a responsible and ethical way. However, it is important to be aware of the potential challenges of the Act and to work to mitigate them.

    To learn more about the EU AI Act, please visit the following link: https://artificialintelligenceact.eu/