ValGuard + LangGraph: validate graph handoffs

Insert ValGuard between LangGraph nodes so a failed schema or policy check cannot become the next node's input.

Last verified:

LangGraph owns the state machine: nodes, edges, and retries. ValGuard owns the contract on each model completion before the next node reads it. Point your chat model client at ValGuard, or validate inside a node, so a failed schema or policy check cannot become the next node's input.

When you need this

Node A classifies a ticket into {team, urgency}. Node B opens a refund tool when team == "billing". The model invents team: "billing_escalation". Without a gate, the router falls through or hits the wrong tool. With ValGuard, an enum rule fails, you re-ask or block, and graph state never carries the bad label.

Architecture

flowchart LR
  G[LangGraph node] --> C[Chat model client]
  C --> VG[ValGuard proxy]
  VG --> M[Upstream model]
  M --> VG
  VG -->|pass| S[Update graph state]
  VG -->|block| R[Route to human or end]

Keep LangGraph for control flow. Put the trust boundary on the OpenAI-compatible path so every language and worker that shares the same agent slug gets the same rules.

Setup (recommended pattern)

  1. Create a ValGuard agent per graph role (classifier, drafter, tool planner).
  2. Attach schema and policy packs; start in shadow mode.
  3. Point the LangChain / LangGraph chat model base_url at ValGuard with X-VG-Agent.
  4. Store validation_passed (or the block envelope) in graph state. Route on that field, not on raw text.
  5. Enforce when shadow metrics look stable.

Code and config

Python client pointed at ValGuard (OpenAI-compatible):

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.valguard.ai/v1",
    api_key=os.environ["VG_API_KEY"],
    default_headers={"X-VG-Agent": "support-classifier"},
)

resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[
        {
            "role": "system",
            "content": 'Return JSON only: {"team":"billing"|"fraud"|"general","urgency":1-5}',
        },
        {"role": "user", "content": "Card charged twice on invoice 4412"},
    ],
)
print(resp.choices[0].message.content)

curl for the same call:

curl -s https://api.valguard.ai/v1/chat/completions \
  -H "Authorization: Bearer $VG_API_KEY" \
  -H "X-VG-Agent: support-classifier" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
      {"role": "user", "content": "Card charged twice on invoice 4412"}
    ]
  }'

In the graph, treat a block as a typed failure edge. Do not call tools after a failed validation.

On validation failure

ModeGraph behavior
PassWrite parsed JSON into state; follow the happy edge
Re-askValGuard may retry once; use the final response
BlockTake an escalate or end edge; attach rule IDs to the ticket
ShadowLog only; still update state as you do today until you enforce

Pair this with the Agentic Tool Call Validator pattern when a node proposes tools.

Latency

Engine: microseconds per pack. HTTP layer: about 0.36 ms p50 with a mocked upstream. A three-step ValGuard playbook (if you use one instead of LangGraph) adds about 1.8 ms (support archetype) to 1.4 ms (RAG archetype) p50. Model time still dominates.

Block and re-ask buffer the full reply. Warn, log, and shadow can stream. See methodology and Trust.

Correlating IDs

Put the LangGraph thread_id / run id in your logs next to ValGuard's X-Request-Id and X-Trace-Id. If you already emit traceparent, send it on the chat request so both systems share one trace tree.

What this does not cover

  • LangGraph checkpointing, interrupts, and human-in-the-loop remain LangGraph's job.
  • ValGuard does not replace LangGraph as a durable state machine.
  • Framework-native validators in one repo do not automatically protect other services; the proxy does.
  • Claims with no rule signature still pass.
  • There is no local ValGuard runtime for offline graph tests; use shadow mode on hosted traffic or mock the proxy in CI.

Deployment

Hosted SaaS by default. Enterprise self-host only with a license. No public Docker image or CLI runtime. See Trust and pricing (Developer $69, Growth $149, Production $399, Enterprise from $1499).

Related

FAQ

Do I need a ValGuard playbook if I already have LangGraph? No. Keep LangGraph. Use ValGuard as the validation layer on model calls. Use a ValGuard playbook when you want the graph and the rules in one place.

Can I validate only some nodes? Yes. Point only those chat clients at ValGuard, or use different X-VG-Agent values per node role.

What about streaming tokens into the UI? Use warn/log/shadow if you need token-by-token output. Block and re-ask buffer first.

Exit cost? Export playbooks as valguard-playbook-template/1 JSON and pull per-agent validators from the API. Org-wide policy export is not shipped yet.

Next step

Start with the quickstart, then shadow mode rollout on one classifier node before you gate tool nodes.