<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LangChain Middleware on On My Way</title>
    <link>https://tuanzi-wow.cn/frameworks/langchain/middleware/</link>
    <description>Recent content in LangChain Middleware on On My Way</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <atom:link href="https://tuanzi-wow.cn/frameworks/langchain/middleware/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Custom Middleware</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/middleware/custom-middleware/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/middleware/custom-middleware/</guid>
      <description>Custom Middleware相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>通过在agent工作流中实现hook来构建自定义的midddleware</p>
<h1 id="hooks">Hooks</h1>
<p>two types:</p>
<h2 id="node-style-hook">Node-style hook</h2>
<p>在特定的执行点顺序执行.Use for validation,logging and state updates.</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Hook</th>
					<th style="text-align: left">When it runs</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>before_agent</code></td>
					<td style="text-align: left">Before agent starts (once per invocation)</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>before_model</code></td>
					<td style="text-align: left">Before each model call</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>after_model</code></td>
					<td style="text-align: left">After each model response</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>after_agent</code></td>
					<td style="text-align: left">After agent completes (once per invocation)</td>
			</tr>
	</tbody>
</table>
<p><strong>Wrap-style hooks</strong> run around each call, giving you control over execution:</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Hook</th>
					<th style="text-align: left">When it runs</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>wrap_model_call</code></td>
					<td style="text-align: left">Around each model call</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>wrap_tool_call</code></td>
					<td style="text-align: left">Around each tool call</td>
			</tr>
	</tbody>
