create_agent API connects a
model to a set of tools and runs the agent loop until the model produces an
answer.
The langchain-neuraltrust middleware evaluates messages and tool activity
inside that loop. Because it runs in the application, it can block a tool call
before execution. To send the model and tool calls through TrustGate instead,
see TrustGate.
Integration capabilities
Pass the agent’s tool list to the middleware to include tool declarations in
the evaluation payload. This allows tool-poisoning rules to inspect tool names,
descriptions, and schemas.
Policy can also apply across turns rather than to one message at a time, which
requires passing a conversation identifier on every invocation — see
Group the turns of a conversation.
Before you start
Create the policy in Observe mode. Observe records decisions in Activity without enforcing
them. Review the results, then switch the policy to Enforce. See
Policies.
Install
langchain-neuraltrust.
It does not require changes to the model provider configuration.Add the middleware
Choose what gets evaluated
Four independent flags map to hooks in the agent loop:
check_tool_results is skipped when check_input is also on because the
conversation payload already contains those tool messages. This avoids
evaluating the same text twice.Enable check_tool_calls when a policy must stop a tool call before it runs.
It adds one evaluation round trip per tool call.Both sync (invoke) and async (ainvoke) paths are implemented for every hook.Group the turns of a conversation
A multi-turn attack spreads its intent across several messages. Each one reads as
harmless on its own; the escalation, the reinforcement, or the reassembled
instruction exists only across turns. See
multi-turn attacks
for the techniques involved — Crescendo, Echo Chamber, Multi-Turn Manipulation,
and Payload Splitting. Their common target is a check that inspects each message
independently, which is what the middleware does until you give it the
conversation.Two inputs give TrustGuard the conversation, and they are complementary rather
than alternatives:Both turns evaluate under the same
Neither happens by default.
/v1/evaluate
synthesizes a session_id when the field is omitted,
so an agent that never passes a thread_id produces one session per evaluation.
Nothing errors and no finding goes missing, so the gap is only visible as
single-turn conversations in Activity.Pass a thread_id on every invocation
The middleware takessession_id from the LangGraph thread_id in the run
config. Set it per invocation, so one agent and one middleware instance can serve
many conversations:session_id, on every enabled stage —
input, output, tool result, and tool call.The thread_id must be a non-blank string. LangGraph accepts a uuid.UUID
object or an int and checkpoints correctly, but the middleware only forwards a
string, so a non-string or empty thread_id drops session_id without an error.
Call str() on whatever your application uses as a conversation identifier.Give one call the history
With a checkpointer and a stablethread_id, before_model receives the whole
accumulated thread, and the middleware forwards all of it. The input evaluation
payload therefore already contains the earlier turns. Without a checkpointer, the
application passes the transcript it wants evaluated in the invoke payload
itself.The message list is sent untrimmed. It grows by two messages per turn, more when
tools run, and the payload_tools schemas are re-sent on every pass through the
agent loop. There is no windowing setting, so raise timeout from its 5.0
second default for long threads and measure against a representative one.Only the input stage carries history. The other three depend entirely on the
session_id grouping for conversational context.Verify
AIMessage. The model is not called, and on_violation prints the
trace_id. Use it to match the run to the finding in Activity.Call close() after invoke, or await aclose() after ainvoke, when the
middleware owns its HTTP clients.Reference
Coverage
Ask. The middleware does not implement an
ask flow. It treats ask as an
unknown verdict and fails closed, so the result is a block. Implement any
human approval step in the application.
Limits. The middleware inspects only the configured agent. A service that
calls the provider directly bypasses it. On streaming routes, output evaluation
occurs after delivery. See Streaming.
Detectors evaluate text and tool calls, but
non-text content parts are not stripped from the payload: a message whose
content is a list of blocks is forwarded to /v1/evaluate as-is, so the image
data available to the agent is also transmitted for evaluation. Non-text parts
cannot be rewritten.
Tool-call redaction requires check_tool_calls=True. A compatible transform
verdict replaces the pending tool arguments before dispatch; it does not rename
the tool.
Each guarded stage adds a round trip, and the hooks run on every pass through the
agent loop. With check_input, check_output, and check_tool_calls enabled, a
turn that calls two tools in parallel and then answers makes six evaluation
calls: input and output on each model pass, plus one per tool call. Sequential
tool calls add further evaluations. Set timeout to limit the effect of a
stalled request.
What is evaluated
Every stage calls
POST /v1/evaluate with the
collector key, and the policy’s detectors decide the verdict.
Verdicts
The middleware applies each verdict to agent state as follows:
At message stages, preserving the ID ensures that a transformed message replaces
the original in the thread instead of being appended to it.
exit_behavior decides what a block does:
These behaviors do not remove
SystemMessages. Under end and replace, the
middleware also keeps the thread structurally valid. It clears tool_calls from
a blocked AIMessage and converts a blocked ToolMessage so it does not leave
an orphaned tool response. Otherwise, the next model call would fail because the
thread contains a tool call without a corresponding response.
On the tool path a block returns a ToolMessage(status="error") and the tool is
never called. A transform dispatches the tool with the rewritten arguments.
on_violation is synchronous and fires from both invoke and ainvoke.
Exceptions from the callback propagate unchanged, so handle callback failures in
your application if they should not interrupt the agent.
Configuration
Every setting falls back to an environment variable:session_id and TRUSTGUARD_SESSION_ID are read once at construction and pin
that value to every request the instance makes. Leave both unset and pass the
conversation identifier per invocation as the LangGraph thread_id instead. See
Group the turns of a conversation.
The middleware never sends consumer_id, so a collector attached in Consumer
ID routing mode has nothing to match. Attach it in Default routing mode.
Fail-open or fail-closed. unreachable_fallback applies only when
TrustGuard cannot be reached because of a connection error, timeout, HTTP
502/504 response, or HTTP 429 response after
retries are exhausted. Those are retried with backoff, honoring Retry-After,
before the fallback applies.
The following cases fail closed even when
fail_open is configured:
- HTTP 401/403, and 503 entitlement failures
- any other 4xx/5xx
- a non-JSON
200, or an unknown verdict, includingask - a
transformed_payloadthat cannot be applied safely - TLS failures. An expired or untrusted certificate is a connect error, but
the middleware treats it as a configuration fault rather than an outage, so
fail_opennever covers it. A corporate MITM proxy without a trusted CA will therefore block every turn even withfail_openset.
Streaming
after_model runs on the assembled AIMessage, once the model call has
finished.
For preventive response enforcement, disable streaming or buffer the stream
until a verdict is available. Buffering increases time to first token. You can
also place TrustGate in front of the route to inspect
the stream at the gateway.
Attributes
session_id: the LangGraphthread_idunless you set it, and omitted when neither is a non-blank string. Sent on every stage. See Group the turns of a conversationmodel_name: the runtime context model unless you set ittrace_id: included in every verdict and the matching finding in Activity
direction has two values, not four. Only the output stage sends
direction: "output"; the input, tool-result, and tool-call stages all send
direction: "input", so all three are evaluated by the policy’s Input phase.
on_violation(verdict, stage) receives the stage name alongside the verdict, so
alerting can distinguish an input finding from a tool-call finding without
parsing the payload. Reconcile a run with the console on trace_id.
Troubleshooting
TrustGate
LangChain needs no NeuralTrust package to call TrustGate. PointChatOpenAI
and ChatAnthropic at the application’s base URL and key. Anthropic’s Python
client takes no /v1 on that URL. The same snippets are on
Frameworks & SDKs.
langchain-mcp-adapters. MultiServerMCPClient speaks
streamable_http and sends the application key in headers. The dict key is
the name you give that server.
Related
- Frameworks & SDKs: the same LangChain clients, and the other frameworks
- Policies: Gates: configure Block and Transform actions
- Evaluate API: request and response reference
- Agent and MCP security detectors: indirect prompt injection and tool poisoning
- Python SDK: protect calls outside an agent loop
- How it works: compare available collectors
- TrustGate: apply policies at the gateway and monitor streamed responses
NeuralTrust/langchain-neuraltrust: source and issues- LangChain docs: LangChain reference