Langchain
Langchain Agents with Aiceberg
This document explains how we monitor different event types across an Agentic workflow in Agents built using Langchain framework
AicebergMiddleware provides real-time safety monitoring for your LangChain Agents. It tracks user inputs, LLM calls, and tool executions and memory to ensure safe and compliant agent behavior.
Simply add it to your LangChain Agents with:
middleware=[AicebergMiddleware()]
and get instant visibility into the safety of your conversational and reasoning workflows.
What is middleware and how it exposes hooks in Langchain?
Langchain middleware is a feature that allows developers to intercept and customize the agent's core loop, which involves calling a language model and executing tools.
Middleware is used to control and customize agent execution at every step. Middleware provides a way to more tightly control what happens inside the agent.
The core agent loop involves calling a model, letting it choose tools to execute, and then finishing when it calls no more tools.
Middleware exposes hooks before and after different event types. You can build custom middleware by implementing hooks that can be run at specific points in the agent execution flow. There are two types of middleware: decorator-based and class-based.
This guide uses class-based middleware because it is more powerful for complex middleware with multiple hooks; decorator-based middleware is useful for a single-hook middleware. Please refer to the appendix for more info.
Aiceberg as a middleware
Aiceberg middleware in LangChain acts as an advanced, real-time control layer within the agent execution flow, enabling security, compliance, and observability features for generative and Agentic AI systems. By plugging Aiceberg middleware into a LangChain agent, every prompt, tool call, and model response can be inspected and checked for policy violations, risks like prompt injection, and sensitive data leakage before reaching the model or returning to the user.
We use specific hooks for monitoring the respective event types in our custom AicebergMiddleware class.
before_agent
Before agent starts (once per invocation)
User → Agent (forward)
after_agent
After agent completes (up to once per invocation)
Agent → User (backward)
wrap_model_call
Around each model call
Agent ↔ LLM (forward & backward)
wrap_tool_call
Around each tool call
Agent ↔ Tools (forward & backward)
before_model / wrap_tool_call
Before model invocation starts / around the read/write memory tool
Agent ↔ Memory (forward & backward)
wrap_tool_call
Around sub-agent as a tool
Agent → Agent
P.S. LangChain generally offers two types of hooks: Node-style hooks and Wrap-style hooks. We use Wrap-style hooks—specifically wrap_model_call and wrap_tool_call—to observe the Agent-to-LLM and Agent-to-Tool event types. Wrap-style hooks allow intercepting execution with full control over handler calls (including the ability to block calls). See the appendix for a detailed distinction.
Events Aiceberg Monitors
User-to-Agent (user_agt)
The agent workflow is instrumented with two monitoring hooks — before_agent and after_agent — which capture User-to-Agent (U2A) events. These events represent both the initial user query sent to the agent and the final agent response produced after completing the full agentic reasoning process. Both hooks act as gates in the agent pipeline. If either hook detects a signal or receives a flagged response from Aiceberg, the entire agent flow can be blocked immediately.
Hook: before_agent
Monitors the user query being sent to the agent by forwarding it to Aiceberg. The Aiceberg response contains an event_id that is stored for correlation with the later final agent response.
Example:
Hook: after_agent
Monitors the agent’s response after it’s generated, linking it to the earlier user query using the stored event ID. If Aiceberg flags the content, the agent flow is halted or the output is redacted.
Example:
Agent-to-LLM (agt_llm)
The agent flow is instrumented with the wrap_model_call hook, which monitors the entire Agent-to-LLM (A2M) interaction lifecycle. This hook captures both the request sent from the agent to the model and the response returned from the model.
Hook: wrap_model_call
Monitors every LLM call initiated by the Agent, as well as the corresponding model response returned from the LLM. Interactions are sanitized (e.g., removing usage parameters) before sending to Aiceberg. If Aiceberg flags content, the call or response can be blocked or replaced with a safe message.
Example (abbreviated):
Agent-to-Tool (agt_tool)
Every tool invocation passes through a monitoring wrapper (wrap_tool_call) that integrates with Aiceberg. Both the tool call and the tool output are tracked and sent to Aiceberg.
Hook: wrap_tool_call
Monitors every tool call made by the Agent and the corresponding tool output. If Aiceberg flags the tool input or output, the middleware can raise an exception and block execution.
Example:
Can the tool call be blocked?
Yes. By raising an exception when Aiceberg flags input/output, the middleware can block a tool call mid-execution.
Why tool blocking is not done by default:
Blocking a tool mid-flight can disrupt the agent’s reasoning flow because the LLM expects tool outputs to continue processing. Without a proper result, you may: (1) send an error message as the tool result (confuses the LLM), (2) send empty/fake data (breaks logic), or (3) stop the entire agent (user gets incomplete response).
Recommended approach:
Log all tool calls and outputs for audit visibility.
If a tool produces unsafe content, handle it after execution (e.g., when the LLM processes the result or before displaying it to the user).
Block tools selectively when the use case demands it (e.g., preventing unsafe DB writes). If blocking, ensure the aftermath is handled properly.
Agent-to-Memory (agt_mem)
LangChain’s agent state manages short-term memory, enabling the agent to retain and access context across conversation turns. The state is persisted with a checkpointer and updated automatically. You can monitor or manipulate this short-term memory using hooks or tools.
1. Accessing Short-Term Memory with @before_model
@before_modelUse the @before_model hook to inspect/modify the agent’s memory before the model is called. The state (including messages) is passed to this hook.
Example — trimming messages before model invocation:
Example — monitoring all conversation messages:
Aiceberg can:
Inspect short-term memory before the model uses it.
Detect sensitive or policy-violating data.
Detect and redact sensitive info (PII) before it reaches the model.
2. Accessing Short-Term Memory via Tools
Tools can read/write short-term memory via runtime.state.
Example — reading from short-term memory:
Example — writing to short-term memory:
Monitoring via wrap_tool_call:
Aiceberg integrates with
wrap_tool_callto monitor inputs and outputs of tool calls, which includes data read from / written to memory. This provides visibility into state modifications and allows detection of sensitive data exposure.
Agent-to-Agent
Agent-to-agent communication occurs through two patterns: Tool Calling (supervisor agent calls sub-agents as tools) and Handoffs (control passes directly to another agent).
Tool Calling
A sub-agent can be called as a tool. The wrap_tool_call hook monitors the incoming request (what is passed to the sub-agent) and the returned response, enabling policy enforcement on the data exchanged.
Example of calling a sub-agent via a tool:
Handoffs
Handoffs refer to passing control and state to another agent. The full implementation is still evolving in LangChain, but conceptual docs and LangGraph components indicate handoffs use a Command object to transfer control and state. Because state is updated and available to the before_model hook, Aiceberg can monitor or redact information passed during handoffs via that hook.
Quick setup
Appendix
Detailed distinction between Node-Style hooks and Wrap-Style hooks
The main difference between node-style hooks and wrap-style hooks in LangChain centers on their execution model and the level of control they provide over the agent's operations.
Node-Style Hooks
Run at specific, predefined points in the agent’s execution flow (e.g., before the agent starts, before or after model calls, or after the agent completes).
Execute sequentially and are typically used for tasks like logging, validation, or updating state.
Invocation order is fixed: before hooks run first to last, after hooks run last to first.
Wrap-Style Hooks
Intercept the execution of specific calls, such as model calls (
wrap_model_call) or tool calls (wrap_tool_call).Provide full control over when—or if—the underlying handler is called, allowing execution to be blocked, retried, or modified dynamically.
These hooks nest like function calls, enabling advanced control flow like retries, fallback mechanisms, or blocking malicious inputs.
We chose wrap-style hooks (wrap_model_call and wrap_tool_call) to observe and control Agent-to-LLM and Agent-to-Tool interactions because they allow full interception of these calls. Node-style hooks are well suited for observing User-to-Agent and Agent-to-User events where simple observation suffices.
Detailed explanation of why we chose class-based middleware over decorator-based
Class-based middleware provides a centralized structure to manage multiple lifecycle hooks—such as before_agent, before_model, wrap_model_call, after_agent, wrap_tool_call, etc.—within one class. This is powerful for orchestrating multiple hooks cohesively and maintaining shared middleware state or configuration across those hooks.
Decorator-based middleware is simpler for single-hook logic but can become fragmented when multiple hook points and internal state management are required.
Using a single class-based middleware to implement all hooks provides:
A unified and centralized way to monitor and control different event types across the entire agent execution flow.
Ease of integration: create an instance of the middleware class and pass it to
middlewarewhen creating the agent.Automatic invocation of lifecycle hooks by LangChain during agent execution.
Last updated