</table>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from langchain.agents.middleware import before_model, after_model, AgentState
</span></span><span class="line"><span class="cl">from langchain.messages import AIMessage
</span></span><span class="line"><span class="cl">from langgraph.runtime import Runtime
</span></span><span class="line"><span class="cl">from typing import Any
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@before_model(can_jump_to=[&#34;end&#34;])
</span></span><span class="line"><span class="cl">def check_message_limit(state: AgentState, runtime: Runtime) -&gt; dict[str, Any] | None:
</span></span><span class="line"><span class="cl">    if len(state[&#34;messages&#34;]) &gt;= 50:
</span></span><span class="line"><span class="cl">        return {
</span></span><span class="line"><span class="cl">            &#34;messages&#34;: [AIMessage(&#34;Conversation limit reached.&#34;)],
</span></span><span class="line"><span class="cl">            &#34;jump_to&#34;: &#34;end&#34;
</span></span><span class="line"><span class="cl">        }
</span></span><span class="line"><span class="cl">    return None
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@after_model
</span></span><span class="line"><span class="cl">def log_response(state: AgentState, runtime: Runtime) -&gt; dict[str, Any] | None:
</span></span><span class="line"><span class="cl">    print(f&#34;Model returned: {state[&#39;messages&#39;][-1].content}&#34;)
</span></span><span class="line"><span class="cl">    return None
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="warp-style-hooks">Warp-style hooks</h2>
<p>拦截执行并且控制出处理机制的调用时机.Use for retries,caching,and information.
由你决定处理机制是被调用zero times(short-circuit短路),once(normal flow), or multiple times(retry logic)</p>
<p><strong>Available hooks:</strong></p>
<ul>
<li><code>wrap_model_call</code> - Around each model call</li>
<li><code>wrap_tool_call</code> - Around each tool call</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.middleware</span> <span class="kn">import</span> <span class="n">wrap_model_call</span><span class="p">,</span> <span class="n">ModelRequest</span><span class="p">,</span> <span class="n">ModelResponse</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrap_model_call</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">retry_model</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handler</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">ModelRequest</span><span class="p">],</span> <span class="n">ModelResponse</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ModelResponse</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">attempt</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">handler</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">attempt</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">raise</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Retry </span><span class="si">{</span><span class="n">attempt</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s2">/3 after error: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="state-updates">State updates</h1>
<p>Both node-style and warp-style hooks can update agent state. But the mechanism differs.</p>
<p>Node-style hooks:
直接返回一个dict, 由graph的reducers将该dict应用到agent&rsquo;s state</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from langchain.agents.middleware import after_model, AgentState
</span></span><span class="line"><span class="cl">from langgraph.runtime import Runtime
</span></span><span class="line"><span class="cl">from typing import Any
</span></span><span class="line"><span class="cl">from typing_extensions import NotRequired
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class TrackingState(AgentState):
</span></span><span class="line"><span class="cl">    model_call_count: NotRequired[int]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@after_model(state_schema=TrackingState)
</span></span><span class="line"><span class="cl">def increment_after_model(state: TrackingState, runtime: Runtime) -&gt; dict[str, Any] | None:
</span></span><span class="line"><span class="cl">    return {&#34;model_call_count&#34;: state.get(&#34;model_call_count&#34;, 0) + 1}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Warp-style hooks:
对于model calls,返回<code>ExtendedModeResponse</code> with a <code>Command</code>来通过model response插入state updates</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.middleware</span> <span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">wrap_model_call</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelResponse</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AgentState</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ExtendedModelResponse</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.types</span> <span class="kn">import</span> <span class="n">Command</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing_extensions</span> <span class="kn">import</span> <span class="n">NotRequired</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsageTrackingState</span><span class="p">(</span><span class="n">AgentState</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Agent state with token usage tracking.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">last_model_call_tokens</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrap_model_call</span><span class="p">(</span><span class="n">state_schema</span><span class="o">=</span><span class="n">UsageTrackingState</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">track_usage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handler</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">ModelRequest</span><span class="p">],</span> <span class="n">ModelResponse</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ExtendedModelResponse</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">handler</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ExtendedModelResponse</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">model_response</span><span class="o">=</span><span class="n">response</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">command</span><span class="o">=</span><span class="n">Command</span><span class="p">(</span><span class="n">update</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;last_model_call_tokens&#34;</span><span class="p">:</span> <span class="mi">150</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><a href="https://reference.langchain.org.cn/python/langgraph/types/Command"><code>Command</code></a> 会流经图的 reducer，因此更新会被正确应用，并且消息是累加的，而不是替换现有状态</p>
<p>对于tool calls,直接返回一个<code>Command</code></p>
<h3 id="composition-with-multiple-middleware">Composition with multiple middleware</h3>
<p>当多个Middleware layer返回<code>ExtendedModelResponse</code>时,他们的命令会进行组合</p>
<ul>
<li>命令通过reducer应用:每个Command都会成为一个独立的状态更新,对于消息而言,他们是additive</li>
<li>冲突时外层优先</li>
<li>重试安全:如果外层的Middleware会导致handler()的多次调用,会抛弃早期的调用命令</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from typing import Annotated, Callable
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">from langchain.agents.middleware import (
</span></span><span class="line"><span class="cl">    AgentMiddleware,
</span></span><span class="line"><span class="cl">    AgentState,
</span></span><span class="line"><span class="cl">    ExtendedModelResponse,
</span></span><span class="line"><span class="cl">    ModelRequest,
</span></span><span class="line"><span class="cl">    ModelResponse,
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">from langchain.messages import SystemMessage
</span></span><span class="line"><span class="cl">from langgraph.types import Command
</span></span><span class="line"><span class="cl">from typing_extensions import NotRequired
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">def _last_wins(_a: str, b: str) -&gt; str:
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Reducer: last writer wins (outer overwrites inner).&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">    return b
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class CustomMiddlewareState(AgentState):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Agent state: trace_layer uses last-wins (outer wins), messages use additive reducer.&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Non-reducer field with last-wins: both middleware write; outermost value wins
</span></span><span class="line"><span class="cl">    trace_layer: NotRequired[Annotated[str, _last_wins]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class OuterMiddleware(AgentMiddleware):
</span></span><span class="line"><span class="cl">    def wrap_model_call(
</span></span><span class="line"><span class="cl">        self,
</span></span><span class="line"><span class="cl">        request: ModelRequest,
</span></span><span class="line"><span class="cl">        handler: Callable[[ModelRequest], ModelResponse],
</span></span><span class="line"><span class="cl">    ) -&gt; ExtendedModelResponse:
</span></span><span class="line"><span class="cl">        response = handler(request)
</span></span><span class="line"><span class="cl">        return ExtendedModelResponse(
</span></span><span class="line"><span class="cl">            model_response=response,
</span></span><span class="line"><span class="cl">            command=Command(update={
</span></span><span class="line"><span class="cl">                &#34;trace_layer&#34;: &#34;outer&#34;,
</span></span><span class="line"><span class="cl">                &#34;messages&#34;: [SystemMessage(content=&#34;[Outer ran]&#34;)],
</span></span><span class="line"><span class="cl">            }),
</span></span><span class="line"><span class="cl">        )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class InnerMiddleware(AgentMiddleware):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Adds trace_layer and message. Outer adds to same keys; trace_layer: outer wins, messages: additive.&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    def wrap_model_call(
</span></span><span class="line"><span class="cl">        self,
</span></span><span class="line"><span class="cl">        request: ModelRequest,
</span></span><span class="line"><span class="cl">        handler: Callable[[ModelRequest], ModelResponse],
</span></span><span class="line"><span class="cl">    ):
</span></span><span class="line"><span class="cl">        response = handler(request)
</span></span><span class="line"><span class="cl">        return ExtendedModelResponse(
</span></span><span class="line"><span class="cl">            model_response=response,
</span></span><span class="line"><span class="cl">            command=Command(update={
</span></span><span class="line"><span class="cl">                &#34;trace_layer&#34;: &#34;inner&#34;,
</span></span><span class="line"><span class="cl">                &#34;messages&#34;: [SystemMessage(content=&#34;[Inner ran]&#34;)],
</span></span><span class="line"><span class="cl">            }),
</span></span><span class="line"><span class="cl">        )
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="create-middleware">Create middleware</h1>
<p>In two ways:</p>
<h2 id="decorator-base-middleware">Decorator-base middleware</h2>
<p>for single-hook middleware. To warp individual functions.</p>
<p><strong>Node-style:</strong></p>
<ul>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/before_agent"><code>@before_agent</code></a> - Runs before agent starts (once per invocation)</li>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/before_model"><code>@before_model</code></a> - Runs before each model call</li>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/after_model"><code>@after_model</code></a> - Runs after each model response</li>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/after_agent"><code>@after_agent</code></a> - Runs after agent completes (once per invocation)</li>
</ul>
<p><strong>Wrap-style:</strong></p>
<ul>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/wrap_model_call"><code>@wrap_model_call</code></a> - Wraps each model call with custom logic</li>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/wrap_tool_call"><code>@wrap_tool_call</code></a> - Wraps each tool call with custom logic</li>
</ul>
<p><strong>Convenience:</strong></p>
<ul>
<li><a href="https://reference.langchain.com/python/langchain/agents/middleware/types/dynamic_prompt"><code>@dynamic_prompt</code></a> - Generates dynamic system prompts</li>
</ul>
<p><strong>When to use decorators:</strong></p>
<ul>
<li>Single hook needed</li>
<li>No complex configuration</li>
<li>Quick prototyping</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span><span class="o">.</span><span class="n">middleware</span> <span class="n">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">before_model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">wrap_model_call</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AgentState</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelResponse</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span> <span class="n">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langgraph</span><span class="o">.</span><span class="n">runtime</span> <span class="n">import</span> <span class="n">Runtime</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">typing</span> <span class="n">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">before_model</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_before_model</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;About to call model with {len(state[&#39;messages&#39;])} messages&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">wrap_model_call</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">retry_model</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handler</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">ModelRequest</span><span class="p">],</span> <span class="n">ModelResponse</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ModelResponse</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">attempt</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">handler</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">except</span> <span class="n">Exception</span> <span class="n">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">attempt</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">raise</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Retry {attempt + 1}/3 after error: {e}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">log_before_model</span><span class="p">,</span> <span class="n">retry_model</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="class-based-middleware">Class-based middleware</h2>
<p>适用于multiple hooks和更复杂的configuration;适用于你需要同时实现同步和异步for the same hook,或者你想要在一个组件内部组合多个hooks</p>
<p>py 的 <code>AgentMiddleware</code>子类会声明三个属性,agent factory会在编译时会自动提取</p>
<ul>
<li>state_schema:适用自定义的字段拓展agent state</li>
<li>tools:注册middleware承载的工具</li>
<li>transformer:注册范围感知的流转化工厂</li>
</ul>
<p>下面这个例子体现了多个hooks和同时实现同步与异步</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span><span class="o">.</span><span class="n">middleware</span> <span class="n">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">AgentMiddleware</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">AgentState</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModelResponse</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langgraph</span><span class="o">.</span><span class="n">runtime</span> <span class="n">import</span> <span class="n">Runtime</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">typing</span> <span class="n">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">LoggingMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">before_model</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;About to call model with {len(state[&#39;messages&#39;])} messages&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">after_model</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Model returned: {state[&#39;messages&#39;][-1].content}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">async</span> <span class="k">def</span> <span class="nf">abefore_model</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Async version of before_model</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">async</span> <span class="k">def</span> <span class="nf">aafter_model</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Async version of after_model</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Model returned: {state[&#39;messages&#39;][-1].content}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">LoggingMiddleware</span><span class="p">()],</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>适用场景:</p>
<ul>
<li>同时定义同步与异步实现for same hook</li>
<li>multiple hooks needed in a single middleware</li>
<li>复杂的定义需要</li>
<li>有跨项目的复用需求</li>
</ul>
<h3 id="custom-state-schema">Custom state schema</h3>
<p>when you need to track state across hooks,middleware can extend the agent&rsquo;s state with custom properties.</p>
<ul>
<li>跨执行跟踪状态</li>
<li>在hooks之间共享数据</li>
<li>实现横切聚焦</li>
<li>执行条件性决策</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-py" data-lang="py"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents</span> <span class="kn">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.messages</span> <span class="kn">import</span> <span class="n">HumanMessage</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.middleware</span> <span class="kn">import</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">before_model</span><span class="p">,</span> <span class="n">after_model</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing_extensions</span> <span class="kn">import</span> <span class="n">NotRequired</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.runtime</span> <span class="kn">import</span> <span class="n">Runtime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">CustomState</span><span class="p">(</span><span class="n">AgentState</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">model_call_count</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_id</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@before_model</span><span class="p">(</span><span class="n">state_schema</span><span class="o">=</span><span class="n">CustomState</span><span class="p">,</span> <span class="n">can_jump_to</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;end&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check_call_limit</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">CustomState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">count</span> <span class="o">=</span> <span class="n">state</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;model_call_count&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">count</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span><span class="s2">&#34;jump_to&#34;</span><span class="p">:</span> <span class="s2">&#34;end&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@after_model</span><span class="p">(</span><span class="n">state_schema</span><span class="o">=</span><span class="n">CustomState</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">increment_counter</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">CustomState</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">Runtime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span><span class="s2">&#34;model_call_count&#34;</span><span class="p">:</span> <span class="n">state</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;model_call_count&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">check_call_limit</span><span class="p">,</span> <span class="n">increment_counter</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Invoke with custom state</span>
</span></span><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">HumanMessage</span><span class="p">(</span><span class="s2">&#34;Hello&#34;</span><span class="p">)],</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;model_call_count&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;user_id&#34;</span><span class="p">:</span> <span class="s2">&#34;user-123&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="custom-stream-transformer目前不知道有什么用处">Custom stream transformer(目前不知道有什么用处)</h3>
<p>中间件可以注册流转换器工厂，将来自活动智能体流的事件投影到类型化的扩展通道上。这对于显现计数器、旁路伪影（side-channel artifacts）、部分输出或网络层脱敏（wire-level redaction）非常有用，而无需与框架的内置投影耦合。在编译时，中间件注册的工厂将与调用者直接传递给智能体工厂的任何内容合并。<a href="https://docs.langchain.org.cn/oss/python/langchain/event-streaming#register-transformers-on-middleware">最终排序规则</a>使内置的 <code>ToolCallTransformer</code> 处于最前面，并让调用者提供的条目排在最后。将 <code>transformers</code> 类属性设置为一个由工厂可调用对象组成的元组。每个工厂的形式为 <code>Callable[[tuple[str, ...]], StreamTransformer]</code> 并通过 <code>factory(scope)</code> 进行调用，其中 <code>scope</code> 是 mini-mux 作用域元组（<code>()</code> 表示根目录，非空表示子图）；每次调用都返回一个新的转换器，以保持每个子图的隔离。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-py" data-lang="py"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents</span> <span class="kn">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.middleware</span> <span class="kn">import</span> <span class="n">AgentMiddleware</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ToolActivityMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">transformers</span> <span class="o">=</span> <span class="p">(</span><span class="n">ToolActivityTransformer</span><span class="p">,)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5-nano&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">ToolActivityMiddleware</span><span class="p">()],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="execution-order">Execution order</h1>
<p>key rules:</p>
<ul>
<li><code>before_*</code> hooks: First to last</li>
<li><code>after_*</code> hooks: Last to first (reverse)</li>
<li><code>wrap_*</code> hooks: Nested (first middleware wraps all others)</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Before 钩子按顺序运行
</span></span><span class="line"><span class="cl">middleware1.before_agent()
</span></span><span class="line"><span class="cl">middleware2.before_agent()
</span></span><span class="line"><span class="cl">middleware3.before_agent()
</span></span><span class="line"><span class="cl">智能体循环开始
</span></span><span class="line"><span class="cl">middleware1.before_model()
</span></span><span class="line"><span class="cl">middleware2.before_model()
</span></span><span class="line"><span class="cl">middleware3.before_model()
</span></span><span class="line"><span class="cl">Wrap 钩子像函数调用一样嵌套
</span></span><span class="line"><span class="cl">middleware1.wrap_model_call() → middleware2.wrap_model_call() → middleware3.wrap_model_call() → 模型
</span></span><span class="line"><span class="cl">After 钩子按相反顺序运行
</span></span><span class="line"><span class="cl">middleware3.after_model()
</span></span><span class="line"><span class="cl">middleware2.after_model()
</span></span><span class="line"><span class="cl">middleware1.after_model()
</span></span><span class="line"><span class="cl">智能体循环结束
</span></span><span class="line"><span class="cl">middleware3.after_agent()
</span></span><span class="line"><span class="cl">middleware2.after_agent()
</span></span><span class="line"><span class="cl">middleware1.after_agent()
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="agent-jumps">Agent jumps</h1>
<p>为了提前结束middleware,发挥一个 dictionary with <code>jump_to</code></p>
<p>可选的 targets:</p>
<ul>
<li><code>'end'</code>:跳转到agent执行的end(或者first after_agent hook)</li>
<li><code>'tools'</code>:跳转到tool节点</li>
<li><code>'model'</code>跳转到model节点(或者first before_model hook)</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from langchain.agents.middleware import after_model, hook_config, AgentState
</span></span><span class="line"><span class="cl">from langchain.messages import AIMessage
</span></span><span class="line"><span class="cl">from langgraph.runtime import Runtime
</span></span><span class="line"><span class="cl">from typing import Any
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@after_model
</span></span><span class="line"><span class="cl">@hook_config(can_jump_to=[&#34;end&#34;])
</span></span><span class="line"><span class="cl">def check_for_blocked(state: AgentState, runtime: Runtime) -&gt; dict[str, Any] | None:
</span></span><span class="line"><span class="cl">    last_message = state[&#34;messages&#34;][-1]
</span></span><span class="line"><span class="cl">    if &#34;BLOCKED&#34; in last_message.content:
</span></span><span class="line"><span class="cl">        return {
</span></span><span class="line"><span class="cl">            &#34;messages&#34;: [AIMessage(&#34;I cannot respond to that request.&#34;)],
</span></span><span class="line"><span class="cl">            &#34;jump_to&#34;: &#34;end&#34;
</span></span><span class="line"><span class="cl">        }
</span></span><span class="line"><span class="cl">    return None
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="configure-tracing">Configure tracing</h1>
<p>自己看</p>
<h1 id="best-practices">Best practices</h1>
<ol>
<li>单一职责</li>
<li>优雅地处理错误</li>
<li>使用正确地hook类型</li>
<li>清晰地记录每一个自定义的state属性</li>
<li>在集成之前对于每一个中间件进行单元测试</li>
<li>考虑执行的顺序:将关键的middleware放在第一位(前面)</li>
<li>使用内置的中间件</li>
</ol>
<h1 id="examples">Examples</h1>
<h2 id="dynamic-prompt">Dynamic prompt</h2>
<p>在运行时动态的调整提示词,以注入上下文,用户特定的指令和其他的信息
这是最常用的middleware之一
使用<code>ModelRequest</code>中的<code>system_message</code>字段来阅读和修改系统提示词.他包含了一个<code>SystemMessage</code>对象</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from collections.abc import Callable
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class ContextMiddleware(AgentMiddleware):
</span></span><span class="line"><span class="cl">    def wrap_model_call(
</span></span><span class="line"><span class="cl">        self,
</span></span><span class="line"><span class="cl">        request: ModelRequest,
</span></span><span class="line"><span class="cl">        handler: Callable[[ModelRequest], ModelResponse],
</span></span><span class="line"><span class="cl">    ) -&gt; ModelResponse:
</span></span><span class="line"><span class="cl">        new_content = list(request.system_message.content_blocks) + [
</span></span><span class="line"><span class="cl">            {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;Additional context.&#34;}
</span></span><span class="line"><span class="cl">        ]
</span></span><span class="line"><span class="cl">        new_system_message = SystemMessage(content=new_content)
</span></span><span class="line"><span class="cl">        return handler(request.override(system_message=new_system_message))
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ModelRequest.system_message 始终是 SystemMessage 对象，即使智能体是使用 system_prompt=&#34;string&#34; 创建的
</span></span><span class="line"><span class="cl">使用 SystemMessage.content_blocks 将内容作为块列表进行访问，无论原始内容是字符串还是列表
</span></span><span class="line"><span class="cl">修改系统消息时，请使用 content_blocks 并追加新块，以保留现有结构
</span></span><span class="line"><span class="cl">对于缓存控制等高级用例，您可以将 SystemMessage 对象直接传递给 create_agent 的 system_prompt 参数
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="dynamic-model-selection">Dynamic model selection</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">from collections.abc import Callable
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
</span></span><span class="line"><span class="cl">from langchain.chat_models import init_chat_model
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">complex_model = init_chat_model(&#34;claude-sonnet-4-6&#34;)
</span></span><span class="line"><span class="cl">simple_model = init_chat_model(&#34;claude-haiku-4-5-20251001&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class DynamicModelMiddleware(AgentMiddleware):
</span></span><span class="line"><span class="cl">    def wrap_model_call(
</span></span><span class="line"><span class="cl">        self,
</span></span><span class="line"><span class="cl">        request: ModelRequest,
</span></span><span class="line"><span class="cl">        handler: Callable[[ModelRequest], ModelResponse],
</span></span><span class="line"><span class="cl">    ) -&gt; ModelResponse:
</span></span><span class="line"><span class="cl">        if len(request.messages) &gt; 10:
</span></span><span class="line"><span class="cl">            model = complex_model
</span></span><span class="line"><span class="cl">        else:
</span></span><span class="line"><span class="cl">            model = simple_model
</span></span><span class="line"><span class="cl">        return handler(request.override(model=model))
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="dynamically-selecting-tools">Dynamically selecting tools</h2>
<p>动态选择工具
适用于:需要减短prompts;更好的准确性;访问控制</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span> <span class="n">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span><span class="o">.</span><span class="n">middleware</span> <span class="n">import</span> <span class="n">AgentMiddleware</span><span class="p">,</span> <span class="n">ModelRequest</span><span class="p">,</span> <span class="n">ModelResponse</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">typing</span> <span class="n">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">ToolSelectorMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrap_model_call</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">handler</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">ModelRequest</span><span class="p">],</span> <span class="n">ModelResponse</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ModelResponse</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Middleware to select relevant tools based on state/context.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Select a small, relevant subset of tools based on state/context</span>
</span></span><span class="line"><span class="cl">        <span class="n">relevant_tools</span> <span class="o">=</span> <span class="n">select_relevant_tools</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">state</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">runtime</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">handler</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">override</span><span class="p">(</span><span class="n">tools</span><span class="o">=</span><span class="n">relevant_tools</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="n">all_tools</span><span class="p">,</span>  <span class="c1"># All available tools need to be registered upfront</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">ToolSelectorMiddleware</span><span class="p">()],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="tool-calling-monitoring">Tool calling monitoring</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-py" data-lang="py"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Callable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.middleware</span> <span class="kn">import</span> <span class="n">AgentMiddleware</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.messages</span> <span class="kn">import</span> <span class="n">ToolMessage</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.tools.tool_node</span> <span class="kn">import</span> <span class="n">ToolCallRequest</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.types</span> <span class="kn">import</span> <span class="n">Command</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ToolMonitoringMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrap_tool_call</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">request</span><span class="p">:</span> <span class="n">ToolCallRequest</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">handler</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">ToolCallRequest</span><span class="p">],</span> <span class="n">ToolMessage</span> <span class="o">|</span> <span class="n">Command</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ToolMessage</span> <span class="o">|</span> <span class="n">Command</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Executing tool: </span><span class="si">{</span><span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Arguments: </span><span class="si">{</span><span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s1">&#39;args&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span> <span class="o">=</span> <span class="n">handler</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Tool completed successfully&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Tool failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Prompt caching(Anthropic)</p>
<ul>
<li><code>ModelRequest.system_message</code> 始终是 <a href="https://reference.langchain.org.cn/python/langchain-core/messages/system/SystemMessage"><code>SystemMessage</code></a> 对象，即使智能体是使用 <code>system_prompt=&quot;string&quot;</code> 创建的</li>
<li>使用 <code>SystemMessage.content_blocks</code> 将内容作为块列表进行访问，无论原始内容是字符串还是列表</li>
<li>修改系统消息时，请使用 <code>content_blocks</code> 并追加新块，以保留现有结构</li>
<li>对于缓存控制等高级用例，您可以将 <a href="https://reference.langchain.org.cn/python/langchain-core/messages/system/SystemMessage"><code>SystemMessage</code></a> 对象直接传递给 <code>create_agent</code> 的 <code>system_prompt</code> 参数</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Overview</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/middleware/overview/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/middleware/overview/</guid>
      <description>Overview相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>The situation that fits middleware</p>
<ul>
<li>Tracking agent behavior with logging, analytics, and debugging.</li>
<li>Transforming prompts, <a href="https://docs.langchain.com/oss/python/langchain/middleware/built-in#llm-tool-selector">tool selection</a>, and output formatting.</li>
<li>Adding <a href="https://docs.langchain.com/oss/python/langchain/middleware/built-in#tool-retry">retries</a>, <a href="https://docs.langchain.com/oss/python/langchain/middleware/built-in#model-fallback">fallbacks</a>, and early termination logic.</li>
<li>Applying <a href="https://docs.langchain.com/oss/python/langchain/middleware/built-in#model-call-limit">rate limits</a>, guardrails, and <a href="https://docs.langchain.com/oss/python/langchain/middleware/built-in#pii-detection">PII detection</a>.</li>
</ul>
<h1 id="the-agent-loop">The agent loop</h1>
<p>the core agent loop involves calling a model ,letting it choose tools to execute,and then finishing when it calls no more tools
<img alt="Core agent loop diagram" loading="lazy" src="https://mintcdn.com/langchain-5e9cc07a/Tazq8zGc0yYUYrDl/oss/images/core_agent_loop.png?fit=max&auto=format&n=Tazq8zGc0yYUYrDl&q=85&s=ac72e48317a9ced68fd1be64e89ec063"></p>
<p>Middleware在其中的作用:
exposes hooks before and after each of these steps</p>
<p><img alt="Middleware flow diagram" loading="lazy" src="https://mintcdn.com/langchain-5e9cc07a/RAP6mjwE5G00xYsA/oss/images/middleware_final.png?fit=max&auto=format&n=RAP6mjwE5G00xYsA&q=85&s=eb4404b137edec6f6f0c8ccb8323eaf1"></p>
<h1 id="use-middleware-inside-a-langgraph-workflow">Use middleware inside a LangGraph workflow</h1>
<p>将整个agent作为更大的StateGraph的子图或者节点
适用场景:
当拓扑结构不再是简单的循环直到结束时
例如:路由到多个agents之一并且对输入进行分类,并发工作,或者通过确定性步骤将agent调用串联起来</p>
<p><code>HumanInTheLoopMiddleware</code>通过 tool&rsquo;s <code>name</code>匹配
<code>@tool</code>修饰符从函数名获取器名称</p>
]]></content:encoded>
    </item>
    <item>
      <title>Prebuilt Middleware</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/middleware/prebuilt-middleware/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/middleware/prebuilt-middleware/</guid>
      <description>Prebuilt Middleware相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="tool-error">Tool error</h1>
<p>捕获工具异常,并且转化为<code>ToolMessage</code>,模型收到信息并且从中修复
<strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/tool_error/ToolErrorMiddleware"><code>ToolErrorMiddleware</code></a></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span> <span class="n">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span><span class="o">.</span><span class="n">middleware</span> <span class="n">import</span> <span class="n">ToolErrorMiddleware</span><span class="p">,</span> <span class="n">ToolRetryMiddleware</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">on_error</span><span class="p">(</span><span class="n">exc</span><span class="p">:</span> <span class="n">Exception</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">ToolCallRequest</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Surface ValueError to the model so it can correct the input</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">isinstance</span><span class="p">(</span><span class="n">exc</span><span class="p">,</span> <span class="n">ValueError</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;`{request.tool_call[&#39;name&#39;]}` failed: {type(exc).__name__}. Fix the input and retry.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Let all other exceptions propagate (halts the run)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Async-only usage</span>
</span></span><span class="line"><span class="cl"><span class="n">async</span> <span class="k">def</span> <span class="nf">aon_error</span><span class="p">(</span><span class="n">exc</span><span class="p">:</span> <span class="n">Exception</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">ToolCallRequest</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">isinstance</span><span class="p">(</span><span class="n">exc</span><span class="p">,</span> <span class="n">ConnectionError</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;Tool `{request.tool_call[&#39;name&#39;]}` encountered a connection error.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="n">search_tool</span><span class="p">,</span> <span class="n">database_tool</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Place retry inner so exceptions reach ToolErrorMiddleware after retries are exhausted</span>
</span></span><span class="line"><span class="cl">        <span class="n">ToolRetryMiddleware</span><span class="p">(</span><span class="n">max_retries</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">on_failure</span><span class="o">=</span><span class="s2">&#34;error&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">ToolErrorMiddleware</span><span class="p">(</span><span class="n">on_error</span><span class="o">=</span><span class="n">on_error</span><span class="p">,</span> <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;search_tool&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Async-only: pass aon_error alone (do not pass on_error)</span>
</span></span><span class="line"><span class="cl"><span class="n">async_agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">tools</span><span class="o">=</span><span class="p">[</span><span class="n">api_tool</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">ToolErrorMiddleware</span><span class="p">(</span><span class="n">aon_error</span><span class="o">=</span><span class="n">aon_error</span><span class="p">)],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="tool-retry">Tool retry</h1>
<p>通过可配置的指数避退机制来自动重试失败tool calls</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/tool_retry/ToolRetryMiddleware"><code>ToolRetryMiddleware</code></a></p>
<h1 id="model-retry">Model retry</h1>
<p>通过可配置的指数避退机制来自动重试失败的model calls
常见:API错误,网络问题</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/model_retry/ModelRetryMiddleware"><code>ModelRetryMiddleware</code></a></p>
<h1 id="model-fallback">Model fallback</h1>
<p>当主模型失败时,回退到备用模型
<strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/model_fallback/ModelFallbackMiddleware"><code>ModelFallbackMiddleware</code></a></p>
<h1 id="summarization">Summarization</h1>
<p>当接近token limit自动触发对话历史的压缩,保留最近几轮对话,摘要旧的上下文</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/summarization/SummarizationMiddleware"><code>SummarizationMiddleware</code></a></p>
<p>摘要是针对文本,对于多模态的信息会仅有生成的文本摘要来表示</p>
<h1 id="human-in-the-loop">Human-in-the-loop</h1>
<p>在工具运行之前,需要人工的同意,编辑或者拒绝时</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/human_in_the_loop/HumanInTheLoopMiddleware"><code>HumanInTheLoopMiddleware</code></a></p>
<h1 id="model-call-limit">Model call limit</h1>
<p>限制model call的数量来防止无限的loop和过度的费用
<strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/model_call_limit/ModelCallLimitMiddleware"><code>ModelCallLimitMiddleware</code></a></p>
<h1 id="tool-call-limit">Tool call limit</h1>
<p>限制工具调用的次数(可以全局控制,也可以对于单一的工具进行限制)
<strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/tool_call_limit/ToolCallLimitMiddleware"><code>ToolCallLimitMiddleware</code></a></p>
<h1 id="pii-detection">PII detection</h1>
<p>使用可配置的策略来检测并处理个人认证信息</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/pii/PIIMiddleware"><code>PIIMiddleware</code></a></p>
<h1 id="custom-pii-types">Custom PII types</h1>
<p>使用<code>detector</code>参数来自定义PII types</p>
<p><strong>Three ways to create custom detectors:</strong></p>
<ol>
<li><strong>Regex pattern string</strong> - Simple pattern matching</li>
<li><strong>Custom function</strong> - Complex detection logic with validation</li>
<li><strong>Custom detector function signature:</strong></li>
</ol>
<h1 id="to-do-list">To-do list</h1>
<p>使得模型有对于复杂任务的planning和track能力</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/todo/TodoListMiddleware"><code>TodoListMiddleware</code></a></p>
<h1 id="llm-tool-selector">LLM tool selector</h1>
<p>顾名思义</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/tool_selection/LLMToolSelectorMiddleware"><code>LLMToolSelectorMiddleware</code></a>
向模型提供结构化的结果输出</p>
<h1 id="provider-tool-search">Provider tool search</h1>
<p>适用于按需暴露tool的场景,通过将selected的tool放置在server-side tool search之后</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/provider_tool_search/ProviderToolSearchMiddleware"><code>ProviderToolSearchMiddleware</code></a></p>
<h1 id="shell-tool">Shell tool</h1>
<p>向agent提供持久的shell会话来执行命令,目前的shell会话暂时不支持中断(人机交互)
选择恰当的策略来匹配你的安全策略(HostExecutionPolicy<code>, </code>DockerExecutionPolicy<code>, or </code>CodexSandboxExecutionPolicy)</p>
<h1 id="filesystem-middleware">FileSystem middleware</h1>
<p>提供agent同时与长期记忆和短期记忆互动的能力</p>
<ul>
<li><code>ls</code>: List the files in the filesystem</li>
<li><code>read_file</code>: Read an entire file or a certain number of lines from a file</li>
<li><code>write_file</code>: Write a new file to the filesystem</li>
<li><code>edit_file</code>: Edit an existing file in the filesystem</li>
</ul>
<h2 id="短期与长期文件系统">短期与长期文件系统</h2>
<p>默认情况下，这些工具写入您图状态（graph state）中的本地“文件系统”。要启用跨线程的持久存储，请配置一个 <code>CompositeBackend</code>，将特定路径（如 <code>/memories/</code>）路由到 <code>StoreBackend</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain</span><span class="o">.</span><span class="n">agents</span> <span class="n">import</span> <span class="n">create_agent</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">deepagents</span><span class="o">.</span><span class="n">middleware</span> <span class="n">import</span> <span class="n">FilesystemMiddleware</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">deepagents</span><span class="o">.</span><span class="n">backends</span> <span class="n">import</span> <span class="n">CompositeBackend</span><span class="p">,</span> <span class="n">StateBackend</span><span class="p">,</span> <span class="n">StoreBackend</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langgraph</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemoryStore</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">store</span> <span class="o">=</span> <span class="n">InMemoryStore</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">agent</span> <span class="o">=</span> <span class="n">create_agent</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="s2">&#34;claude-sonnet-4-6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">store</span><span class="o">=</span><span class="n">store</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="n">FilesystemMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">backend</span><span class="o">=</span><span class="n">CompositeBackend</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">default</span><span class="o">=</span><span class="n">StateBackend</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                <span class="n">routes</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;/memories/&#34;</span><span class="p">:</span> <span class="n">StoreBackend</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl">            <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">custom_tool_descriptions</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;ls&#34;</span><span class="p">:</span> <span class="s2">&#34;Use the ls tool when...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;read_file&#34;</span><span class="p">:</span> <span class="s2">&#34;Use the read_file tool to...&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>  <span class="c1"># Optional: Custom descriptions for filesystem tools</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>当您为 <code>/memories/</code> 配置了带有 <code>StoreBackend</code> 的 <code>CompositeBackend</code> 时，任何以 <strong>/memories/</strong> 为前缀的文件都将保存到持久存储中，并在不同的线程之间保持存在。没有此前缀的文件将保留在暂存的状态存储中。</p>
<h1 id="subagent">Subagent</h1>
<p>上下文独立,并且保持主agent的上下文干净
允许你通过<code>task</code>来创建subagents</p>
<p>A subagent is defined with a <strong>name</strong>, <strong>description</strong>, <strong>system prompt</strong>, and <strong>tools</strong>. You can also provide a subagent with a custom <strong>model</strong>, or with additional <strong>middleware</strong>.</p>
<p>对于更复杂的情景,你需要提供预编译的LangGraph graph作为subagent</p>
<h1 id="rubric-grading">Rubric grading</h1>
<p>某些任务有明确的“完成”定义，智能体在第一次尝试时可能无法可靠地达到。<code>RubricMiddleware</code> 允许您将*“完成”的具体标准*声明为量规（rubric），让智能体自我评估并不断迭代，直到满足该标准或达到最大迭代次数限制。</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/deepagents/middleware/rubric/RubricMiddleware"><code>RubricMiddleware</code></a></p>
<h1 id="file-search">File search</h1>
<p>提供Glob和Grep能力来访问文件系统</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/file_search/FilesystemFileSearchMiddleware"><code>FilesystemFileSearchMiddleware</code></a></p>
<h1 id="context-editing">Context editing</h1>
<p>上下文管理</p>
<p><strong>API reference:</strong> <a href="https://reference.langchain.com/python/langchain/agents/middleware/context_editing/ContextEditingMiddleware"><code>ContextEditingMiddleware</code></a>, <a href="https://reference.langchain.com/python/langchain/agents/middleware/context_editing/ClearToolUsesEdit"><code>ClearToolUsesEdit</code></a></p>
<h1 id="llm-tool-emulator">LLM tool emulator</h1>
<p>将实际的工具相应更换为AI生成的response,通常是为了测试目的</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
